How to replace a value in text file using powershell script - powershell

My file consists of following data (no header)
DEPOSIT ADD 123456789 (VALUE)(VARIABLE) NNNN VALUEVARIABLE
DEPOSIT ADD 234567890 (VALUE)(P75) NNNN VALUEVARIABLE
DEPOSIT ADD 345678901 (VALUE)(VARIABLE) NNNN VALUEVARIABLE
This is a tab delimited text file.
There are total of 5 columns. (123456789 (VALUE)(VARIABLE) is a single value column)
My requirements are:
I need to fetch only the row which contains P75 to update in the same file.
I have to replace the values in Col3,Col4 and in Col5 after fetching P75 other rows should be unaffected.
from
DEPOSIT ADD 234567890 (VALUE)(P75) NNNN VALUEVARIABLE
to
DEPOSIT ADD 234567890 (VTG)(SPVTG) TCM VTGSPVTG
Only the records which contains P75 should be updated like this. The replace values are same for all selected records.
My script which I have written is
$original_file='C:\Path\20200721130155_copy.txt' -header Col1,Col2,Col3,Col4,Col5,| Select Col3,Col4,Col5
(Get-Content $original_file) |ForEach-Object {
if($_.Col3 -match '(VALUE)(P75)')
{
$_ -replace '(VALUE)(P75)', '(VTG)(SPVTG)' `
-replace 'VALUEVARIABLE', 'VTGSPVTG' `
-replace 'NNNN', 'TCM' `
}
$_
}| Set-Content $original_file+'_new.txt' -Force
I am getting output file with same content. The file is not getting updated.
Please advice.
Thanks

You can do the following:
$newfile = "{0}_new.txt" -f $original_file
Get-Content $original_file | Foreach-Object {
if ($_ -match '\(VALUE\)\(P75\)') {
$_ -replace '\(VALUE\)\(P75\)','(VTG)(SPVTG)' -replace 'VALUEVARIABLE', 'VTGSPVTG' -replace 'NNNN', 'TCM'
} else {
$_
}
} | Set-Content $newfile -Force
Since -replace uses regex, you must backslash escape special regex characters like ( and ).
Since $_ is the current line read from Get-Content without -Raw, you will need to output $_ if you want to make no changes. If you do want to replace text, then $_ -replace 'regex','text' will output that line with the replaced text.
Alternatively, you can apply the same logic above in a switch statement, which is more efficient:
$newfile = "{0}_new.txt" -f $original_file
$(switch -regex -file $original_file {
'\(VALUE\)\(P75\)' {
$_ -replace '\(VALUE\)\(P75\)','(VTG)(SPVTG)' -replace 'VALUEVARIABLE', 'VTGSPVTG' -replace 'NNNN', 'TCM'
}
default { $_ }
}) | Set-Content $newfile -Force

You can use Import-Csv -Delimiter "`t" to import the data of the original file.
Then loop over the items and change the values if Col3 matches the search text.
$original_file = 'C:\Path\20200721130155_copy.txt'
$out_file = $original_file -replace '\.txt$', '_new.txt'
(Import-Csv -Path $original_file -Delimiter "`t" -Header 'Col1','Col2','Col3','Col4','Col5') | ForEach-Object {
if( $_.Col3 -like '*(*)*(P75)') {
$_.Col3 = $_.Col3 -replace '\([^)]+\)\(P75\)$', '(VTG)(SPVTG)'
$_.Col4 = 'TCM'
$_.Col5 = 'VTGSPVTG'
}
# rejoin the fields and output the line
$_.PsObject.Properties.Value -join "`t"
} | Set-Content -Path $out_file -Force
Output will be
DEPOSIT ADD 123456789 (VALUE)(VARIABLE) NNNN VALUEVARIABLE
DEPOSIT ADD 234567890 (VTG)(SPVTG) TCM VTGSPVTG
DEPOSIT ADD 345678901 (VALUE)(VARIABLE) NNNN VALUEVARIABLE
Regex details for -replace:
\( Match the character “(” literally
[^)] Match any character that is NOT a “)”
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
\) Match the character “)” literally
\( Match the character “(” literally
P75 Match the characters “P75” literally
\) Match the character “)” literally
$ Assert position at the end of the string (or before the line break at the end of the string, if any)

