Powershell - Find Files by Searching for String and Find/Replace Text - powershell

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.

Related

How do I use PowerShell to replace text in files using a template file and the filename in place of placeholder text

I have a template file: Agriculture.txt, which includes the word "Agriculture" in several places.
I have about 30 files in a folder, including Agriculture.txt, each with no text except for the Agriculture file.
I want to replace the text in each file with the content of the Agriculture file, and replace the word "Agriculture" in each file with the name of the file (minus the .txt extension.)
I tried using these powershell commands:
$Content = Get-Content C:\Temp\Technologies\Agriculture.txt
$FileList = Get-ChildItem C:\Temp\Technologies\ | select FullName
$FileList | ForEach-Object { ($Content).replace("Agriculture",$_) | Set-Content $_.FullName }
I get an error message:
Cannot find an overload for "replace" and the argument count: "2". At
line:1 char:30
... ch-Object { ($Content).replace("Agriculture",$) | Set-Content $.Ful ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : NotSpecified: (:) [], MethodException
FullyQualifiedErrorId : MethodCountCouldNotFindBest
Can someone help me fix my Powershell commands?
Thanks!
New commands:
$Content = Get-Content -Raw C:\AMD\Temp\Technologies\Agriculture.txt
$FileList = Get-ChildItem C:\AMD\Temp\Technologies\
$FileList | ForEach-Object { ($Content).replace("Agriculture",$_.BaseName) | Set-Content $_.FullName }
Is that right?
EDIT Those last commands worked. Thank you, Santiago!
New commands:
$Content = Get-Content -Raw C:\AMD\Temp\Technologies\Agriculture.txt
$FileList = Get-ChildItem C:\AMD\Temp\Technologies\
$FileList | ForEach-Object { ($Content).replace("Agriculture",$_.BaseName) | Set-Content $_.FullName }
Answer courtesy Santiago!

Powershell Set Content Replaced string

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.

Select-String -Pattern $variable issue

I'm trying to create a report by cross referencing two text documents. I have C:\formeremployees.txt and C:\shareaudit.txt. As you can guess the formeremployees.txt has a list of former employee usernames only. No Headers; only usernames. The C:\shareaudit.txt contains a list of every folder on a share with the ACL info on the same line next to the folder path.
Here was my attempt at creating a report that only lists the lines that have user accounts from the formeremployees.txt:
$Users = Get-Content C:\formeremployees.txt
foreach ($User in $Users) {
$Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
$Output.Line | Out-File C:\completereport.txt -Append
}
But unfortunately, I get the following error:
Select-String : Cannot bind argument to parameter 'Pattern' because it is an
empty string.
At line:7 char:71
+ $Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [Select-String], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.SelectStringCommand
Select-String : Cannot bind argument to parameter 'Pattern' because it is an
empty string.
At line:7 char:71
+ $Output = Select-String -Path "C:\ShareAudit.txt" -Pattern "$User"
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [Select-String], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.SelectStringCommand
Then I get a sad, empty completereport.txt file. I can't seem to get this to work or know if it's possible.
Edit________________________
Here's what else I've tried and the results:
$Users = Get-Content C:\formeremployees.txt|?{!([string]::IsNullOrWhitespace($_))}
foreach ($User in $Users) {
$Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
$Output.Line | Out-File C:\completereport.txt -Append
}
This gave me a blank C:\completereport.txt document.
$Users = Get-Content C:\formeremployees.txt|?{!([string]::IsNullOrWhitespace($_))}
$Pattern = ($Users|ForEach{[regex]::escape($_)}) -join '|'
Get-Content "C:\Shareaudit.txt" | Where{$_ -match $Pattern} | Set-Content C:\completereport.txt
This as far as I can tell didn't do anything. There was no completereport.txt document created when it finished.
$Users=(Get-Content C:\formeremployees.txt) -ne ''
foreach ($User in $Users) {
$Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
$Output.Line | Out-File C:\completereport.txt -Append
}
This gave me a blank text document.
$Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "<single username from formeremployeee.txt>"
$Output.Line | Out-File C:\completereport.txt -Append
When I put in a username that I knew still had permissions to some folders in the share and was also in the formeremployee.txt, the script worked as intended and gave me a list of the folders I needed so there's nothing wrong with the bottom part of the script, so I'm guessing something is up with the formeremployee.txt or the way I used the $Users variable.
To test further, I tried this:
$Users=(Get-Content C:\formeremployees.txt) -ne ''
foreach ($User in $Users) {
Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
}
This didn't output any results. The text formeremployee.txt file lists the usernames as follows:
username1
username2
username3
username4
Is it in the wrong format for this?
The most obvious answer is that you have blank lines in your FormerEmployee.txt file. The simplest solution is to update your first line:
$Users = Get-Content C:\formeremployees.txt|?{!([string]::IsNullOrWhitespace($_))}
What I would probably do to speed things up is make a regex pattern out of the users, and run the Select-String once, instead of once per user:
$Users = Get-Content C:\formeremployees.txt|?{!([string]::IsNullOrWhitespace($_))}
$Pattern = ($Users|ForEach{[regex]::escape($_)}) -join '|'
Get-Content "C:\Shareaudit.txt" | Where{$_ -match $Pattern} | Set-Content C:\completereport.txt

PowerShell v4 Get-Content : A parameter cannot be found that matches parameter name 'Delimiter'

I am hoping someone can give me the push I need to get this done, but I have had no luck with this exact situation online.
I need to use PowerShell (unfortunately I can't use Python or .NET to so this:( ) to parse though a list of files to determine if they contain a line termination of /r rather than /r/n. This script was previously in production and working, when single files were passed.
I am making adjustments so that multiple files can be accommodated.
I am getting the list of filenames and putting them into an array (which is working) but when I tey to loop the files through the if statement I get this error.
Here is my code:
param(
#[Parameter(Mandatory=$True)]
[String]$FileName = "C:\LineTermTest\*C*.txt"
)
$FileNameArray = Get-ChildItem -Path $FileName | where {!$_.psicontainter }| Select-Object fullname
for ($i=0; $i -le $FileNameArray.Length -1; $i++ )
{
$File = $FileNameArray[$i]
if (Get-Content -path $File -Delimiter "`0" | Select-String "[^`r]`n" )
{
$content = Get-Content $File
$content | Set-Content $File -Replace "`n", "'r'n" -Encoding ASCII
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}
}
and here is the error I get
Get-Content : A parameter cannot be found that matches parameter name 'Delimiter'.
At line:13 char:39
+ if (Get-Content -path $File -Delimiter <<<< "0" | Select-String "[^r]`n" )
+ CategoryInfo : InvalidArgument: (:) [Get-Content], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : A parameter cannot be found that matches parameter name 'Delimiter'.
At line:13 char:39
+ if (Get-Content -path $File -Delimiter <<<< "0" | Select-String "[^r]`n" )
+ CategoryInfo : InvalidArgument: (:) [Get-Content], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : A parameter cannot be found that matches parameter name 'Delimiter'.
At line:13 char:39
+ if (Get-Content -path $File -Delimiter <<<< "0" | Select-String "[^r]`n" )
+ CategoryInfo : InvalidArgument: (:) [Get-Content], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.GetContentCommand
in your script, $File is a 'PSCustomObject', not a FileInfo object, so you need to use its property 'fullname' like this: Get-Content -path $File.fullname -Delimiter "`0". This is because the objects in $FileNameArray are created by using Select-Object.
You can avoid this by leaving out "| Select-Object fullname" when creating $FileNameArray. That way, the $FileNameArray contains FileInfo objects, and Get-Content can use those directly. This is the preferred way.
So this line:
$FileNameArray = Get-ChildItem -Path $FileName | where {!$_.psicontainter }| Select-Object fullname
becomes this line:
$FileNameArray = Get-ChildItem -Path $FileName | where {!$_.psicontainter }

file rename in bulk in powershell using foreach loop

$folderpath = 'E:\BOOKS\Python\python\python'
$items = Get-ChildItem -Recurse $folderpathc *_pdf
foreach( $i in $items) { Rename-Item E:\BOOKS\Python\python\python\$i E:\BOOKS\Python\python\python\$i.pdf }
Hi, I tried to rename the file under a folder using above command but not able to do and got below error.
Rename-Item : Cannot rename because item at 'E:\BOOKS\Python\python\python\book_pdf' does not exist.
At line:1 char:37
+ foreach( $i in $items) { Rename-Item <<<< E:\BOOKS\Python\python\python\$i E:\BOOKS\Python\python\python\$i.pdf }
+ CategoryInfo : InvalidOperation: (:) [Rename-Item], PSInvalidOperationException
+ FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.RenameItemCommand
Looks like you want to change all '_pdf' to '.pdf', if so this is a pretty easy way to do it.
ls -Path 'E:\BOOKS\Python\python\python' -Filter *_pdf |
ForEach-Object {$_ | Rename-Item -NewName $_.Name.Replace('_pdf', '.pdf')}
You're over complicating things. Don't re-type out the path name to the file, use the FullName property already provided by the Get-ChildItem cmdlet. Then just use a substring of the BaseName property to remove the last 4 characters, and add ".pdf" to the end.
$folderpath = 'E:\BOOKS\Python\python\python'
$items = Get-ChildItem -Recurse $folderpathc *_pdf
foreach( $i in $items) {
Rename-Item $i.FullName ($i.basename.substring(0,$i.BaseName.length-4)+".pdf")
}