How do I find & replace file contents in PowerShell? - powershell

I need to find text in a file, like:
PolicyFile=$(SrcRoot)BeHttp
and replace it with this:
PolicyFile=$(SrcRoot)PPCore/BeHttp
So I wrote following script but it's not working.
Get-ChildItem 'D:\SomeFolder\\*.MKE' -Recurse | ForEach {
(Get-Content $_ | ForEach {
$_ -replace "PolicyFile=$(SrcRoot)BeHttp", "PolicyFile=$(SrcRoot)PPCore//BeHttp"
}) | Set-Content $_
}

You want to escape the characters powershell considers reserved. Also, when using Get-Content, you need to provide full path. That path is available under FullName of the Child Item (Get-ChildItem).
Get-ChildItem 'D:\SomeFolder\*.MKE' -Recurse | ForEach {
(Get-Content $_.FullName) -replace 'PolicyFile=\$\(SrcRoot\)BeHttp', 'PolicyFile=$(SrcRoot)PPCore//BeHttp' | Set-Content $_.FullName }
To Escape $ ( ), use \. Also, you dont need to use For-Each on string obtained from Get-Content.
Update:
When running Get-ChildItem, i do see all the files from all subfolders.
PS C:\Users\user> Get-ChildItem 'C:\Temp\*.MKE' -Recurse | % { $_.FullName}
C:\Temp\1\new.mke
C:\Temp\2\3\new.mke
C:\Temp\2\new.mke
C:\Temp\new.mke

Related

Powershell script to locate only files starting with specified letters and ending with .csv

cd 'A:\P\E\D'
$files = Get-ChildItem . *.CSV -rec
ForEach ($file in $files) {
(Get-Content $file -Raw) | ForEach-Object {
*some simple code*
} | Set-Content $file
}
How to modify this powershell script to locate only files starting with letters A/a to O/o and ending with .csv in specified directory cd?
I thought the solution below would work, but the test file M_K_O_X.CSV stored in the cd directory was not found and modified. The solution above will find and modify the file. It's possible that I have the regex expression wrong or the problem is somewhere else? I tried also this regex -- "[A-O]..CSV"
cd 'A:\P\E\D'
$files = Get-ChildItem . -rec | Where-Object { $_.Name -like "[a-oA-O]*.*.CSV" }
ForEach ($file in $files) {
(Get-Content $file -Raw) | ForEach-Object {
*some simple code*
} | Set-Content $file
}
Looking at your wildcard pattern, seems like you have an extra *. that shouldn't be there:
'M_K_O_X.CSV' -like '[a-oA-O]*.*.CSV' # False
'M_K_O_X.CSV' -like '[a-oA-O]*.CSV' # True
In this case you could simply use the -Include Parameter which supports character ranges. Also PowerShell is case insensitive by default, [a-oA-O]*.CSV can be reduced to [a-o]*.CSV:
Get-ChildItem 'A:\P\E\D' -Recurse -Include '[a-o]*.csv' | ForEach-Object {
($_ | Get-Content -Raw) | ForEach-Object {
# *some simple code*
} | Set-Content -LiteralPath $_.FullName
}
As commented, I would use the standard wildcard -Filter to filter for all files with a .csv extension.
Then pipe to a Where-Object clause in which you can use regex -match
$files = Get-ChildItem -Path 'A:\P\E\D' -Filter '*.csv' -File -Recurse |
Where-Object { $_.Name -match '^[a-o]' }
foreach ($file in $files) {
# switch `-Raw` makes Get-Content return a single multiline string, so no need for a loop
$content = Get-Content -Path $file.FullName -Raw
# *some simple code manipulating $content*
$content | Set-Content -Path $file.FullName
}
However, if these are valid csv files, I would not recommend using a pure textual manipulation on them, instead use Import-Csv -Path $file.FullName and work on the properties on each of the objects returned.

Replacing characters from multiple files with regex and Powershell

I would like to change _ to - in all .md files from folder FOO. The code below does what I need but I don't how to save results in folder FOO or some other...
$mdfiles = gci *.md
gc $mdfiles | ForEach-Object {if ( $_ -match '^!') {$_ -replace '_', '-'} else {$_}} | out-file ...
A ForEach-Object is needed to iterate over the file names as well. Ternary expressions are in PowerShell Core, but I am not sure about Windows PowerShell. This is not tested, but might give a start.
Get-ChildItem -File -Path '.' -Filter '*.md' |
ForEach-Object {
$OutFile = ".\foo\$($_.Name)"
Get-Content -Path $_.FullName |
ForEach-Object { ($_ -match '^!') ? ($_ -replace '_','-') : ($_) } |
Out-File -FilePath $OutFile
}
Also, it is bad practice to use alias commands in a stored script.

Powershell command to prepend text before each file

I am trying to build a PowerShell command to extract all files from multiple folder and combine into a single file.
Script:
Get-ChildItem -Path $(Pipeline.Workspace)/Common_All/Drop_$(DropFolder)_Migrations -Filter "Release*"
-Directory | Get-ChildItem -File -Filter *.sql | ForEach-Object { $_ |Get-Content;
"GO" } | out-file $(System.DefaultWorkingDirectory)/combined-script.sql
This returns combined script with "GO" appended after each file content
How do I prepend a text before each file content?
ForEach-Object { "prepended text`n" + ($_ | Get-Content -raw) + "`nGO" }
It may also be done same way as you've appended GO.
ForEach-Object {"prepended text"; $_ |Get-Content; "GO" }