Related

How to search and replace combined with if & else in powershell

Every night I got a text file that needs to be edited manually.
The file contains approximately 250 rows. Three example of a rows:
112;20-21;32;20-21;24;0;2;248;271;3;3;;
69;1;4;173390;5;0;0;5460;5464;3;3;;
24;7;4;173390;227;0;0;0;0;3;3;;
I need to replace the two last values in each row.
All rows ending with ;0;3;3;; should be replaced with ;0;17;18;; (the last one, solved)
The logic for the other two:
If the row contain a '-' it should replace the two last values from ;3;3;; to ;21;21;;
If it don´t have a '-' it should replace the two last values from ;3;3;; to ;22;22;;
This is my script
foreach ($file in Get-ChildItem *.*)
{
(Get-Content $file) -replace ';0;3;3;;',';;0;17;18;;' -replace ';3;3;;',';21;21;;' |Out-file -encoding ASCII $file-new}
If I could add a '-' in the end of each row continga a '-' I could solve the issue with a modified script:
(Get-Content $file) -replace ';0;3;3;;',';;0;17;18;;' -replace ';3;3;;-',';22;22;;' -replace ';3;3;;',';21;21;;'|Out-file -encoding ASCII $file-new}`
But how do I add a '-' in the end of a row, if the row contain a '-'?
Best Regards
Mr DXF
I tried with select-string, but I can´t figure it out...
if select-string -pattern '-' {append-text '-'|out-file -encoding ascii $file-new
else end
}
The following might do the trick, it uses a switch with the -Regex flag to read your files and match lines with regular expressions.
foreach ($file in Get-ChildItem *.* -File) {
& {
switch -Regex -File $file.FullName {
# if the line ends with `;3;3;;` but is not preceded by `;0`
'(?<!;0);3;3;;$' {
# if it contains a `-`
if($_.Contains('-')) {
$_ -replace ';3;3;;$', ';21;21;;'
continue
}
# if it doesn't contain a `-`
$_ -replace ';3;3;;$', ';22;22;;'
continue
}
# if the line ends with `';0;3;3;;`
';0;3;3;;$' {
$_ -replace ';0;3;3;;$', ';0;17;18;;'
continue
}
# if none of the above conditions are matched,
# output as is
Default { $_ }
}
} | Set-Content "$($file.BaseName)-new$($file.Extension)" -Encoding ascii
}
Using the content example in question the end result would become:
112;20-21;32;20-21;24;0;2;248;271;21;21;;
69;1;4;173390;5;0;0;5460;5464;22;22;;
24;7;4;173390;227;0;0;0;0;17;18;;

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

Sort and export-CSV

