Issue with nested powershell - powershell

I have this code snippet where I try tro replace some strings in all files of a directory. I thought I could nest the foreach in the ForEach-Object, but this does not seem to work.
The error I get is:
InvalidArgument: (:) [ForEach-Object], ParameterBindingException
$files = Get-ChildItem $testdir\reference *.* -recurse
$replacementMap = #{"Fruit::Apple" = "NewApple";"Fruit::Banana" = "NewBanana"}
foreach ($file in $files)
{
If (Get-Content $($file.FullName) | Select-String -Pattern "Fruit::")
{
$content = Get-Content $($file.FullName) | ForEach-Object
{
$line = $_
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
}
$content = $content -join "`r`n"
$content | Set-Content $($file.FullName)
}
This code worked without the
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
part. Anyone has a clue what I'm doing wrong? Thanks in advance

You missed a curly brace closure and formatting issue on the foreach-object. You need to take care of foreach and foreach-object in a different way:
Replace your existing foreach part with this:
foreach ($file in $files)
{
If(Get-Content $($file.FullName) | Select-String -Pattern "Fruit::")
{
$content = Get-Content $($file.FullName) | %{
$line = $_
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
}
$content = $content -join "`r`n"
$content | Set-Content $($file.FullName)
}
}

Instead of processing the files line-wise, just do the replacement operation on the entire file content at once. If the content has changed, overwrite the file.
$replacementMap = #{
"Fruit::Apple" = "NewApple"
"Fruit::Banana" = "NewBanana"
}
Get-ChildItem $testdir\reference -File -Recurse | foreach {
$content = Get-Content $_
$dirty = $false
foreach ($key in $replacementMap.Keys) {
$content = $content -replace $key,$replacementMap.$key
$dirty = $true
}
if ($dirty) { $content | Set-Content $_ }
}

Related

Powershell: Error while replacing the sub-string in a file

I am trying to replace some of the sub-strings with some values across multiple files. I have written this code to do so.
$dirPath = 'C:\repos\sync_cpc_cva\ExpressV2\Parameters\AG08\KeyVault'
$ConfigPath = 'C:\repos\sync_cpc_cva\config.json'
$lookupTable = #{}
(Get-Content -Raw -Path $configPath | ConvertFrom-Json).psobject.properties | Foreach { $lookupTable[$_.Name] = $_.Value }
Get-ChildItem -Path $dirPath -Recurse -Filter *.json |
Foreach-Object {
Get-Content $_.FullName | ForEach-Object {
$line = $_
$lookupTable.GetEnumerator() | ForEach-Object {
if($line -match $_.Key) {
$line = $line -replace $_.Key, $_.Value
}
}
$line
} | Set-Content -Path $_.FullName
}
If I print the content, it has replaced all the values correctly, however, it is unable to set the contents into the same file. And I am getting following error while running the code :
Set-Content : The process cannot access the file
'C:\repos\sync_cpc_cva\ExpressV2\Parameters\AG08\KeyVault\KeyVault.USNat.json' because it is being used by another process.
At C:\repos\sync_cpc_cva\filltest.ps1:22 char:9
+ } | Set-Content -Path $_.FullName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Set-Content], IOException
+ FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.SetContentCommand
Any idea how can I replace the sub-strings in the same file?
You have several inner foreach-object loops, so you modify the $_ operator while being used. I dont think this can work. Revolve it like this:
$lookupTable = #{}
(Get-Content -Raw -Path $configPath | ConvertFrom-Json).psobject.properties | Foreach { $lookupTable[$_.Name] = $_.Value }
$files = Get-ChildItem -Path $dirPath -Recurse -Filter *.json
foreach ($file in $files) {
$content = Get-Content $file.FullName
for($i=0;$i -lt $content.length; $i++) {
$lookupTable.GetEnumerator() | ForEach-Object {
$content[$i] = $content[$i] -replace $_.Key, $_.Value
}
}
$content | Set-Content $file.FullName
}

replacing more text strings with certain text strings in powershell

