So, i need to save a string that was replaced on files.
I'm doing something wrong, but i`m not be able to figure it out, what!
My code:
Get-ChildItem -Path C:\Users\user\Documents -Recurse -Include "*.txt" -File | Select-String -Pattern \b192\.168\.10\.2\b , \b192\.168\.10\.11\b -AllMatches -List | Foreach-Object { $_ -replace '\b192\.168\.10\.2\b', "DEV" -and $_ -replace '\b192\.168\.10\.11\b', "QUAL" | Set-Content $_}
And gives-me the following error:
Set-Content : Could not open the alternate data stream '1:192.168.10.11' of the file 'C:\Users\user\Documents\result.txt'.
At line:1 char:323
+ ... place '\b192\.168\.10\.11\b', "QUAL" | Set-Content $_}
+
+ CategoryInfo : ObjectNotFound: (C:\Users\paulo....ents\result.txt:String) [Set-Content], FileNotFoundException
+ FullyQualifiedErrorId : GetContentWriterFileNotFoundError,Microsoft.PowerShell.Commands.SetContentCommand
Set-Content : Could not open the alternate data stream '1:192.168.10.11' of the file
'C:\Users\user\Documents\test.txt'
At line:1 char:323 ... place '\b192\.168\.10\.11\b', "QUAL" | Set-Content $_}
CategoryInfo : ObjectNotFound: (C:\Users\user\test.txt:String) [Set-Content], FileNotFoundException
FullyQualifiedErrorId : GetContentWriterFileNotFoundError,Microsoft.PowerShell.Commands.SetContentCommand
Thanks for any Help!
Inside the ForEach-Object block, $_ will refer to the current match result as returned by Select-String - to get the file path, reference the Path property:
... | ForEach-Object { ... |Set-Content -LiteralPath $_.Path}
The -and operator is used inside if tests like if(this -and that).
You should change the double replace actions from
$_ -replace '\b192\.168\.10\.2\b', "DEV" -and $_ -replace '\b192\.168\.10\.11\b', "QUAL"
into
$_ -replace '\b192\.168\.10\.2\b', "DEV" -replace '\b192\.168\.10\.11\b', "QUAL"
Also, if I understand the question properly, you want to find all string replacements in the file, and to get all, you need to remove the -List switch from Select-String.
Next, as Mathias explains in his answer, you need to use the Path property from the current match to get the file FullName.
However, if you pipe this through to Set-Content straight away, you will get an exception because the file then is in use and you cannot write to the same file.
Below creates a new file in the same path, with _replacements appended to the filename
# use '-Include' instead of '-Filter' if you need more file extensions to filter on
Get-ChildItem -Path 'C:\Users\user\Documents' -Recurse -Filter "*.txt" -File |
Select-String -Pattern '\b192\.168\.10\.2\b', '\b192\.168\.10\.11\b' -AllMatches |
Foreach-Object {
$file = '{0}_replacements{1}' -f [System.IO.Path]::GetFileNameWithoutExtension($_.Path),
[System.IO.Path]::GetExtension($_.Path)
$target = Join-Path -Path ([System.IO.Path]::GetDirectoryName($_.Path)) -ChildPath $file
$_ -replace '\b192\.168\.10\.2\b', "DEV" -replace '\b192\.168\.10\.11\b', "QUAL" |
Add-Content -Path $target
}
This results in a file called 'C:\Users\user\Documents\test_replacements.txt'
C:\Users\user\Documents\test.txt:4:DEV
C:\Users\user\Documents\test.txt:7:QUAL
The original file 'C:\Users\user\Documents\test.txt' will not be altered.
Related
Although not a developer, I've been finding answers to problems via StackOverflow for a while now, and feel like I owe a lot of y'all a cold beer or two.
I've run into an issue that I can't seem to wrap my head around.
I would like to be able to run the following, to convert a group of jpg files to Base64 for submission to a regulatory agency that requires this format:
[Convert]::ToBase64String((Get-Content C:\temp\168211_wheels.jpg -Encoding Byte)) >> c:\temp\Pictest.txt
I will run this against a group of jpg files, all in the same directory.
My first, fairly tragic, attempt was this:
Get-ChildItem -Path ' C:\testimages\*' -Include '*.jpg' | ForEach-Object {
[Convert]::ToBase64String((Get-Content C:\testimages\*.jpg -Encoding Byte)) >> } c:\temp\Pictest.txt
I receive the following Error Message:
**At line:1 char:152
+ ... e64String((Get-Content C:\testimages\*.jpg -Encoding Byte))>>}c:\temp ...
+ ~
Missing file specification after redirection operator.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingFileSpecification**
Any guidance or suggestions are very welcomed, as this is only my second attempt at working in Powershell.
Regards,
Darren
[System.IO.Directory]::EnumerateFiles('S:\SCRIPTS\GC', '*.exe', [System.IO.SearchOption]::AllDirectories) |
ForEach-Object {
return [PSCustomObject]#{
SourceFile = $_
TargetFile = [System.IO.Path]::ChangeExtension($_, 'j64')
}
} |
ForEach-Object {
$content = $null
$content = [System.IO.File]::ReadAllBytes($_.SourceFile)
$encodedContent = $null
$encodedContent = [System.Convert]::ToBase64String($content, [System.Base64FormattingOptions]::InsertLineBreaks)
[System.IO.File]::WriteAllText($_.TargetFile, $encodedContent, [System.Text.Encoding]::ASCII)
Write-Host "Processed $($_.SourceFile)"
}
Using [System.IO.Directory]::EnumerateFiles(...) is faster on huge amount of files because it returns only full file names without any other properties.
It can be replaced by
Get-ChildItem -Path 'S:\SCRIPTS\GC' -Filter '*.exe' -File | Select -ExpandProperty 'FullName' if you want in this case.
[System.IO.File]::ReadAllBytes(...) and [System.IO.File]::WriteAllText(...) is just more obivious in this case, in my opinion.
And please, avoid redirection operators like '>>' and '>' using powershell. They create problems quite often.
Assuming you want one base64 encoded file per jpg file, this is how you could do it:
Get-ChildItem -Path 'C:\testimages\*' -Filter *.jpg | ForEach-Object {
$fileName = "{0} - Base64Encoded.txt" -f $_.BaseName
$path = Join-Path $_.Directory -ChildPath $fileName
$content = Get-Content $_.FullName -Encoding Byte -Raw
[Convert]::ToBase64String($content) | Out-File $path -NoNewline
}
Which will output one txt file with the same name as the original file followed by - Base64Encoded.
Then if you want to convert the files to their original format you can decode them like this:
Get-ChildItem -Path 'C:\testimages\*' -Filter *Base64Encoded* | ForEach-Object {
$fileName = "{0} - Decoded.jpg" -f $_.BaseName
$path = Join-Path $_.Directory -ChildPath $fileName
$content = Get-Content $_.FullName -Raw
[convert]::FromBase64String($content) | Set-Content $path -Encoding Byte
}
I'm trying to search through a directory of files and pull out all the file paths that have the pattern. Then loop through each file and search for another pattern of text. It works if I manually do:
Select-String -Path "C:\inetpub\mailroot\Badmail-Archive\003c908531613052021000000A2.BAD" -Pattern ('Final-Recipient') | Select -ExpandProperty line
It does not if I do it in the loop:
$FileList = Get-ChildItem "C:\inetpub\mailroot\Badmail-Archive" -Filter *.BAD | Select-String -Pattern 'Diagnostic-Code: smtp;550 5.1.1' | Select-Object Path
$FileList += Get-ChildItem "C:\inetpub\mailroot\Badmail-Archive" -Filter *.BAD | Select-String -Pattern 'Diagnostic-Code: smtp;550 5.1.2' | Select-Object Path
$FileList += Get-ChildItem "C:\inetpub\mailroot\Badmail-Archive" -Filter *.BAD | Select-String -Pattern 'Diagnostic-Code: smtp;550 5.2.1' | Select-Object Path
foreach($filepath in $FileList) {
$BADSearch = Select-String -Path $filepath -Pattern ('Final-Recipient') | Select -ExpandProperty line
$eMailAddress = $BADSearch.Split(";")[1]
echo "File Path: $filepath"
echo $eMailAddress
}
File Path: C:\inetpub\mailroot\Badmail-Archive\003c908531613052021000000A2.BAD
Select-String : Cannot find drive. A drive with the name '#{Path=C' does not exist.
At C:\Scripts\BadEmails-SQLGenerator.ps1:46 char:14
+ ... BADSearch = Select-String -Path $filepath -Pattern ('Final-Recipient' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (#{Path=C:String) [Select-String], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.SelectStringCommand
Your problem is with treating any arbitrary object as if it's a string. Select-String expects a string instance for the Path parameter, but you're passing it the resulting MatchInfo object returned from the previous Select-String call.
You'll need to tease out the path by selecting the .Path member from the previous command output:
Select-String -Path $filepath.Path # ...
I'm learning powershell and trying to write a script that can find files in a directory by a string, and then do a find and replace on the files that are found. I want to store the file list as a variable and then loop through the files and replace specific strings. Here's my script and error, if you have any ideas it would be much appreciated. Thanks!
$GetFiles = Select-String -path "C:\temp\*.xml" -pattern "<cmn:BusinessName>ABC INC</cmn:BusinessName>"|Select-Object filename
foreach ($file in $GetFiles)
{
(Get-Content $Files.PSPath) |Foreach-Object {
$_ -replace "<cmn:FileNumber>0001234</cmn:FileReceiverNumber>", "<cmn:FileReceiverNumber>12345678</cmn:FileReceiverNumber>" `
-replace "<cmn:DropIndicator>DROP</cmn:Indicator>", "<cmn:DropIndicator>DONTDROP</cmn:DropIndicator>"
} |Set-Content $Files.PSPath
}
ERROR
Get-Content : Cannot bind argument to parameter 'Path' because it is
null. At C:\scripts\script.ps1:5 char:18
+ (Get-Content $Files.PSPath) |Foreach-Object {
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-Content], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetContentCommand
$GetFiles = Select-String -Path "C:\temp\*.xml" -Pattern "<cmn:BusinessName>ABC INC</cmn:BusinessName>"
foreach ($File in $GetFiles)
{
$NewContent = Get-Content $File.Path | Foreach-Object {
$_ -replace "<cmn:FileNumber>0001234</cmn:FileReceiverNumber>", "<cmn:FileReceiverNumber>12345678</cmn:FileReceiverNumber>" `
-replace "<cmn:DropIndicator>DROP</cmn:Indicator>", "<cmn:DropIndicator>DONTDROP</cmn:DropIndicator>"
}
$NewContent | Set-Content $File.Path
}
In foreach, you have defined variable named $File but in inside your foreach statement, you are $Files instead of $File
In Line 1, by using Select-Object Filename in the end, $GetFiles has object with only one property Filename. So PSPath property doesn't exist. that is why you getting the null error. BTW the output of Select-String command doesn't have PSPath property.
I'm having problems after a folder is copied to a different location, I need to rename the folders in the directory to remove ".deploy" from the end, but I get the following error below. I have Googled around for PowerShell admin permissions, but cannot seem to find a 'catch-all' for my scenario.
Get-Content : Access to the path 'C:\OldUserBackup\a.deploy' is denied.
At C:\PSScripts\DesktopSwap\TestMergeDir.ps1:28 char:14
+ (Get-Content $file.PSPath) |
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\OldUserBackup\a.deploy:String) [Get-Content], UnauthorizedAccessException
+ FullyQualifiedErrorId : GetContentReaderUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetContentCommand
Here is what I have:
$UserName = [Environment]::UserName
$CurrUser = [Environment]::UserName + '.deploy'
$OldUserDir = 'C:\OldUserBackup'
$CurrDate = Get-Date -format G
$PathExist = Test-Path $OldUserDir
if ($PathExist -eq $true) {
#Copy Desktop, Downloads, Favorites, Documents, Music, Pictures, Videos
Copy-Item -Path $OldUserDir -Destination C:\Users\$UserName\Desktop\CopyTest -Recurse -Force
$configFiles = Get-ChildItem $OldUserDir *.deploy -rec
foreach ($file in $configFiles) {
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace ".deploy", "" } |
Set-Content $file.PSPath
}
}
You should use the -Directory switch on the Get-ChildItem cmdlet to only get directories. Then use the Rename-Item cmdlet to rename the folders. I use the -replace function with a simple regex to get the new folder name:
$deployFolders = Get-ChildItem $OldUserDir *.deploy -rec -Directory
$deployFolders | Foreach {
$_ | Rename-Item -NewName ($_.Name -replace ('\.deploy$') )
}
You don't even have to use the Foreach-Object cmdlet (Thanks to AnsgarWiechers):
Get-ChildItem $OldUserDir *.deploy -rec -Directory |
Rename-Item -NewName { $_.Name -replace ('\.deploy$') }
I want to replace all space characters into "_" in names of all subfolders and files.
Unfortunately when I type:
Get-ChildItem -recurse -name | ForEach-Object { Rename-Item $_ $_.replace(" ","_") }
Error message:
Rename-Item : Source and destination path must be different. At line:1
char:60
+ Get-ChildItem -recurse -name | ForEach-Object { Rename-Item <<<< $_ $.replace(" ","") }
+ CategoryInfo : WriteError: (PATH_HERE) [Rename-Item], IOException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
How I should improve this short code?
Don't use the Name switch, it outputs only the names of the objects, not their full path. Try this:
Get-ChildItem -Recurse | `
Where-Object {$_.Name -match ' '} | `
Rename-Item -NewName { $_.Name -replace ' ','_' }
The issue here is that if there is no space in the file name the name does not change. This is not supported by Rename-Item. You should use Move-Item instead:
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace(" ", "_") }
Additionally, in your answer you missed the underscore in $_.replace(...) plus you where replacing spaces with an empty string. Included this in my answer.
Adding a filter worked for me:
Get-ChildItem C:\path-to-directory -Recurse -Filter *foo* | Rename-Item -NewName { $_.name -replace 'foo', 'bar'} -verbose