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

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
}

Related

Write a script to sort words in alphabet order from specific file and put them into 26 text files named A.txt, B.txt, and so on up to Z.txt

I need to sort words alphabetically from a specific file and put them into 26 text files named A.txt, B.txt and so on up to Z.txt.
$Content = Get-Content ".\\1.txt"
$Content = ($Content.Split(" .,:;?!/()\[\]{}-\`\`\`"")|sort)
$linecount = 0
$filenumber = 0
$destPath = "C:\\test"
$destFileSize = 26
$Content |Group {$_.Substring(0,1).ToUpper()} |ForEach-Object {
$path = Join-Path $destPath $_.Name
$\_.Group |Set-Content $path
}
$Content | % {
Add-Content $destPath$filenumber.txt "$\_"
$linecount++
If ($linecount -eq $destFileSize) {
$filenumber++
$linecount = 0
}
}
You could do something like this, but this also could mean some files may not be written if there are no words beginning with a certain letter found in the file:
$destPath = "D:\test"
(Get-Content -Path 'D:\Test\Lorem.txt' -Raw) -split '\W' -ne '' |
Group-Object {$_.Substring(0,1).ToUpperInvariant()} |
Where-Object {$_.Name -cmatch '[A-Z]'} | ForEach-Object {
$_.Group | Sort-Object | Set-Content -Path (Join-Path -Path $destPath -ChildPath ('{0}.txt' -f $_.Name))
}
If you always want exactly 26 files even if some may contain nothing, use this instead
$destPath = "D:\test"
$wordGroups = (Get-Content -Path 'D:\Test\Lorem.txt' -Raw) -split '\W' -ne '' |
Group-Object {$_.Substring(0,1).ToUpperInvariant()}
foreach ($char in ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' -split '(.)' -ne '')) {
$outFile = Join-Path -Path $destPath -ChildPath ('{0}.txt' -f $char)
$group = $wordGroups | Where-Object { $_.Name -eq $char }
if ($group) { $group.Group | Sort-Object | Set-Content -Path $outFile } # output the found words
else { $null | Set-Content -Path $outFile } # or create an empty file
}
The Where-Object {$_.Name -cmatch '[A-Z]'} clause makes it ignore words starting with some other character than A to Z

Issue with nested 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 $_ }
}

Script working for 1 directory path but not for multiple directory paths

