BallerNacken
BallerNacken

Reputation: 355

Powershell - replace some lines

I check a file for lines where a char in a special position is missing. If the char is missing, I insert it in that exact location. Now I want to write that new line with that extra char over the old line into the file. For example I have the following in the file:

"C:\\path\test\test"
"C:\\path\test\test2\"
"C:\\path\test\test3\"

After I run my short script, I want it to look like:

"C:\path\test\test\"
"C:\path\test\test2\"
"C:\path\test\test3\"

I try to accomplish that using this little loop:

$content = Get-Content C:\path\to\file.txt
foreach ($line in $content)
{
    if($line[-25] -ne '\') {
        $test = $line -replace '(?<!\\)(?=.{24}$)', '\' | Set-Content C:\path\to\file.txt
    }
    else {
        continue
    }
}

That code does insert me the char at the correct position. But in the end only line one is in the file, instead of all three lines. I think I am close, but just started with powershell.

Upvotes: 1

Views: 1321

Answers (2)

Ryan Bemrose
Ryan Bemrose

Reputation: 9266

As @briantist points out, you need to move the Set-Content outside the for loop to set the entire file at once. Inside the for loop, there is no need for character counting, though. And I find it is better to use the system APIs to work with paths rather than try to write a regex.

Get-Content $inputFileName | Foreach-Object {
  # Strip quotes
  $line = $_ -replace '"',''

  # Add trailing backslash if needed
  $line += '\'

  # Call .NET GetFullPath to normalize. This removes double backslashes
  # and resolves relative paths if needed.
  $line = [System.IO.Path]::GetFullPath($line)

  # Add quotes back, and output string
  '"' + $line + '"'
} | Set-Content $outputFileName

Or, in much condensed form

gc $inputFile | % { [io.path]::GetFullPath((($_+'\') -replace '"','')) } | sc $outputFile

Upvotes: 2

briantist
briantist

Reputation: 47792

Set-Content replaces all the content in a file with the input values. You're calling it independently on each iteration of the loop, when what you really want is to call it with all the results of the loop.

There are many ways to change it, but I would consider replacing foreach with ForEach-Object, and piping the result into Set-Content:

Get-Content C:\path\to\file.txt |
ForEach-Object -Process {
    $line = $_
    if($line[-25] -ne '\') {
        $line -replace '(?<!\\)(?=.{24}$)', '\'
    } else {
        $line
    }
} | Set-Content C:\path\to\file.txt

Upvotes: 2

Related Questions