I read a xml file and want to replace three strings.
My Code:
foreach ($file in Get-ChildItem $files){
(Get-Content $file) | Foreach-Object {
$_ -replace 'TABLE_NAME="\$ZoneProdukt\$', ('TABLE_NAME="' + $ZONPRODLANG)`
-replace 'OPTION="Copy" ', '' `
-replace ('<JOB ','<JOB TIMETO="TEST" ') | ? {$_ -notlike "*TIMETO=`""}
} | Set-Content ($destination_folder + $file.name)
}
the last replace provides only the half of the result I
expect.
If there are lines containing "JOB" and "TIMETO" they will not be displayed (because of Where-Object)
How to keep lines if the mentioned "TIMETO"-Attribute already exists?
examples:
source line in file (without "TIMETO"):
<JOB JOBISN="30" USER="testuser">
correct replace:
<JOB TIMETO="TEST" JOB JOBISN="30" USER="testuser">
....
....
source line in file (with "TIMETO"):
<JOB JOBISN="30" USER="testuser" TIMETO="0400">
replace -> this line will not be displayed !!
..
thanks in advance! br danijel
You could use an if-statement in your ForEach-Object:
foreach ($file in Get-ChildItem $files){
(Get-Content $file) | Foreach-Object {
if($_ -like "*TIMETO=`""){
$_ -replace 'TABLE_NAME="\$ZoneProdukt\$', ('TABLE_NAME="' + $ZONPRODLANG)`
-replace 'OPTION="Copy" ', '' `
}else{
$_ -replace 'TABLE_NAME="\$ZoneProdukt\$', ('TABLE_NAME="' + $ZONPRODLANG)`
-replace 'OPTION="Copy" ', '' `
-replace ('<JOB ','<JOB TIMETO="TEST" ')
}
} | Set-Content ($destination_folder + $file.name)
}
Manipulating xml using regex is generally bad practice. You should use Get-Content and cast as [xml], which will allow you to manipulate the object. Check out this this MSDN demo.
Related
I need to extract columns from one file and join them in another file.
I used this code to select the columns that I need:
$original_path = 'C:\Users\leticia.araujo\Downloads\Arquivo Buffer\Arquivo teste'
$files = Get-ChildItem $original_path
ForEach($file in $files) {
$pathFile = $original_path + '\' + $file.Name
$SegundaColuna = Get-Content -Path $pathFile | Foreach {"$(($_ -split ',')[3..3])"}
$TerceiraColuna = Get-Content -Path $pathFile | Foreach {"$(($_ -split ':')[3..3])"}
$QuartaColuna = Get-Content -Path $pathFile | Foreach {"$(($_ -split ',')[10..10])"}
}
When I try to put these in a txt using
'Add-Content $pathFile $SegundaColuna,$TerceiraColuna,$QuartaColuna'
I got, but in the file the columns are not next to each other. they are under each other.
Example:
I need they are like this:
1 a
2 b
3 c
But they are like this:
1
2
3
a
b
c
Focusing on a single file inside your foreach loop:
Since the values to join come from the same lines of a given file, read that file line by line:
Get-Content -Path $pathFile | # Read the file line by line.
ForEach-Object { # Process each line.
($_ -split ',')[3],
($_ -split ':')[3],
($_ -split ',')[10] -join ' ' # Output the column values joined with a space.
} |
Set-Content out.txt
If you need to merge columns across all your input files and create a single output file, replace the foreach loop with a single pipeline:
Get-ChildItem $original_path |
Get-Content |
ForEach-Object {
($_ -split ',')[3],
($_ -split ':')[3],
($_ -split ',')[10] -join ' '
} |
Set-Content out.txt
I have file1.html with this lines:
<bogus></bogus>
<title>Something goes here</title>
<TheEnd>END</TheEnd>
I made 3 different PowerShell scripts with regex in order to change this line: <title>Something goes here</title>:
$path = 'c:\Folder1\file1.html'
$Content = Get-Content -Path $path
foreach($Line in $Content){
$Line -replace "<title>(.*?)</title>",'$1 NEW is now there!' #This regex selects everything between tags and make a replace:
}
Set-Content -Path $Path -Value $Line
AND
$Content = Get-Content -Path c:\Folder1\file1.html
foreach($Line in $Content){
$Line -replace "<title>(.*?)</title>",'$1 NEW is now there!' #This regex selects everything between tags and make a replace:
}
Set-Content -Path $Path -Value $Line
OR
$path = 'c:\Folder1\file1.html'
$Content = Get-Content -Path $path
$GetTitle = [regex]"<title>(.*?)</title>"
foreach($Line in $Content){
$Line -replace $GetTitle,'$1 NEW is now there!' #This regex selects everything between tags and make a replace:
}
Set-Content -Path $Path -Value $Line
The output should be.
<bogus></bogus>
<title>NEW is now there!</title>
<TheEnd>END</TheEnd>
Mention that all my codes are working in PowerShell, but does not make any change in File1.html. That is the problem. can anyone correct my codes?
With regex -replace, you need to think what you want to keep and capture that in backreferences.
In your case, you want to retain <title> and </title>, and replace what is in between those tags.
Change the regex to '(<title>).*?(</title>)'.
Also, you can use the -Raw switch on Get-Content to read the file as a single multilined string, do the replacement and pipe the result straight through to Set-Content
$path = 'c:\Folder1\file1.html'
(Get-Content -Path $path -Raw) -replace '(<title>).*?(</title>)', '$1NEW is now there!$2' |
Set-Content -Path $Path
Details:
'$1' + Insert the text that was last matched by capturing group number 1
' NEW is now there!' + Insert the character string “ NEW is now there!” literally
'$2' Insert the text that was last matched by capturing group number 2
$path = 'c:\Folder1\file1.html'
$Content = Get-Content -Path $path
$newContent =#()
$RegexForTitle = '(?<=title>).*(?=</title>)'
foreach($Line in $Content)
{
$newContent += $Line -replace $RegexForTitle,'NEW IS NOW HERE!'
}
Set-Content -Path $Path -Value $newContent
#optional this line
'| Out-File -path file1.html'
I have this powershell code which should replace every occurrence of a string in every file in the directory with a new string.
This works, however an empty line is added in the end.
What causes this, and how can this be nicely avoided?
$files = Get-ChildItem $currentDir *.* -recurse
foreach ($file in $files)
{
$find = "placeholder"
$replace = "newvalue"
$content = Get-Content $($file.FullName) -Raw
$content -replace $find,$replace | Out-File $($file.FullName)
}
Simply removing the last line is not a good solution since sometimes my files will contain an empty line which I want to keep.
You could use the -NoNewline parameter to prevent Out-File from appending the extra line at the end of the file.
$content -replace $find,$replace | Out-File $($file.FullName) -NoNewline
Note: this was added in PowerShell 5.0
I am limited to PS version 4, and this is what I used
$files = Get-ChildItem $currentDir . -recurse
$find = "placeholder"
$replace = ""newvalue"
foreach ($file in $files)
{
$content = Get-Content $($file.FullName) -Raw | ForEach-Object { $_ -replace $find,$replace}
$content = $content -join "`r`n"
$content | Set-Content $($file.FullName)
}
Note that this only works if it is ok to store the complete file in memory.
I have a text file that looks like this:
Data I'm NOT looking for
More data that doesn't matter
Even more data that I don't
&Start/Finally the data I'm looking for
&Data/More data that I need
&Stop/I need this too
&Start/Second batch of data I need
&Data/I need this too
&Stop/Okay now I'm done
Ending that I don't need
Here is what the output needs to be:
File1.txt
&Start/Finally the data I'm looking for
&Data/More data that I need
&Stop/I need this too
File2.txt
&Start/Second batch of data I need
&Data/I need this too
&Stop/Okay now I'm done
I need to do this for every file in a folder (sometimes there will be multiple files that will need to be filtered.) The files names can be incrementing: ex. File1.txt, File2.txt, File3.txt.
This is what I have tried with no luck:
ForEach-Object{
$text -join "`n" -split '(?ms)(?=^&START)' -match '^&START' |
Out-File B:\PowerShell\$filename}
Thanks!
Looks like you were pretty close: your code correctly extracted the paragraphs of interest, but intra-paragraph out-filtering of non-&-starting lines was missing, and you needed to write to paragraph-specific output files:
$text -join "`n" -split '(?m)(?=^&Start)' -match '^&Start' |
ForEach-Object { $ndx=0 } { $_ -split '\n' -match '^&' | Out-File "File$((++$ndx)).txt" }
This creates sequentially numbered files starting with File1.txt for every paragraph of interest.
To do it for every file in a folder, with output filenames using fixed naming scheme File<n> across all input files (and thus cumulative numbering):
Get-ChildItem -File . | ForEach-Object -Begin { $ndx=0 } -Process {
(Get-Content -Raw $_) -split '(?m)(?=^&Start)' -match '^&Start' |
ForEach-Object { $_ -split '\n' -match '^&' | Out-File "File$((++$ndx)).txt" }
}
To do it for every file in a folder, with output filenames based on the input filenames and numbering per input file (PSv4+, due to use of -PipelineVariable):
Get-ChildItem -File . -PipelineVariable File | ForEach-Object {
(Get-Content -Raw $_) -split '(?m)(?=^&Start)' -match '^&Start' |
ForEach-Object {$ndx=0} { $_ -split '\n' -match '^&' | Out-File "$($File.Name)$((++$ndx)).txt" }
}
You post a second question (against the rules) and it was deleted but here is my quick answer for it. I hope it will help you and give you more sense how PS works:
$InputFile = "C:\temp\test\New folder (3)\File1.txt"
# get file content
$a=Get-Content $InputFile
# loop for every line in range 2 to last but one
for ($i=1; $i -lt ($a.count-1); $i++)
{
#geting string part between & and / , and construct output file name
$OutFile = "$(Split-Path $InputFile)\$(($a[$i] -split '/')[0] -replace '&','').txt"
$a[0]| Out-File $OutFile #creating output file and write first line in it
$a[$i]| Out-File $OutFile -Append #write info line
$a[-1]| Out-File $OutFile -Append #write last line
}
Something like this?
$i=0
gci -path "C:\temp\ExplodeDir" -file | %{ (get-content -path $_.FullName -Raw).Replace("`r`n`r`n", ";").Replace("`r`n", "~").Split(";") | %{if ($_ -like "*Start*") {$i++; ($_ -split "~") | out-file "C:\temp\ResultFile\File$i.txt" }} }
I have a text (.txt) file with following content:
Car1
Car2
Car3
Car4
Car5
For changing Car1 for random text I used this script:
Get-ChildItem "C:\Users\boris.magdic\Desktop\q" -Filter *.TXT |
Foreach-Object{
$content = Get-Content $_.FullName
$content | ForEach-Object { $_ -replace "Car1", "random_text" } | Set-Content $_.FullName
}
This is working ok, but now I want to add one text line under Car2 in my text file.
How can I do that?
Just chain another -replace and use a new line!
Get-ChildItem "C:\Users\boris.magdic\Desktop\q" -Filter *.TXT |
Foreach-Object{
$file = $_.FullName
$content = Get-Content $file
$content | ForEach-Object { $_ -replace "Car1", "random_text" -replace "(Car2)","`$1`r`nOtherText" } | Set-Content $file
}
First thing is that | Set-Content $_.FullName would not work since the file object does not exist in that pipe. So one simple this to do it save the variable for use later in the pipe. You can also use the ForEach($file in (Get-ChildItem....)) construct.
The specific change to get what you want is the second -replace. We place what you want to match in brackets to that we can reference it in the replacement string with $1. We use a backtick to ensure PowerShell does not treat it as a variable.
We can remove some redundancy as well since -replace will work against the strings of file as a whole
Get-ChildItem "c:\temp" -Filter *.TXT |
Foreach-Object{
$file = $_.FullName
(Get-Content $file) -replace "Car1", "random_text" -replace "(Car2)","`$1`r`nOtherText" | Set-Content $file
}
While this does work with your sample text I want to point out that more complicated strings might require more finesse to ensure you make the correct changed and that the replacements we are using are regex based and do not need to be for this specific example.
.Replace()
So if you were just doing simple replacements then we can update your original logic.
Foreach-Object{
$file = $_.FullName
$content = Get-Content $_.FullName
$content | ForEach-Object { $_.replace("Car1", "random_text").replace("Car2","Car2`r`nOtherText")} | Set-Content $file
}
So that is just simple text replacement chained using the string method .Replace()