Exclude all sub-directories from PowerShell Get-ChildItem -match

Goal
Exclude all sub-directories when running a PowerShell script that matches a filename regex pattern
Directory structure
/
- 2018-11-19.md
18-2/
- 2018-10-16.md
- 2019-01-14.md
- 2019-10-10.md
18-3/
- 2019-01-13.md
- 2019-04-25.md
PowerShell script
$file = '2018-11-19.md'
Get-ChildItem -recurse | where-object { $_.FullName -match '[0-9]{4}-[0-9]{2}-[0-9]{2}.md' } |
ForEach-Object {$fullname = $_.fullname; (Get-Content $_.fullname | foreach-object {
$_ -replace "apple", "orange"
}) | Set-Content $fullname}
(Get-Content $file | ForEach-Object {
$_ -replace '<p(.*?)>(.*?)</p>', '$2'
}) | Set-Content -Encoding Utf8 $file
Get-ChildItem -recurse | where-object { $_.FullName -match '[0-9]{4}-[0-9]{2}-[0-9]{2}.md' } |
foreach-Object {$fullname2 = $_.fullname; (Get-Content $_.fullname |
pandoc -f markdown -t markdown -o $fullname2 $fullname2
)}
Details
The goal is to run the PowerShell script on only file(s) in the root directory. These file(s) at root will change but always be named according to the convention shown. The regex in the PowerShell script successfully matches this filename.
Currently the script changes all files in the directory example above.
Any examples I can find show how to exclude specific directories by identifying their names in the script (e.g., -Exclude folder-name). I want to exclude all sub-directories without naming them specifically because...
...In the future sub-directories may be added for 18-4, 19-5, etc., so it seems like an exclusion based on a regex would make sense.
Attempts
To limit the script's scope to the root directory, I tried variations on -notmatch with \, \*, \*.*, \\*, etc., without success.
To exclude sub-directories, I tried variations on -Exclude with the same paths but did not succeed.
My PowerShell knowledge is not advanced enough to get further than this. I would be grateful for any help or to be pointed in the right direction. Thank you for any help.
As pointed out by Owain and gvee in the comments, when using the -Recurse switch, you tell the Get-ChildItem cmdlet that you wish to traverse through the sub directory structure from the selected location. As expained on the docs site of the cmdlet
Gets the items in the specified locations and in all child items of the locations.
So simply removing the switch should make the code do as you want.
If you ever want only X level of sub directories you can use -Depth switch.
Get-ChildItem | where-object { $_.FullName -match '[0-9]{4}-[0-9]{2}-[0-9]{2}.md' } |
ForEach-Object {$fullname = $_.fullname; (Get-Content $_.fullname | foreach-object {
$_ -replace "apple", "orange"
}) | Set-Content $fullname}
(Get-Content $file | ForEach-Object {
$_ -replace '<p(.*?)>(.*?)</p>', '$2'
}) | Set-Content -Encoding Utf8 $file
Get-ChildItem | where-object { $_.FullName -match '[0-9]{4}-[0-9]{2}-[0-9]{2}.md' } |
foreach-Object {$fullname2 = $_.fullname; (Get-Content $_.fullname |
pandoc -f markdown -t markdown -o $fullname2 $fullname2
)}

Doing a variable substitution and saving the files

I want it to go though some folders recursivly, find all files with certain endings, and then go through found files to do the subsitute and then save. I can make it work when I have a given filename, but I seem to be haveing some trouble when it comes to the unknown.
The thought is to read the variables for a given environment from a file and save it into PowerShell as variables (this part seems to work, so I've not mentioned it again), then simply substitute the mentioned variables in a set of unknown files.
I've been looking at this question, which seemed to have something of a similar problem, except that I'm trying to use a variable substitusion. Also looked at some previous answer for replace, but again, this is not replace, but variable substitution, so it seems to work a bit differently. So with that in mind, this is what I got so far:
After looking at the examples in the first link:
Get-ChildItem -Path .\ -Include *.ini,*.exe.config -Recurse | %{ gc $_ } | %{
$ExecutionContext.InvokeCommand.ExpandString($_) +
(Get-Content $_.FullName | Out-String ) | Set-Content -Path $_.FullName
}
After trying to do something with the second link:
$places = 'C:\Users\Roger\Documents\test\Program Files (x86)'
$places |
Get-ChildItem -Recurse -Include *.ini,*.config |
ForEach-Object {
(Get-Content $_) | % {
$ExecutionContext.InvokeCommand.ExpandString($_) |
Set-Content $_
'Processed: {0}' -f $_.FullName
}
}
And of course my own feeble attempts:
Get-ChildItem .\ -Include *.ini,*.exe.config -Recurse | %{ gc $_ } | %{
$ExecutionContext.InvokeCommand.ExpandString($_)
} | Set-Content $_.FullName
$places = 'C:\temp'
$places |
Get-ChildItem -Recurse -Include *.ini,*.config | ForEach-Object {
(Get-Content $_) | ForEach-Object {
$ExecutionContext.InvokeCommand.ExpandString($_)
} | Set-Content $_
'Processed: {0}' -f $_.FullName
}
Move the Set-Content out of the inside ForEach-Object. Set-Content would have been trying to fire for each line using the current line as its name as you have seen. Also, for yours and others sanity, you should try and be consistent with using aliases or not. I see both ForEach-Object and % which could be confusing to new users.