I have a csv file containing rows of the following extract:
"EmployeeID","FirstName","LastName","Location","Department","TelephoneNo","Email"
"000001 ","abc ","def ","Loc1"," "," ","name1#company.com "
"000023 ","ghi ","jkl ","Loc2"," "," ","name2#company.com "
"000089 ","mno ","pqr ","Loc2"," "," ","name3#company.com "
How do I keep the quotes and sort and save as a csv file?
I have the following powershell source script which works with csv files not having double quotes for the columns:
Get-Content $Source -ReadCount 1000 |
ConvertFrom-Csv -Delimiter $Delimiter |
Sort-Object -Property $NamesOfColumns -Unique |
ForEach-Object {
# Each of the values in $ColumnValueFormat must be executed to get the property from the loop variable ($_).
$values = foreach ($value in $ColumnValueFormat) {
Invoke-Expression $value
}
# Then the values can be passed in as an argument for the format operator.
$ShowColsByNumber -f $values
} |
Add-Content $Destination;
The $Source, $Delimiter, $NamesOfColumns and $ColumnValueFormat are given or built dynamically.
$ColumnValueFormat with a non quoted csv file contains:
$_.EmployeeID.Trim()
$_.FirstName.Trim()
$_.LastName.Trim()
$_.Location.Trim()
$_.Department.Trim()
$_.TelephoneNo.Trim()
$_.Email.Trim()
$ColumnValueFormat with a quoted csv file contains:
$_."EmployeeID".Trim()
$_."FirstName".Trim()
$_."LastName".Trim()
$_."Location".Trim()
$_."Department".Trim()
$_."TelephoneNo".Trim()
$_."Email".Trim()
The problem seems to be based around the $ColumnValueFormat that is placing the column headers with the double quotes. (If I remove them I am not sure the internals of the cmdlet will recognize the column headings when it is processing the rows)
I am having two problems:
The column heading surrounded by the double quotes. The problem seems to be based around the $ColumnValueFormat that is placing the column headers with the double quotes as it does not process the rows. (If I remove the double quotes then it does not recognize the column headings when it is processing the rows).
Another problem I came across last minute is if the last column is blank it thinks it's a null and when the Invoke-Expression $value executes (where $value holds the last column expression of $_.Email.Trim() - on a non quoted CSV file) it bombs. If I try to place the statement in a try/catch block it simply ignore it the last column is not added to the $values array and again bombs.
Quotes around property names are used syntactically to access names with spaces, not to write quotes to the output.
Export-Csv cmdlet doesn't have an option to force quotes so we'll have to export the CSV manually. And we'll have to process empty values that are $Null after ConvertFrom-Csv with an empty string. In case only some fields are needed we'll use Select cmdlet with -index parameter.
Get-Content $Source |
ConvertFrom-Csv |
%{ $header = $false } {
if (!$header) {
$header = $true
'"' + (
($csv[0].PSObject.Properties.Name.trim() |
select -index 1,6
) -join '","'
) + '"'
}
'"' + (
($_.PSObject.Properties.Value |
%{ if ($_) { $_.trim() } else { '' } } |
select -index 1,6
) -join '","'
) + '"'
} | Out-File $Destination
The above code is great for pass-through processing of large CSV files because it doesn't keep the entire file in memory. Otherwise it's possible to simplify the code a bit:
$csv = Get-Content $Source | ConvertFrom-Csv
$csv | %{
'"' + (
($csv[0].PSObject.Properties.Name.trim() |
select -index 1,6
) -join '","'
) + '"'
} {
'"' + (
($_.PSObject.Properties.Value |
%{ if ($_) { $_.trim() } else { '' } } |
select -index 1,6
) -join '","'
) + '"'
) | Out-File $Destination

Powershell - reading ahead and While

