How do I add 1 to a variable each time it replaces a specific string - powershell

What I am trying to do is add 1 to $b each time it replaces the word hello with modal$b so it should look like modal1, modal2 etc.
$a=1
$b=1
$original_file = 'C:\Users\me\Desktop\2.txt'
$destination_file = 'C:\Users\me\Desktop\4.txt'
do {
(Get-Content $original_file) | ForEach-Object {
$_ -replace "hello", "modal$b"`
} | add-Content $destination_file
$a++
}
until ($a -gt 1)
Any help would be greatly appreciated!
thanks,
Sarash

You could do it with just a ForEach-Object loop, there doesn't seem to be a need for the Do loop. Following what you already have, you can increase your variable like below.
$original_file = 'C:\Users\me\Desktop\2.txt'
$destination_file = 'C:\Users\me\Desktop\4.txt'
$b = 0
Get-Content $original_file | ForEach-Object {
$_ -replace 'hello', "modal$(($b++))"
} | Out-File $destination_file
The following would work too, using a Regex.Replace with a Script Block, this example would require to use the -Raw switch on Get-Content. It's important to note that using this method, the pattern is case sensitive (i.e.: 'Hello' will not match 'hello'), if you want it to be case-insensitive you could use the (?i) flag: '(?i)hello'.
[ref]$ref = 0
[regex]::Replace((Get-Content $original_file -Raw), 'hello', {
"modal$(($ref.Value++))"
}) | Out-File $destination_file
Replacement with a script block was implemented in PowerShell 6 (Core), thanks Mathias R. Jessen for the hard work :)
Above example, if you have PS Core, would be replaced by:
[ref]$ref = 0
(Get-Content $original_file -Raw) -replace 'hello', {
"modal$(($ref.Value++))"
} | Out-File $destination_file
And there wouldn't be a need for the (?i) flag since -replace is already case-insensitive.

Related

Cycling through multiple variables in for loop

I just started working with Powershell and this is my first script.
I am checking for 3 strings in last 50 lines of a log file. I need to find all three strings and print error message if any one of those is missing. I have written following script but it does not give me the expected results.
(Get-Content C:\foo\bar.log )[-1..-50] | Out-File C:\boom\shiva\log.txt
$PO1 = Get-Content C:\boom\shiva\log.txt | where {$_ -match "<Ping:AD_P01_RCV> ok"}
$PO2 = Get-Content C:\boom\shiva\log.txt | where {$_ -match "<Ping:AD_P02_SND> ok"}
$PO3 = Get-Content C:\boom\shiva\log.txt | where {$_ -match "<Ping:AD_P03_RCV> ok"}
I am satisfied with above piece of code. The problem is with the below. I dont want to use if-else thrice. I am struggling to draft a for loop which can save space and still give me the same result.
if (!$PO1)
{
"PO1 is critical"
}
else
{
"PO1 is OK"
}
if (!$PO2)
{
"PO2 is critical"
}
else
{
"PO2 is OK"
}
if (!$PO3)
{
"PO3 is critical"
}
else
{
"PO3 is OK"
}
Can someone gave me small example of how i can fit these 3 if-else in one for loop.
If you only want to find out that all 3 strings are present this script will also show which one is missing.
(binary encoded in the variable $Cnt)
## Q:\Test\2018\07\13\SO_51323760.ps1
##
$Last50 = Get-Content 'C:\foo\bar.log' | Select-Object -Last 50
$Cnt = 0
if ($Last50 -match "<Ping:AD_P01_RCV> ok"){$Cnt++}
if ($Last50 -match "<Ping:AD_P02_SND> ok"){$Cnt+=2}
if ($Last50 -match "<Ping:AD_P03_RCV> ok"){$Cnt+=4}
if ($cnt -eq 7){
"did find all 3 strings "
} else {
"didn't find all 3 strings ({0})" -f $cnt
}
Variant immediately complaining missing P0(1..3)
$Last50 = Get-Content 'C:\foo\bar.log' | Select-Object -Last 50
if (!($Last50 -match "<Ping:AD_P01_RCV> ok")) {"PO1 is critical"}
if (!($Last50 -match "<Ping:AD_P02_SND> ok")) {"PO2 is critical"}
if (!($Last50 -match "<Ping:AD_P03_RCV> ok")) {"PO3 is critical"}
Sorry I'm a bit slow this monday.
To check in a loop different variables by building the variable name:
1..3| ForEach-Object {
If (!(Get-Variable -name "P0$_").Value){"`$P0$_ is critical"}
}
What you're trying to do is better addressed with a hashtable than with individually named variables.
$data = Get-Content 'C:\boom\shiva\log.txt'
$ht = #{}
1..3 | ForEach-Object {
$key = 'P{0:d2}' -f $_
$str = if ($_ -eq 2) {"${key}_SND"} else {"${key}_RCV"}
$ht[$key] = $data -match "<ing:AD_${str}> ok"
}
$ht.Keys | ForEach-Object {
if ($ht[$_]) {
"${key} found in log."
} else {
"${key} not found in log."
}
}
You can check if all lines were present at least once with something like this:
if (($ht.Values | Where-Object { $_ }).Count -lt 3) {
'Line missing from log.'
}
PSv3 introduced the -Tail (-Last) parameter to Get-Content, which is the most efficient way to extract a fixe number of lines from the end of a file.
You can pipe its output to Select-String, which accepts an array of regex patterns, any of which produces a match (implicit OR logic).
$matchingLines = Get-Content -Tail 50 C:\foo\bar.log |
Select-String '<Ping:AD_P01_RCV> ok', '<Ping:AD_P02_SND> ok', '<Ping:AD_P03_RCV> ok'
if ($matchingLines) { # at least 1 of the regexes matched
$matchingLines.Line # output the matching lines
} else { # nothing matched
Write-Warning "Nothing matched."
}
I finally got below draft that resolved my query to cycle variables through a for loop. I finally had to convert those individual variables to a array. But htis gives me expected result. Basically i need this script to provide input to my Nagios plugin which needs minor modification but its done.
(Get-Content C:\foo\bar.log )[-1..-50] | Out-File C:\boom\shiva\log.txt
$j = 1
$PO = new-object object[] 3
$PO[0] = Get-Content C:\boom\shiva\log.txt | where {$_ -match "<Ping:AD_P01_RCV> ok"}
$PO[1] = Get-Content C:\boom\shiva\log.txt | where {$_ -match "<Ping:AD_P02_SND> ok"}
$PO[2] = Get-Content C:\boom\shiva\log.txt | where {$_ -match "<Ping:AD_P03_RCV> ok"}
foreach( $i in $PO){
if (!$i){
"PO "+$j+" is CRITICAL"}
else{
"PO "+$j+" is OK"}
$j+=1
}
Thank you LotPings, Ansgar and mklement0 for your support and responses. I picked up a few things from your answers.

powershell replace command if line starts with a specific character

I have a text file that I would like to read and do some replacements using powershell only if the line starts with a specific character.
SAy i want to change all the dash (-) to an 'x' if and only if the line starts with a y.
I tried using the command
(Get-Content trial.log2) | Foreach-Object {$_ -replace "-", 'x'} | Set-Content trial.log2
However, it actually replaces all occurrences of the dash, not only for the line the starts with a y.
Can this be also done if I want to have multiple find replace and string manipulation using one get content command?
I have another string manipulation but only if it starts with an F
If line starts with an F, then get first 4 characters of the line, then append 'NEW' then get the next characters from character 20 to 30.
if line starts with a y, then do a replace of - with an X.
$F=(get-content $file) -like 'F*'
(Get-Content $file) | Foreach-Object {
$_ -replace "^F.+", -join("$F".Substring(0,4), "$NEW3",
} | Set-Content trial.log2
Get-Content trial.log2 | ForEach-Object {
if ( $_ -match '^y' ) {
$_ -replace '-', 'X'
}
else {
$_
}
} | Set-Content trial.log3
However, if i do this, texts are being written twice. I think there is something wrong with how I look for the line that starts with the F
Any help is appreciated. Thanks!
You can use a look-behind ((?<=pattern)) to assert that the preceding characters include a y following the start of the string:
(Get-Content trial.log2) | Foreach-Object {$_ -replace '(?<=^y.*)-','x'} | Set-Content trial.log2
How about something like:
Get-Content trial.log2 | ForEach-Object {
if ( $_ -match '^y' ) {
$_ -replace '-', 'x'
}
else {
$_
}
} | Out-File trial.log2.temp

Remove empty rows from csv in powershell [duplicate]

I know that I can use:
gc c:\FileWithEmptyLines.txt | where {$_ -ne ""} > c:\FileWithNoEmptyLines.txt
to remove empty lines. But How I can remove them with '-replace' ?
I found a nice one liner here >> http://www.pixelchef.net/remove-empty-lines-file-powershell. Just tested it out with several blanks lines including newlines only as well as lines with just spaces, just tabs, and combinations.
(gc file.txt) | ? {$_.trim() -ne "" } | set-content file.txt
See the original for some notes about the code. Nice :)
This piece of code from Randy Skretka is working fine for me, but I had the problem, that I still had a newline at the end of the file.
(gc file.txt) | ? {$_.trim() -ne "" } | set-content file.txt
So I added finally this:
$content = [System.IO.File]::ReadAllText("file.txt")
$content = $content.Trim()
[System.IO.File]::WriteAllText("file.txt", $content)
You can use -match instead -eq if you also want to exclude files that only contain whitespace characters:
#(gc c:\FileWithEmptyLines.txt) -match '\S' | out-file c:\FileWithNoEmptyLines
Not specifically using -replace, but you get the same effect parsing the content using -notmatch and regex.
(get-content 'c:\FileWithEmptyLines.txt') -notmatch '^\s*$' > c:\FileWithNoEmptyLines.txt
To resolve this with RegEx, you need to use the multiline flag (?m):
((Get-Content file.txt -Raw) -replace "(?m)^\s*`r`n",'').trim() | Set-Content file.txt
If you actually want to filter blank lines from a file then you may try this:
(gc $source_file).Trim() | ? {$_.Length -gt 0}
You can't do replacing, you have to replace SOMETHING with SOMETHING, and you neither have both.
This will remove empty lines or lines with only whitespace characters (tabs/spaces).
[IO.File]::ReadAllText("FileWithEmptyLines.txt") -replace '\s+\r\n+', "`r`n" | Out-File "c:\FileWithNoEmptyLines.txt"
(Get-Content c:\FileWithEmptyLines.txt) |
Foreach { $_ -Replace "Old content", " New content" } |
Set-Content c:\FileWithEmptyLines.txt;
file
PS /home/edward/Desktop> Get-Content ./copy.txt
[Desktop Entry]
Name=calibre
Exec=~/Apps/calibre/calibre
Icon=~/Apps/calibre/resources/content-server/calibre.png
Type=Application*
Start by get the content from file and trim the white spaces if any found in each line of the text document. That becomes the object passed to the where-object to go through the array looking at each member of the array with string length greater then 0. That object is passed to replace the content of the file you started with. It would probably be better to make a new file...
Last thing to do is reads back the newly made file's content and see your awesomeness.
(Get-Content ./copy.txt).Trim() | Where-Object{$_.length -gt 0} | Set-Content ./copy.txt
Get-Content ./copy.txt
This removes trailing whitespace and blank lines from file.txt
PS C:\Users\> (gc file.txt) | Foreach {$_.TrimEnd()} | where {$_ -ne ""} | Set-Content file.txt
Get-Content returns immutable array of rows. You can covert this to mutable array and delete neccessary lines by index.Particular indexex you can get with match. After that you can write result to new file with Set-Content. With this approach you can avoid empty lines that powershell replace tool leaves when you try to replace smthing with "". Note that I dont guarantee perfect perfomance. Im not a professional powershell developer))
$fileLines = Get-Content $filePath
$neccessaryLine = Select-String -Path $filePath -Pattern 'something'
if (-Not $neccessaryLine) { exit }
$neccessaryLineIndex = $neccessaryLine.LineNumber - 1
$updatedFileContent = [System.Collections.ArrayList]::new($fileLines)
$updatedFileContent.RemoveAt($neccessaryLineIndex)
$updatedHostsFileContent.RemoveAt($domainInfoLineIndex - 1)
$updatedHostsFileContent | Set-Content $hostsFilePath
Set-Content -Path "File.txt" -Value (get-content -Path "File.txt" | Select-String -Pattern '^\s*$' -NotMatch)
This works for me, originally got the line from here and added Joel's suggested '^\s*$': Using PowerShell to remove lines from a text file if it contains a string

