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
)}
Related
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.
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.
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
There are a lot of directories and .conf/.xml files I have to open and search through. I have this right now:
$Path = "D:\Logs"
$Text = "*\log"
$PathArray = #()
$Results = "D:\Logs\Search.txt"
Get-Childitem $Path -Filter "*.conf" -Recurse |
Where-Object {$_.Attributes -ne "Directory"} |
ForEach-Object
{
If (Get-Content $_.FullName | Select-String -Pattern $Text -AllMatches)
{
$PathArray += $_.FullName
$PathArray += $_.FullName
}
}
Write-Host "Contents of ArrayPath:"
$PathArray | ForEach-Object {$_}
$PathArray | % {$_} | Out-File “D:\Logs\Search.txt”
I am trying to create this script, so I can have the out-file report all of the .conf files in a txt file with the correct path the .conf files are located in. I will also do the same with the .xml files by simply replacing the .conf with .xml. As of now, I am getting the .txt file up, but no paths. I know I am missing one or two things but I can not figure out what it is. I will have to manually change the old paths with the news ones I have already created. I would like to run this script to search for all the .conf/.xml files with a *\log or *\logs in them.
There's an issue with your regex not escaping backslash and it apparently not matching what you've shown as the typically content of a .conf file. Plus it can be simplified. Try this - adjusting the $Text regex to actually match the desired text in the .conf files:
$Path = "D:\Logs"
$Text = "\\log"
$Results = "D:\Logs\Search.txt"
$PathArray = #(Get-Childitem $Path -Filter *.conf -Recurse -File |
Where {(Get-Content $_.FullName -Raw) -match $Text})
Write-Host "Contents of ArrayPath:"
$PathArray
$PathArray | Out-File $Results -Encoding ascii
There were a couple of issues. Keith got the biggest one being that Select-String uses regex by default. Again, you had some redundancies like adding to the $pathArray twice and the use of ForEach-Object {$_}.
I wanted to show a solution that still uses select-string but uses some of the switch therein to get the use you want out of it. Main one being -simplematch which treats the pattern literally and not as regex. I saw an underscore leading the log in your sample text so I use that here. If you don't want to or it does not match your data simply remove it.
$Path = "D:\Logs"
$Text = "_log"
$Results = "D:\Logs\Search.txt"
$PathArray = Get-Childitem $Path -Filter "*.conf" -Recurse |
Where-Object {$_.Attributes -ne "Directory"} |
Where-Object{Select-string $_ -Pattern $Text -SimpleMatch -Quiet} |
Select-Object -ExpandProperty FullName
# Show results on screen
$PathArray
# Export results to file
$PathArray | Set-Content $Results
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.