I have a text file in the following format:
.....
ENTRY,PartNumber1,,,
FIELD,IntCode,123456
...
FIELD,MFRPartNumber,ABC123,,,
...
FIELD,XPARTNUMBER,ABC123
...
FIELD,InternalPartNumber,3214567
...
ENTRY,PartNumber2,,,
...
...
the ... indicates there is other data between these fields. The ONLY thing I can be certain of is that the field starting with ENTRY is a new set of records. The rows starting with FIELD can be in any order, and not all of them may be present in each group of data.
I need to read in a chunk of data
Search for any field matching the
string ABC123
If ABC123 found, search for the existence of the
InternalPartNumber field & return that row of data.
I have not seen a way to use Get-Content that can read in a variable number of rows as a set & be able to search it.
Here is the code I currently have, which will read a file, searching for a string & replacing it with another. I hope this can be modified to be used in this case.
$ftype = "*.txt"
$fnames = gci -Path $filefolder1 -Filter $ftype -Recurse|% {$_.FullName}
$mfgPartlist = Import-Csv -Path "C:\test\mfrPartList.csv"
foreach ($file in $fnames) {
$contents = Get-Content -Path $file
foreach ($partnbr in $mfgPartlist) {
$oldString = $mfgPartlist.OldValue
$newString = $mfgPartlist.NewValue
if (Select-String -Path $file -SimpleMatch $oldString -Debug -Quiet) {
$stringData = $contents -imatch $oldString
$stringData = $stringData -replace "[\n\r]","|"
foreach ($dataline in $stringData) {
$file +"|"+$stringData+"|"+$oldString+"|"+$newString|Out-File "C:\test\Datachanges.txt" -Width 2000 -Append
}
$contents = $contents -replace $oldString $newString
Set-Content -Path $file -Value $contents
}
}
}
Is there a way to read & search a text file in "chunks" using Powershell? Or to do a Read-ahead & determine what to search?
Assuming your fine isn't too big to read into memory all at once:
$Text = Get-Content testfile.txt -Raw
($Text -split '(?ms)^(?=ENTRY)') |
foreach {
if ($_ -match '(?ms)^FIELD\S+ABC123')
{$_ -replace '(?ms).+(^Field\S+InternalPartNumber.+?$).+','$1'}
}
FIELD,InternalPartNumber,3214567
That reads the entire file in as a single multiline string, and then splits it at the beginning of any line that starts with 'ENTRY'. Then it tests each segment for a FIELD line that contains 'ABC123', and if it does, removes everything except the FIELD line for the InternalPartNumber.
This is not my best work as I have just got back from vacation. You could use a while loop reading the text and set an entry flag to gobble up the text in chunks. However if your files are not too big then you could just read up the text file at once and use regex to split up the chunks and then process accordingly.
$pattern = "ABC123"
$matchedRowToReturn = "InternalPartNumber"
$fileData = Get-Content "d:\temp\test.txt" | Where-Object{$_ -match '^(entry|field)'} | Out-String
$parts = $fileData | Select-String '(?smi)(^Entry).*?(?=^Entry|\Z)' -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value
$parts | Where-Object{$_ -match $pattern} | Select-String "$matchedRowToReturn.*$" | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value
What this will do is read in the text file, drop any lines that are not entry or field related, as one long string and split it up into chunks that start with lines that begin with the work "Entry".
Then we drop those "parts" that do not contain the $pattern. Of the remaining that match extract the InternalPartNumber line and present.

How to remove some words from all text file in a folder by powershell?

I have a situation that I need to remove some words from all text file in a folder.
I know how to do that only in 1 file, but I need to do it automatically for all text files in that folder. I got no idea at all how to do it in powershell.
The name of the files are random.
Please help.
This is the code
$txt = get-content c:\work\test\01.i
$txt[0] = $txt[0] -replace '-'
$txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] -replace '-'
$txt | set-content c:\work\test\01.i
Basicly it jsut removes a "-" from first line and last line, but i need to do this on all files in the folder.
Get-ChildItem c:\yourfolder -Filter *.txt | Foreach-Object{
... your code goes here ...
... you can access the current file name via $_.FullName ...
}
Here is a full working example:
Get-ChildItem c:\yourdirectory -Filter *.txt | Foreach-Object{
(Get-Content $_.FullName) |
Foreach-Object {$_ -replace "what you want to replace", "what to replace it with"} |
Set-Content $_.FullName
}
Now for a quick explanation:
Get-ChildItem with a Filter: gets all items ending in .txt
1st ForEach-Object: will perform the commands within the curly brackets
Get-Content $_.FullName: grabs the name of the .txt file
2nd ForEach-Object: will perform the replacement of text within the file
Set-Content $_.FullName: replaces the original file with the new file containing the changes
Important Note: -replace is working with a regular expression so if your string of text has any special characters
something like this ?
ls c:\temp\*.txt | %{ $newcontent=(gc $_) -replace "test","toto" |sc $_ }
$files = get-item c:\temp\*.txt
foreach ($file in $files){(Get-Content $file) | ForEach-Object {$_ -replace 'ur word','new word'} | Out-File $file}
I hope this helps.
Use Get-Childitem to filter for the files you want to modify. Per response to previous question "Powershell, like Windows, uses the extension of the file to determine the filetype."
Also:
You will replace ALL "-" with "" on the first and last lines, using what your example shows, IF you use this instead:
$txt[0] = $txt[0] -replace '-', ''
$txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] -replace '-', ''