Loop over array

I need a piece of powershell-code to search and replace a certain string inside a text-file. In my example, I want to replace 23-06-2016' with '24-06-2016'. The script below does this job:
$original_file = 'file.old'
$destination_file = 'file.new'
(Get-Content $original_file) | Foreach-Object {
$_ -replace '23-06-2016', '24-06-2016' `
} | Out-File -encoding default $destination_file
As the search / replace string change I want to loop over an array of dates which might look like this:
$dates = #("23-06-2016","24-06-2016","27-06-2016")
I tried use the
$original_file = 'file.old'
$destination_file = 'file.new'
foreach ($date in $dates) {
(Get-Content $original_file) | Foreach-Object {
$_ -replace 'date', 'date++' `
} | Out-File -encoding default $destination_file
}
In a first step, the date '23-06-2016' should be replaced by '24-06-2016' and in a second step, the date '24-06-2016' should be replaced by '27-06-2016'.
As my script is not working I am seeking for some advice.
You are using $date as your instance variable in your foreach loop but then referencing it as 'date', which is just a string. Even if you used '$date' it would not work because single-quoted strings do not expand variables.
Further, $date is not a number, so date++ would not do anything even it were referenced as a variable $date++. Further still, $var++ returns the original value before incrementing, so you would be referencing the same date (as opposed to the prefix version ++$var).
In a foreach loop, it's not very practical to refer to other elements, in most cases.
Instead, you could use a for loop:
for ($i = 0; $i -lt $dates.Count ; $i++) {
$find = $dates[$i]
$rep = $dates[$i+1]
}
This isn't necessarily the most clear way to do it.
You might be better off with a [hashtable] that uses the date to find as a key, and the replacement date as the value. Sure, you'd be duplicating some dates as value and key, but I think I'd rather have the clarity:
$dates = #{
"23-06-2016" = "24-06-2016"
"24-06-2016" = "27-06-2016"
}
foreach ($pair in $dates.GetEnumerator()) {
(Get-Content $original_file) | Foreach-Object {
$_ -replace $pair.Key, $pair.Value
} | Out-File -encoding default $destination_file
}

How do I add a simple search and replace to this loop in Powershell?

I wrote this so far, it runs through and does exactly what I want, now I'm stuck. I can't seem to get it to open the newly created $NOSPACE.shtml and replace a word with the variable $SPACE though. A little help please?
$CITYLIST=import-csv CityList.txt
$CITYLIST | FOREACH-OBJECT { $_ }
FOREACH ($Item in $CITYLIST) {
$Item.City
$NOSPACE=$Item.City.replace(" ","_")
$SPACE=$Item.City
Add-Content _citylist.shtml "`n<a class=`"cityareaslist`" href=`"`/$NOSPACE.shtml`"> $SPACE<`/a>" ; Copy-Item index.shtml "$NOSPACE.shtml"
}
(Get-Content "$NOSPACE.shtml") |
Foreach-Object {$_ -replace 'word',$SPACE} |
Set-Content "$NOSPACE.shtml"