I have a text file where I need to edit some rows, but the script only outputs the rows that were edited. I want the output to be the original file, just with the rows I want to edit being the only thing that changes. My file is tab delimited and looks like this
File.txt
ABC 123456 987654321 DEF 0 More Text
ABC 123457 987654320 DEF 0 More Text
XYZ 123457 987654320 DEF 0 More Text
I want the file to output this
ABC 123456 987654321 DEF 0 More Text
ABC 123457 987654320 GHI 1 More Text
XYZ 123457 987654320 DEF 0 More Text
This is what I have. $arrExcelValues is an array of values pulled from an excel file
foreach ($file in $files){
get-content -Path $file |
where {$_ | Select-String -Pattern '^ABC. '}|
where {$_ | Select-String -Pattern $arrExcelValues} | % {$_ -replace "DEF`t0","GHI`t1"} | Set-Content $out
}
Powershell Identifies the rows to edit based on matching patterns, but my output is just the edited row
ABC 123457 987654320 GHI 1 More Text
I believe that the issue is that where is filtering out everything that is not a match, but once the matching rows have been changed, I can't figure out how to get an output with all of the original file contents & my edited rows
CodePudding user response:
Both Select-String
and Where-Object
filter their input, so by definition you will only get filtered (potentially modified) output, not all input.
As an aside:
Import-Csv
/Export-Csv
can process TSV (tab-separated values) data as well, using-Delimiter "`t"
- However, you may have to supply headers via the
-Headers
parameter, and in Windows PowerShell you'll invariably get double-quoted fields in the output (you can opt-out in Powershell (Core) 7 ). - The convenience of OOP processing via these cmdlets comes at the cost of diminished performance.
Sticking with your plain-text parsing approach, I suggest using a switch
statement, which allows you to combine selective processing with passing all (potentially modified) lines through; as an added bonus, switch -File
is faster than a Get-Content
-based pipeline:
# Simulate file input as an array of lines.
$lines = @'
ABC 123456 987654321 DEF 0 More Text
ABC 123457 987654320 DEF 0 More Text
XYZ 123457 987654320 DEF 0 More Text
'@ -split '\r?\n'
# The values to match the 2nd column against.
$arrExcelValues = '123457'
# Note:
# * The following outputs to the *display* by default.
# * Replace `($lines)` with `-File $file` in your code.
# * Wrap the entire statement in `& { ... }` so you can pipe to `Set-Content`
# * Use `$(...)` instead if you want to write back to the input file.
switch -Regex ($lines) {
'^ABC\t' {
$fields = $_ -split "`t"
if ($fields[1] -in $arrExcelValues) {
$fields[3] = 'GHI'
$fields[4] = 1
$fields -join "`t"
} else {
$_ # pass through
}
}
default { $_ } # pass through
}