Powershell - Find and Replace and show work - powershell

I have written this script to find "Procedure division" and replace it with a null value. It seems to work, so that's a good this.
What I need to add is the actual file names that were changed. I have 12K files and only around 800 or so are supposedly needing the change. I need to know what ones were actually changed.
Is there a way to add the path and file name to be displayed in the below script?
$old = Read-Host 'PROCEDURE DIVISION.'
$new = Read-Host ''
Get-Children D:\temp *.cbl -recurse | ForEach {
(Get-Content $_ | ForEach {$_ -replace "$old", "$new"}) | Set-Content $_
}

Related

Script lists all files that don't contain needed content

I'm trying to find all files in a dir, modified within the last 4 hours, that contain a string. I can't have the output show files that don't contain needed content. How do I change this so it only lists the filename and content found that matches the string, but not files that don't have that string? This is run as a windows shell command. The dir has a growing list of hundreds of files, and currently output looks like this:
File1.txt
File2.txt
File3.txt
... long long list, with none containing the needed string
(powershell "Set-Location -Path "E:\SDKLogs\Logs"; Get-Item *.* | Foreach { $lastupdatetime=$_.LastWriteTime; $nowtime = get-date; if (($nowtime - $lastupdatetime).totalhours -le 4) {Select-String -Path $_.Name -Pattern "'Found = 60.'"| Write-Host "$_.Name Found = 60"; }}")
I tried changing the location of the Write-Host but it's still printing all files.
Update:
I'm currently working on this fix. Hopefully it's what people were alluding to in comments.
$updateTimeRange=(get-date).addhours(-4)
$fileNames = Get-ChildItem -Path "K:\NotFound" -Recurse -Include *.*
foreach ($file in $filenames)
{
#$content = Get-Content $_.FullName
Write-host "$($file.LastWriteTime)"
if($file.LastWriteTime -ge $($updateTimeRange))
{
#Write-Host $file.FullName
if(Select-String -Path $file.FullName -Pattern 'Thread = 60')
{
Write-Host $file.FullName
}
}
}
If I understood you correctly, you just want to display the file name and the matched content? If so, the following will work for you:
$date = (Get-Date).AddHours(-4)
Get-ChildItem -Path 'E:\SDKLogs\Logs' | Where-Object -FilterScript { $date -lt $_.LastWriteTime } |
Select-String -Pattern 'Found = 60.' |
ForEach-Object -Process {
'{0} {1}' -f $_.FileName, $_.Matches.Value
}
Get-Date doesn't need to be in a variable before your call but, it can become computationally expensive running a call to it again and again. Rather, just place it in a variable before your expression and call on the already created value of $date.
Typically, and for best practice, you always want to filter as far left as possible in your command. In this case we swap your if statement for a Where-Object to filter as the objects are passed down the pipeline. Luckily for us, Select-String returns the file name of a match found, and the matched content so we just reference it in our Foreach-Object loop; could also use a calculated property instead.
As for your quoting issues, you may have to double quote or escape the quotes within the PowerShell.exe call for it to run properly.
Edit: swapped the double quotes for single quotes so you can wrap the entire expression in just PowerShell.exe -Command "expression here" without the need of escaping; this works if you're pattern to find doesn't contain single quotes.

I need my script to accept input from a file rather than read-host

