I'm using the below PowerShell script to search and replace, which works fine.
$files = Get-ChildItem 'E:\replacetest' -Include "*.txt" -Recurse | ? {Test-Path $_.FullName -PathType Leaf}
foreach($file in $files)
{
$content = Get-Content $file.FullName | Out-String
$content| Foreach-Object{$_ -replace 'hello' , 'hellonew'`
-replace 'hola' , 'hellonew' } | Out-File $file.FullName -Encoding utf8
}
The issue is the script also modifies the files which does not have the matching text in it. How we ignore the files that do not have the matching text?
You can use match to see if the content is actually changed. Since you were always writing using out-file the file would be modified.
$files = Get-ChildItem 'E:\replacetest' -Include "*.txt" -Recurse | Where-Object {Test-Path $_.FullName -PathType Leaf}
foreach( $file in $files ) {
$content = Get-Content $file.FullName | Out-String
if ( $content -match ' hello | hola ' ) {
$content -replace ' hello ' , ' hellonew ' `
-replace ' hola ' , ' hellonew ' | Out-File $file.FullName -Encoding utf8
Write-Host "Replaced text in file $($file.FullName)"
}
}
You've got an extra foreach and you need an if statement:
$files = Get-ChildItem 'E:\replacetest' -Include "*.txt" -Recurse | ? {Test-Path $_.FullName -PathType Leaf}
foreach($file in $files)
{
$content = Get-Content $file.FullName | Out-String
if ($content -match 'hello' -or $content -match 'hola') {
$content -replace 'hello' , 'hellonew'`
-replace 'hola' , 'hellonew' | Out-File $file.FullName -Encoding utf8
}
}
Related
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
I'm trying to replace ALL accented letters and some strings in multiple files located in one folder. The strings replacement is working but not the accented letters
I've multiple files located in "C:\\FilePath"
I've created a Batch file with the following code:
#echo off
Powershell.exe -executionpolicy remotesigned -File C:\Users\User\Desktop\IFCParser.ps1
pause
And IFCParser.ps1 contains all the following lines, one after the other:
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName | Select-String -Pattern 'IFCBuilding') {(Get-Content $_ | ForEach {$_ -replace 'IFCBuilding', 'IFCBuildingElementProxy'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName | Select-String -Pattern 'IFCAnotherWord') {(Get-Content $_ | ForEach {$_ -replace 'IFCAnotherWord', 'IFCBuildingElementProxy'}) | Set-Content $_ }}
The above code DOES the job when I run the bat file, but I can't get the following part to work:
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'á' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'á', 'a'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'é' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'é', 'e'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'í' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'í', 'i'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'ó' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'ó', 'o'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'ú' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'ú', 'u'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'Á' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'Á', 'A'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'É' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'É', 'E'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'Í' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'Í', 'I'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'Ó' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'Ó', 'O'}) | Set-Content $_ }}
Get-ChildItem -Path C:\FilePath\*.* -recurse | ForEach {If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'Ú' -AllMatches) {(Get-Content $_ -Encoding UTF8 | ForEach {$_ -creplace 'Ú', 'U'}) | Set-Content $_ }}
I'm testing this on a file like this:
áéíóúÁÉÍÓÚÑñáéíóúÁ
ÉÍÓÚÑñáéíóúÁÉÍÓÚÑñá
éíóúÁÉÍÓÚÑñáéíóúÁÉÍÓÚÑñáéíó
úÁÉÍÓÚÑñáéíóúÁÉÍÓÚÑñ
And it stays the same, no accents removed.
I think that I've something wrong with the encoding, I've run this with the parameter just in the first GetContent, only on the second one, and with no -Encoding at all.
By the way, I'm sure that there are more effective ways of doing this, but I'm just starting with this here and not finding one that works.
As for replacing the contents of the files in your folder, you should be able to do that using just one Get-ChildItem call.
Put this helper function on top of your script; it is used for replacing all the accented letters in the files:
function Replace-Diacritics {
Param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string] $Text
)
($Text.Normalize( [Text.NormalizationForm]::FormD ).ToCharArray() |
Where-Object {[Globalization.CharUnicodeInfo]::GetUnicodeCategory($_) -ne
[Globalization.UnicodeCategory]::NonSpacingMark }) -join ''
}
Now the rest of the code simplified:
Get-ChildItem -Path 'C:\FilePath\*.*' -File -Recurse | ForEach-Object {
$content = Get-Content -Path $_.FullName -Raw -Encoding UTF8 | Replace-Diacritics
$content -replace '\b(IFCBuilding|IFCAnotherWord)\b', 'IFCBuildingElementProxy' | Set-Content -Path $_.FullName -Encoding UTF8
}
Using your example file, the new content after calling `Replace-Diacritics``will be:
aeiouAEIOUNnaeiouA
EIOUNnaeiouAEIOUNna
eiouAEIOUNnaeiouAEIOUNnaeio
uAEIOUNnaeiouAEIOUNn
Operator -replace uses regex. The pattern '\b(IFCBuilding|IFCAnotherWord)\b' means to find he words 'IFCBuilding' OR 'IFCAnotherWord' as whole words (\b is a Word Boundary) and replace these with 'IFCBuildingElementProxy'.
If you also need this to be case-sensitive, use -creplace instead of -replace
For very large files, Get-Content may not be the cmdlet you'll want to use as it reads the file in memory as a whole.
To handle those large files using a combination of a StreamReader and a StreamWriter would be much more memory efficient (at the cost of more disk read/write actions).
Note that you cannot read a file and write to the same file simultaniously, so the code below will create a new name for the updated file by appending _New to the BaseName.
Again start with this helper function on top
function Replace-Diacritics {
Param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string] $Text
)
($Text.Normalize( [Text.NormalizationForm]::FormD ).ToCharArray() |
Where-Object {[Globalization.CharUnicodeInfo]::GetUnicodeCategory($_) -ne
[Globalization.UnicodeCategory]::NonSpacingMark }) -join ''
}
Get-ChildItem -Path 'C:\FilePath\*.*' -File -Recurse | ForEach-Object {
# create a StreamReader to read the file line-by-line
$reader = [System.IO.StreamReader]::new($_.FullName, [System.Text.Encoding]::UTF8)
# older PowerShell versions use:
# $reader = New-Object System.IO.StreamReader($_.FullName, [System.Text.Encoding]::UTF8)
# create a full path and filename for the updated output file
$outFile = Join-Path -Path $_.DirectoryName -ChildPath ('{0}_New{1}' -f $_.BaseName, $_.Extension)
# create a StreamWriter object to write the lines to the new output file
# The StreamWriter class by default writes files with UTF-8 encoding without a Byte-Order Mark (BOM)
$writer = [System.IO.StreamWriter]::new($outFile)
# loop through the lines of the file
while ($null -ne ($line = $reader.ReadLine())) {
if (![string]::IsNullOrWhiteSpace($line)) {
$line = ($line | Replace-Diacritics) -replace '\b(IFCBuilding|IFCAnotherWord)\b', 'IFCBuildingElementProxy'
}
$writer.WriteLine($line)
}
# clean up for next file
$writer.Flush()
$writer.Dispose()
$reader.Dispose()
}
Running a single line of code on a single file like this works as expected:
Get-ChildItem -Path C:\temp\testdata.txt | ForEach-Object {
If (Get-Content $_.FullName -Encoding UTF8 | Select-String 'á' -AllMatches) {
(Get-Content $_ -Encoding UTF8 | ForEach-Object { $_ -creplace 'á', 'a' }) | Set-Content $_ }
}
Given this, your code must be failing in the file recursion or in the execution process.
Run the script in an editor before trying to run as a batch and try adding error trapping. You can also add some logging to track down what's happening when running as batch:
Start-Transcript -Path 'c:\temp\outputlog.txt'
Try {
Get-ChildItem -Path C:\temp\testdata.txt -recurse -ErrorAction Stop | ForEach-Object {
Write-Host "Processing $_"
If (Get-Content $_.FullName -Encoding UTF8 -ErrorAction Stop | Select-String 'á' -AllMatches) {
Write-Host "Found match for á, replacing...."
(Get-Content $_ -Encoding UTF8 -ErrorAction Stop | ForEach-Object { $_ -creplace 'á', 'a' }) | Set-Content $_ -ErrorAction Stop }
}
}
Catch {
$_
Stop-Transcript
}
Stop-Transcript
in particular path i need to find ""' and replace it with "' in multiple files
Tried below code but its not working due to special character to be found and replaced
$configFiles = Get-ChildItem . *.ini -rec
foreach ($file in $configFiles)
{
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace """'", ""'" } |
Set-Content $file.PSPath
}
You're not escaping your characters properly:
$configFiles = Get-ChildItem -Filter *.ini -Recurse
ForEach ($file in $configFiles)
{
#(Get-Content -Path $file.FullName) -replace "`"{2}'", "`"'" |
Set-Content -Path $file.FullName
}
I use below code to change strings in files:
Set-Location -Path C:\Users\Documents\corporate
foreach ($file in get-ChildItem *.rdl)
{
$_.Replace("Protection", "Converters") | Set-Content $file
$_.Replace("Drives", "Automation") | Set-Content $file
$_.Replace("MACHINES", "Generators") | Set-Content $file
$file.name
}
I want to add information what has changed in individual files.
For example:
file 1 Protection
file 3 Protection, MACHINES
try this way ...
Get-Content -Path "C:\Users\Documents\corporate" -Filter "*.rdl" | ForEach-Object {
$Local:CurrentFileFullName = $_.FullName
((Get-Content -Path $CurrentFileFullName ) -replace "Protection", "Converters" -replace "Drives", "Automation" -replace "MACHINES", "Generators" | Set-Content $CurrentFileFullName -Force)
}
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
}