my code replace one text for another text well
$pathhh = "E:\times"
$searchWords = 'NEWYORK'
$replaceWord = 'CANCELED'
Foreach ($sw in $searchWords)
{
Get-Childitem -Path $pathhh -Recurse |
Select-String -Pattern "$sw" |
Select Path,LineNumber,#{n='SearchWord';e={$sw}}
}
if (1 -eq 1) {
$files = Get-Childitem -Path $pathhh -File -Recurse
foreach ($file in $files) {
$content = get-content -raw $file.PSPath
# regex may have problems
$content = $content -replace $searchWords,
$replaceWord
set-content $file.PSPath $content
}
}
but I need more different replacements like
replace NEWYORK with CANCELLED and replace LA with INFO and replace LONDON with DELAYED so more text replacements so like
$searchWords = 'NEWYORK,LA,LONDON'
$replaceWord = 'CANCELED,INFO,DELAYED'
but have no idea how to connect it all together thank you for your help!
Instead of using two comma delimited strings as you are trying now, I would suggest creating a replacements map (Hashtable) to store the words to replace and their replacement strings in one easy to use structure.
Something like this:
$replacements = #{
'NEWYORK' = 'CANCELLED'
'LA' = 'INFO'
'LONDON' = 'DELAYED'
}
$files = Get-Childitem -Path $pathhh -File -Recurse
foreach($file in $files) {
$content = Get-Content -Path $file.FullName -Raw
foreach ($item in $replacements.Keys) {
$content = $content.Replace($item, $replacements[$item])
}
Set-Content -Path $file.FullName -Value $content
}

How to modify Firefox config (pref.js) line using Powershell

Modifying the Mozilla Firefox Pref.js file using PowerShell.
Need to modify the pref.js file line to reflect a new value
(Hence, instead of any value under user_pref("network.automatic-ntlm-auth.trusted-uris", "*"); to show user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com") or just add changes after the *
$TopDir = "$env:APPDATA\Mozilla\Firefox\Profiles"
$FileName = 'prefs.js'
$DefaultProfileDir = (Get-ChildItem -LiteralPath $TopDir -Directory).
Where({
$_.FullName -match '\.default'
}).FullName
$FullFileName = Join-Path -Path $DefaultProfileDir -ChildPath $FileName
$data = foreach($line in Get-Content $FullFileName)
{
if($line -contains 'user_pref("network.automatic-ntlm-auth.trusted-uris".*')
{
$line -replace '*' , 'user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com");'
}
else
{
$line
}
}
$data | Set-Content C:\testfolder\test2.txt
Nelson,
I didn't have your line in my FF profile but I got the code to work on another line so you should be able to modify it to work with yours.
$TopDir = "G:\Test"
$FileName = 'prefs.js'
$FullFileName = "$TopDir\$FileName"
Clear-Host
$Data = foreach($line in Get-Content $FullFileName) {
if( ($line.IndexOf('browser.bookmarks.restore_default_bookmarks')) -ne -1)
{
If ($line.IndexOf('false') -ne -1) {
$line = $line.replace( 'false' , 'true')
}
}
$line
}
$data | Set-Content $("$TopDir" + "\prefs2.js")
I think your code would be:
$TopDir = "$env:APPDATA\Mozilla\Firefox\Profiles"
$FileName = 'prefs.js'
$DefaultProfileDir = (Get-ChildItem -LiteralPath $TopDir -Directory).
Where({
$_.FullName -match '\.default'
}).FullName
$FullFileName = Join-Path -Path $DefaultProfileDir -ChildPath $FileName
$Data = foreach($line in Get-Content $FullFileName) {
if( ($line.IndexOf('network.automatic-ntlm-auth.trusted-uris')) -ne -1)
{
If ($line.IndexOf('*') -ne -1) {
$line = $line.replace( '*' , 'abc.com,.abcd.com,.abcde.com')
}
}
$line
}
$data | Set-Content C:\testfolder\test2.txt
Note: -contains is designed for lists.
HTH
I extended it to be easier to use any property
Input
user_pref("foo", "bar")
user_pref("network.automatic-ntlm-auth.trusted-uris", "*")
Output
user_pref("foo", "bar")
user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com")
$lines = 'user_pref("foo", "bar")', 'user_pref("network.automatic-ntlm-auth.trusted-uris", "*")'
# multi-line regex for readability
$regex = '(?x)
(?<prefix>user_pref\()
(?<name>
".*"
)
\s*,\s*
(?<value>
".*"
)
(?<suffix>.*)
'
"`ninput:"
$lines
"`noutput:"
$lines | ForEach-Object {
if($_ -match $regex) {
if($matches.name -eq '"network.automatic-ntlm-auth.trusted-uris"') {
$_ = '{0}{1}, {2}{3}' -f #(
$Matches.prefix,
$Matches.name,
'".abc.com,.abcd.com,.abcde.com"',
$Matches.suffix
)
}
}
$_
}

Windows Powershell to replace a set of characters from a text files in a folder

Working on a code which will replace a set of characters from a text files in a folder. IS there a way where it can do it for all the files in the folder. I am using a Windows 7 OS and Powershell Version 3.Attaching the code which I have. The issue is it creates a new file when I run the code (New_NOV_1995.txt) but it doesn't change any character in the new file as mentioned in the code. Help very much Appreciated.
$lookupTable = #{
'¿' = '|'
'Ù' = '|'
'À' = '|'
'Ú' = '|'
'³' = '|'
'Ä' = '-'
}
$original_file = 'C:\FilePath\NOV_1995.txt'
$destination_file = 'C:\FilePath\NOV_1995_NEW.txt'
Get-Content -Path $original_file | ForEach-Object {
$line = $_
$lookupTable.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line = $line -replace $_.Key, $_.Value
}
}
$line
} | Set-Content -Path $destination_file
In the following example, I'm assuming that H:\Replace_String is a directory. In your code above, you don't have a backslash so it would only select files in the root of H:.
$configFiles = Get-ChildItem -path H:\Replace_String\*.txt
foreach ($file in $configFiles)
{
(Get-Content $file) |
Foreach-Object { $_ -replace "Cat", "New_Cat" } |
Foreach-Object { $_ -replace "Dog", "New_Dog" } |
Set-Content $file
}
The (original) answer proposed by Tony Hinkle needs another loop. The reason for this is that Get-Content produces an array. Each line represents an element of the array.
$configFiles = Get-ChildItem -path 'H:\Replace_String\*.txt'
foreach ($file in $configFiles){
$output = #()
$content = Get-Content $file
foreach ($line in $content) {
$line = $content.Replace("Cat", "New_Cat")
$line = $content.Replace("Dog", "New_Dog")
$output += $line
}
$output | Set-Content -Path $file
}
Edit: I noticed that Tony Hinkle's answer was modified as I posted this. He's sending everything through a pipeline where I'm storing the array in a variable then looping through. The pipeline method is probably more memory efficient. The variable with second loop for each element of the array is more easily modified to do more than just the two replacments.