I have a ps script which will ask for a number, then search for that number in a location with 1000s of files, copy those file names having those number and then output it to a file. That number is also saved in a txt file in a different location, from which I manually copy and insert into this script. Is it possible to make the script read from the 2nd line onwards of the file containing the number, then search for that number within files, like it is doing now?
This is the code I am using:-
$Path = "D:\Projects\MSMQ Journal Messages\PurchaseManagementPO"
$Text = Read-Host -Prompt "PO Number"
$PathArray = #()
$Results = "D:\Chayan\POmiss\miss.txt"
# This code snippet gets all the files in $Path that end in ".xml".
Get-ChildItem $Path -Filter "*.xml" |
Where-Object { $_.Attributes -ne "Directory"} |
ForEach-Object {
If (Get-Content $_.FullName | Select-String -Pattern $Text)
{
$PathArray += $_.FullName
$PathArray += $_.FullName
}
}
Write-Host "Contents of ArrayPath:"
$PathArray | % {$_} | Out-File "D:\Chayan\POmiss\miss.txt" -Append
That PO Number comes from a file, which is generated through a different script, and gets saved like below:-
ponumMaster
908859
280973
I manually put these number in the read-host to do the search and save file name. Is there a way powershell can copy these numbers from this file and do the task?
You should be able to use -skip to move past the first line
The example below would skip the first line and give the results after that
get-content C:\_lab\test.txt | select -skip 1
This example would skip the first line and only give the results from the second line
get-content C:\_lab\test.txt | select -first 1 -skip 1
For your script, you should just need to do the following:
$Text = get-content C:\_lab\test.txt | select -skip 1
#we clear this variable so it can be run multiple times in the same session
clear-variable final -ErrorAction Ignore
#grab txt file content and split into an array
[array]$txt=(get-content "D:\Chayan\POmiss\miss.txt") -split " "
#take out the blanks and assign to new variable called final (we clear this above so it can be run multiple times in the same session)
foreach($line in $txt){
if($line.replace(" ","")){
[array]$final+=$line
}
}
#run script, calling the variable $text in place of the numbers
foreach($text in $final){
(your normal script here)
}

Powershell comparing values / then checking the validity

I have been trying numerous functions in Powershell including Comparison Object, for each function but with no success.
This is what I want to do.
I have two files named as File 1 and File 2. Within File 1, I have a line like this.
In File 2, I have this.
So I want to compare the value of ContractGroups from File 1 to the value of ContractGroups in file 2. If matched, I want to add an outcome to the file.
The difficulty I am having is that I can set up a variable to get the content for matching. But when comparing, I want to only take into account of ContractGroups for a match. So as soon as file 2 is scanned, then I want to see if the value of ContractGroups matches to file 1.
I have tried this.
$file1 = Get-Content "C:\Users\Altunokc\Desktop\Guardian\NotUsed\20200513 Environment parameters\EnvironmentParameters_ICE_EU_PROD.xml"
$file2 = Get-Content "C:\Users\Altunokc\Desktop\inetpub\wwwroot\EU\Guardian\Website\appSettings.config"
$pattern = $file1| %{$_ -MATCH '.Name="ContractGroups" Value'}
$result = "C:\Users\Altunokc\Desktop\Differentdd.txt"
$file1 | foreach { $pattern = $file2 -match $_
if ( $match ) { $match | Out-File -Force $result -Append }
}
and this way
if(Compare-Object -ReferenceObject $(Get-Content $file2 ) -DifferenceObject $(Get-Content $file1 ) | %{$_ -MATCH '.Name="ContractGroups" Value'} -SimpleMatch|
Out-File -FilePath C:\Users\Altunokc\Desktop\Different.txt)
{"match"}
Else {"No "}
No luck.
I think for comparison, I just need to find a way of scanning the line that contains Name="ContractGroups" Value from file 1 and then reading its value (CC) and then scanning file 2 to first see if has a line matches Name="ContractGroups" Value and if yes, and then does the value of it is the same (CC) as file one? If yes, then just write that "Matched" to a blank file.
It would be helpful if you shed light on this. Sorry as I am new on PS.

Delete lines from multiple textfiles in PowerShell