I am trying to
Create a CD_TMP file in each WE*.MS directory
Set content by processing the AHD*.TPL and ADT*.TPL files
Rename the AHD*.TPL to AHD*.TPL.Done and ADT*.TPL to AHD*.TPL.Done.
When there is only one WE.20150408.MS directory, the scripts works fine
but when there are more than one directories (i.e. WE.20150408.MS, WE.20151416.MS,WE.20140902.MS), it does not work and gives error message:
Get-Content: An object at specified path AHD*TPL does not exist of has been filtered by the -Include or -Exclude parameter.
At C:\Temp\Script\Script.ps1:24 Char:14
+ $content = Get=Content -path $AHD
+ CatagoryInfo :ObjectNotFound: (System.String[]:Strint[1) [Get-Content], Exception
+ FullyQualifiedErrorID: ItemNotFound,Micorsoft.Powershell.Commands.GetContentCommand
SCRIPT:
$SOURCE_DIR = "C:\Work"
$Work_DIR = "WE*MS"
$WE_DIR = "$SOURCE_DIR\$Work_DIR"
$AHD = "AHD*TPL"
$ADT = "ADT*TPL"
$AHD_FILES = $SOURCE_DIR
$CD_TMP = "CD_TMP"
$Str1 = "TEMP"
##############
Set-Location $WE_DIR
New-Item -Path "CD_TMP" -type file -force
#############
foreach ( $File in ( get-childitem -name $WE_DIR))
{
$content = Get-Content -path $AHD
$content | foreach {
If ($_.substring(0,4) -NotLike $Str1)
{
'0011' + '|' + 'HD' + '|' + 'AHD' + $_
}
} | Set-Content $CD_TMP
}
Get-ChildItem AHD*.TPL| ForEach {Move-Item $_ ($_.Name -replace ".TPL$",
".TPL.Done")}
##############
foreach ( $File in ( get-childitem -name $WE_DIR))
{
$content = Get-Content -path $ADT
$content | foreach {
If ($_.substring(0,4) -NotLike $Str1)
{
'0022' + '|' + 'DT' + '|' + 'ADT' + $_
}
} | Set-Content $CD_TMP
}
Get-ChildItem ADT*TPL| ForEach {Move-Item $_ ($_.Name -replace ".TPL$",
".TPL.Done")}
PAUSE
Is it first giving the error Set-Location : Cannot set the location because path 'C:\Work\WE*MS' resolved to multiple containers. ? That's what I expect it to say when it fails.
Then, because it can't change into the folder, it can't find any AHD files.
Does it work properly for one folder? It writes the CD_TMP file for AHD files, then overwrites it for ADT files. That doesn't seem right.
Also you can make it a bit more direct by changing:
putting lots of things in $CAPITAL variables at the start, then using them once, or never.
The .substring() -notlike test to use .startswith()
The string building with ++++ into a single string
The renaming into a Rename-Item with -NewName scriptblock
I'm thinking this:
$folders = Get-ChildItem "C:\Work\WE*MS" -Directory
foreach ($folder in $folders) {
# AHD files
$content = Get-Content "$folder\AHD*.TPL"
$content = $content | where { -not $_.StartsWith('TEMP') }
$content | foreach {"0011|HD|AHD$_"} | Set-Content "$folder\CD_TMP" -Force
Get-ChildItem "$folder\AHD*.TPL" | Rename-Item -NewName {$_.Name + '.Done'}
# ADT files
$content = Get-Content "$folder\ADT*.TPL"
$content = $content | where { -not $_.StartsWith('TEMP') }
$content | foreach {"0011|HD|ADT$_"} | Add-Content "$folder\CD_TMP"
Get-ChildItem "$folder\ADT*.TPL" | Rename-Item -NewName {$_.Name + '.Done'}
}
Although I don't know what the input or output should be, so I can't test it. NB. it now does Add-Content to append to the CD_TMP file, instead of overwriting it.
There's still alot of redundancy with $content, but the lines mostly stand alone like this.

Powershell mystical error

Please help! i'm confused what to do.
Script split files by content:
$InPC = "C:\Scripts\"
Get-ChildItem $InPC -Filter *.prt | ForEach-Object -Process {
$basename= $_.BaseName
$m = ( ( Get-Content $_ | Where { $_ | Select-String "--------------------- Instance Type and Transmission --------------" -Quiet } | Measure-Object | ForEach-Object { $_.Count } ) -ge 2)
$a=1
if ($m) {
Get-Content $_ | % {
If ($_ -match "--------------------- Instance Type and Transmission --------------") {
$OutputFile = "$basename-$a.prt"
$a++
}
Add-Content $OutputFile $_
}
Remove-Item $_
}
}
Everything is going OK when i'm set-location to C:\Scripts. But in base case it won't work and give next error:
Get-Content : Path not found "C:\Users\a.ulianov\PRTPRT.prt".
C:\Scripts\2.ps1:23 знак:18
+ $m = ( ( Get-Content $_ | Where { $_ | Select-String "------------------ ...
+ ~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\a.ulianov\PRTPRT.prt:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : Path not found "C:\Users\a.ulianov\test.prt".
C:\Scripts\2.ps1:23 знак:18
+ $m = ( ( Get-Content $_ | Where { $_ | Select-String "------------------ ...
+ ~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\a.ulianov\test.prt:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
it's seems to work with default PS location during excuting. What i might modify in this case?
#
thanks to #Raf #Rynant here is little crooked but working solution:
$InPC = "C:\Scripts"
Get-ChildItem -Path $InPC -Filter *.prt | ForEach-Object -Process {
$basename= $_.BaseName
$m = ( ( Get-Content $_.FullName | Where { $_ | Select-String "--------------------- Instance Type and Transmission --------------" -Quiet } | Measure-Object | ForEach-Object { $_.Count } ) -ge 2)
$a = 1
if ($m) {
Get-Content $_.FullName | % {
If ($_ -match "--------------------- Instance Type and Transmission --------------") {
$OutputFile = "$InPC\$basename _$a.prt"
$a++
}
Add-Content $OutputFile $_
}
Remove-Item $_.FullName
}
}
Looks a bit messy but I think the only change you need to do is replace:
Get-Content $_
with
Get-Content $_.FullName

Replace multiple strings in a file using powershell

I am using following coe to replace the string
$folders=Get-ChildItem -Path "C:\temp\Database Scripts"
foreach($folder in $folders)
{
Write-Host $folder
$spath=[string]::Concat("C:\temp\Database Scripts\", $folder)
$subfolders=Get-ChildItem $spath
foreach($subfolder in $subfolders )
{
if($subfolder -match "Running Scripts")
{
$subfolerpath=[string]::Concat($spath,"\",$subfolder,"\*")
$files =get-childitem -Path $subfolerpath -include "AVEVAScripts*"
if($files -ne $null)
{
foreach( $file in $files)
{
Write-Host $file;
(Get-Content $file) | ForEach-Object {$_ -replace "DATABASE_USER","fhghjgj" `
-replace "DATABASE_PASSWORD", "DFGHFHJGJH" } |Set-Content $file
}
}
}
}
}
But ending up with following error.
Set-Content : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
Please help :)
Remove the $x in the end of Set-Content. $x is never declared.
Also, you could simplify it a lot. Ex:
Get-ChildItem -Filter "Running Scripts" -Path "C:\temp\Database Scripts" -Recurse | ForEach-Object {
Get-ChildItem -Path $_.FullName -Filter "AVEVAScripts*" -Recurse | ForEach-Object {
(Get-Content $_.FullName) | ForEach-Object {
$_ -replace "DATABASE_USER","fhghjgj" -replace "DATABASE_PASSWORD", "DFGHFHJGJH"
} | Set-Content $_.FullName
}
}
Or find all files that includes "AVEVAScripts" in it's name, then check if their full path includes "Running Scripts"
Get-ChildItem -Filter "AVEVAScripts*" -Path "C:\temp\Database Scripts" -Recurse |
Where-Object { $_.FullName -like "*Running Scripts*" } |
ForEach-Object {
(Get-Content $_.FullName) | ForEach-Object {
$_ -replace "DATABASE_USER","fhghjgj" -replace "DATABASE_PASSWORD", "DFGHFHJGJH"
} | Set-Content $_.FullName
}