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
Related
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 # ...
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.
So I am trying to pipe in a file list into import CSV:
ls *.csv | select FullName | where FullName -NotMatch "fixed" | ForEach-Object {
Import-Csv -Path %($_.FullName) -Delimiter ";"
}
But I am getting this error:
Import-Csv : A positional parameter cannot be found that accepts argument
'C:\***\Documents\Daraz Order\order.list.export 2019-11-13.csv'.
At line:2 char:1
+ Import-Csv -Path %($_.FullName) -Delimiter ";"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Import-Csv], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.ImportCsvCommand
It gives the same error for all the files in the list. I have tried the ToString() method. But the error persist:
+ Import-Csv -Path %($_.FullName.ToString()) -Delimiter ";"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Import-Csv], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.ImportCsvCommand
What might be causing this?
The answer Patrick gave you is correct. However you still get the error. You should try different methods of troubleshooting (using Powershell_ISE obviously).
$import = #()
ls *.csv| select FullName | where FullName -NotMatch "fixed" | foreach-object {
$path = $_.FullName
$path.GetType()
$path
try{
Get-Content -Path $path
}catch{
Write-Host "Path does not exist"
}
$import += Import-Csv -path $path -delimiter ";"
}
In the above example, to make sure we don't get distracted, we put all the content in the import array. Next look at the path, does it exist? Can you obtain the content of the file? Let's look at the type of the file by trying .GetType().
The error basically says that Import-CSV does not except your argument. So try fixing Import-CSV with a valid path to see if you get it to work first.
PS: Your script does actually work on my system (PowerShell 5.1).
It seems some idiosyncrasy of PowerShell 6. I just decided to use a code block and just pipes. The code that worked for me was:
ls *.csv| select FullName,BaseName | where FullName -NotMatch "fixed"| foreach {
$file = $_.FullName.ToString()
$bname = $_.BaseName.ToString()
$bnamepath = ".\$bname-fixed.csv"
Import-Csv $file -Delimiter ';' | Export-Csv $bnamepath -Delimiter ','
}
I still had to do a pipe at the end because keeping the data in a variable causes the output file just have the object properties and not the values of the other CSV. This script will work if you wan to process the files in Excel without changing your system's locale.
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 am building a script to pull information from one system and place it into a CSV for another system to pull it. I have to do some data cleanup before that though. I am pulling userid's from the system and they come out as dom\userid. For my other system to use it i have to have just the userid with no dom. I used the below code to remove dom and this works:
$csv = Import-Csv C:\Support\Broker.csv
$csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -replace 'DOM', '' } }
$csv | Export-Csv C:\Support\Broker.csv -NoTypeInformation -Force
This leaves me with \userid. As soon as I add the \ to the replace string, the system freaks because it tries to interpret the \ and can't do a replace so I tried the following
$csv = Import-Csv C:\Support\Broker.csv
$csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -replace 'DOM`\', '' } }
$csv | Export-Csv C:\Support\Broker.csv -NoTypeInformation -Force
I get the same error
ERROR: Regular expression pattern is not valid: DOM_WFLD\.
broker.ps1 (24): ERROR: At Line: 24 char: 54
ERROR: + $csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -repl ...
ERROR: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~
ERROR: + CategoryInfo : InvalidOperation: (DOM_WFLD:String) [], RuntimeException
ERROR: + FullyQualifiedErrorId : InvalidRegularExpression
I can't seem to get this to work. Does anyone have any ideas?
On the line that contains $csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -replace 'DOM\', '' } }`
It could be simplified and made more versitale.
$csv | %{ $_.LastConnectionUser -replace '\w+\\'}
If statement for something like this is not really required since the action you are taking doesnt significantly increase processing time. Also your IF will match if a user name contains DOM which i imagine was not your intention.
Which would replace all characters infront of a \ including the \ itself. As David explains you need to escape the \ as it is a special chracter in regular expressions. -replace with no second parameter automatically just removes the match.
$csv = Import-Csv C:\Support\Broker.csv
$csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -replace 'DOM\\', '' } }
$csv | Export-Csv C:\Support\Broker.csv -NoTypeInformation -Force
The escape you have to use is the regular expression one (so backslash), not the PowerShell one (back tick)