I am trying to delete lines with a defined content from multiple textfiles.
It works in the core, but it will rewrite every file even if no changes are made, which is not cool if you are just modifying 50 out of about 3000 logonscripts.
I even made a if statement but it seems like it doesn't work.
Alright this is what I already have:
#Here $varFind will be escaped from potential RegEx triggers.
$varFindEscaped = [regex]::Escape($varFind)
#Here the deletion happens.
foreach ($file in Get-ChildItem $varPath*$varEnding) {
$contentBefore = Get-Content $file
$contentAfter = Get-Content $file | Where-Object {$_ -notmatch $varFindEscaped}
if ($contentBefore -ne $contentAfter) {Set-Content $file $contentAfter}
}
What the variables mean:
$varPath is the path in which the logonscripts are.
$varEnding is the file ending of the files to modify.
$varFind is the string that triggers the deletion of the line.
Any help would be highly appreciated.
Greetings
Löwä Cent
You have to read the file regardless but some improvement on your change condition could help.
#Here the deletion happens.
foreach ($file in Get-ChildItem $varPath*$varEnding) {
$data = (Get-Content $file)
If($data -match $varFindEscaped){
$data | Where-Object {$_ -notmatch $varFindEscaped} | Set-Content $file
}
}
Read the file into $data. Check to see if the pattern $varFindEscaped is present in the file. If it is than filter out those matching the same pattern. Else we move onto the next file.

Powershell Find Replace & Backup

Right now, i am looking to improve my code to use less space and be more intelligent, i need the below code to only backup files IF it's modified by the Find & Replace, right now i'm doing a backup of everything and overwriting old back-ups.
The next thing i would like would be to NOT overwrite the backups, but instead give them a number, so if there are 2 of the same backups in the "backup" folder it would look like this:
Filebackup.DCN3 -> Filebackup1.DCN3
So i always have the original file.
get-childitem -path "C:\Users\Administrator\Desktop\Eurocard\SEB" -filter *.* -recurse | copy-item -destination "C:\Users\Administrator\Desktop\Eurocard\Backup"
(Get-ChildItem "C:\Users\Administrator\Desktop\Eurocard\SEB\*.*" -recurse).FullName |
Foreach-Object {
(Get-Content $_ -Raw).
Replace('*','Æ').
Replace('"','Æ').
Replace('#','Æ').
Replace('¤','Æ').
Replace('&','Æ').
Replace('(','Æ').
Replace(')','Æ').
Replace('=','Æ').
Replace('?','Æ').
Replace('´','Æ').
Replace('`','Æ').
Replace('|','Æ').
Replace('#','Æ').
Replace('£','Æ').
Replace('$','Æ').
Replace('{','Æ').
Replace('[','Æ').
Replace(']','Æ').
Replace('}','Æ').
Replace('^','Æ').
Replace('~','Æ').
Replace('¨','Æ').
Replace('*','Æ').
Replace('<','Æ').
Replace('>','Æ').
Replace('\','Æ').
Replace('_','Æ').
Replace(';','Æ').
Replace('.','Æ').
Replace('!','Æ')|
Set-Content $_
}
Is there anyone who can help with this ?
Well, to start a large portion of your regex replaces probably aren't working, you need to escape most of them...for example "\". Anyways you can shorten up the whole replace to one expression like this:
-replace '[*"#¤&()=?´`|#£${\[\]}^~¨*<>\\_;.!]','Æ'
#query to show it working
'*"#¤&()=?´`|#£${[]}^~¨*<>\_;.!' -replace '[*"#¤&()=?´`|#£${\[\]}^~¨*<>\\_;.!]','Æ'
expanding on that here is how you would get it to only backup if you modify the file:
(Get-ChildItem "C:\Users\Administrator\Desktop\Eurocard\SEB\*.*" -recurse).FullName |
Foreach-Object {
$Content = (Get-Content $_ -Raw)
$Regex = '[*"#¤&()=?´`|#£${\[\]}^~¨*<>\\_;.!]'
If ($Content | Select-String $Regex -Quiet)
{
$Content -Replace $Regex,'Æ'
<#
rest of code block such as copies, backups, renames whatever would go here.
This way it is only taking place if the file has an unwanted character and is
modified
#>
}
}