Doing a variable substitution and saving the files - powershell

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.

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.

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
)}

Array output to multiple lines

Get-ChildItem "$Folder" *.xlsx -Recurse | ?{-not ($_.PSIsContainer -or (Test-Path "I:\TEMP_Dir_SSN\$_"))} | copy-Item -Destination "I:TEMP_Dir_SSN" | out-null
Get-ChildItem "$Folder" *.xlsx -Recurse | %{
$test = Resolve-Path $_.FullName
$holdArray += $test.path
}
$holdArray | out-file "I:\TEMP_Dir_SSN\fullPath.txt" -append
The output:
I:\1992.xlsxI:\projects\confluence\projects\documents\XXXX_ComplianceRegulations.xlsxI:\projects\confluence\projects\documents\XXXX_vendorCloudStandardsPoliciesRegs.xlsx
I want it:
I:\1992.xlsx
I:\projects\confluence\projects\documents\XXXX_ComplianceRegulations.xlsx
I:\projects\confluence\projects\documents\XXXX_vendorCloudStandardsPoliciesRegs.xlsx
I'm not sure what is going wrong here. It should be adding it to the next line down, not appending it to the end of the string.
Change $holdArray += $test.path to [array]$holdArray += $test.path
You have not told PowerShell this is an array so it is treating it as a string.
You are flattening the "array" to a space delimited string since you have not declared $holdArray initially. Skip the array "build" logic and use the pipeline to get the results you want.
Get-ChildItem $Folder *.xlsx -Recurse |
Resolve-Path | Convert-Path |
Add-Content "I:\TEMP_Dir_SSN\fullPath.txt"
Add-Content appends by default.

How can I look for file paths within .conf files?

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

How do I recursively rename folders with Powershell?

Recursive renaming files using PS is trivial (variation on example from Mike Ormond's blog):
dir *_t*.gif -recurse
| foreach { move-item -literal $_ $_.Name.Replace("_thumb[1]", "")}
I'm trying to recursively rename a folder structure.
The use case is I'd like to be able to rename a whole VS.NET Solution (e.g. from Foo.Bar to Bar.Foo). To do this there are several steps:
Rename folders (e.g. \Foo.Bar\Foo.Bar.Model => \Bar.Foo\Bar.Foo.Model)
Rename files (e.g. Foo.Bar.Model.csproj => Bar.Foo.Model.csproj)
Find and Replace within files to correct for namespace changes (e.g. 'namespace Foo.Bar' => 'namespace Bar.Foo')
I'm currently working the first step in this process.
I found this posting, which talks about the challenges, and claims a solution but doesn't talk about what that solution is.
I keep running into the recursion wall. If I let PS deal with the recursion using a flag, the parent folder gets renamed before the children, and the script throws an error. If I try to implement the recursion myself, my head get's all achy and things go horribly wrong - for the life of me I cannot get things to start their renames at the tail of the recursion tree.
Here's the solution rbellamy ended up with:
Get-ChildItem $Path -Recurse | %{$_.FullName} |
Sort-Object -Property Length -Descending |
% {
Write-Host $_
$Item = Get-Item $_
$PathRoot = $Item.FullName | Split-Path
$OldName = $Item.FullName | Split-Path -Leaf
$NewName = $OldName -replace $OldText, $NewText
$NewPath = $PathRoot | Join-Path -ChildPath $NewName
if (!$Item.PSIsContainer -and $Extension -contains $Item.Extension) {
(Get-Content $Item) | % {
#Write-Host $_
$_ -replace $OldText, $NewText
} | Set-Content $Item
}
if ($OldName.Contains($OldText)) {
Rename-Item -Path $Item.FullName -NewName $NewPath
}
}
How about this - do a recursive list of the full names, sort it in descending order by the length of the full name, and then run that back through your rename routine.
e.g.
gci <directory> -recurse |
foreach {$_.fullname} |
sort -length -desc
Maybe something in this is useful, here's a snippet that recurses and prepends "pre" to a directory structure
$dirs = Get-ChildItem c:/foldertorecurse -rec | Where-Object {$_.PSIsContainer -eq 1} | sort fullname -descending
foreach ( $dir in $dirs ) { rename-item -path $dir.fullname -newname ("pre" + $dir.name) }