Add Content in Powershell

I need to search through multiple files and underneath specific lines i need to insert lines referenced previously in each respective file. So far i cannot get my script to work at all.
This is what i have so far :
$TextLocation = "M:\test"
$files = get-childitem -filter *.gto -path $TextLocation
Foreach ($file in $files) {
$pagetitle = "DS_PGSEQ-DC:"
$a = Get-Content $file.FullName | Select-String "AssignedToUserID-TZ"
$b = Get-Content $file.FullName | Select-String "EFormID-TZ"
Foreach ($line in $file)
{
if([String]$line -eq "DS_PGSEQ-DC:0001")
{
}
elseif([String]$line -eq $pagetitle)
{
Add-Content $file.FullName ($a -and $b)
}
}
}
There are two common ways for inserting text into a text file:
Process the input file(s) line-by-line, write the output to a temporary file and replace the input file(s) with them temp file afterwards.
for ($file in $files) {
$filename = $file.FullName
Get-Content $filename | % {
if ( $_ -match 'seach pattern' ) {
$_
"new line"
}
} | Out-File $tempfile
MoveItem $tempfile $filename -Force
}
Read the entire content of the file(s), insert the text by using a regular expression replacement, and write the modified content back to the file(s).
for ($file in $files) {
$text = [System.IO.File]::ReadAllText($file.FullName)
$text -replace '.*search pattern.*', "`$0`nnew line" |
Out-File $file.FullName
}
$TextLocation = "M:\test"
$Outputlocation = "M:\test\output"
$files = get-childitem -filter *.gto -path $TextLocation
Foreach ($file in $files) {
$pattern = "\d\d\d[2-9]"
$found=$false
$contains=$false
$lines = Get-Content($file.FullName) |
Foreach-object {
if ($_ -match "AssignedToUserID-TZ") {
$a = $_
}
if ($_ -match "EFormID-TZ") {
$b = $_
}
if ($_ -match "DS_PGSEQ-DC:$pattern") {
if($found -eq $false) {
$found=$true
}
} else {
$found=$false
}
if ($found -eq $true) {
$contains=$true
#Add Lines after the selected pattern
$_
$a
$b
}
if ($found -ne $true) {
$_
}
} | Set-Content($Outputlocation + "\" + $file.Name)
}