Powershell to replace text in multiple files stored in many folders - file

I want to replace a text in multiple files and folders. The folder name changes, but the filename is always config.xml.
$fileName = Get-ChildItem "C:\config\app*\config.xml" -Recurse
(Get-Content $fileName) -replace 'this', 'that' | Set-Content $fileName
When I run the above script it works, but it writes the whole text in config.xml about 20 times. What's wrong?

$filename is a collection of System.IO.FileInfo objects.
You have to loop to get the content for each file :
this should do what you want :
$filename | %{
(gc $_) -replace "THIS","THAT" |Set-Content $_.fullname
}

In general, you should use the pipeline and combine the ForEach-Object and/or Where-Object CmdLets.
In your case, this would like like something more akin to:
Get-ChildItem "C:\config\app*\config.xml" -Recurse | ForEach-Object -Process {
(Get-Content $_) -Replace 'this', 'that' | Set-Content $_
}
Which can be shortened somewhat to:
dir "C:\config\app*\config.xml" -recurse |% { (gc $_) -replace 'this', 'that' | (sc $_) }

$filename is an array of filenames, and it's trying to do them all at once. Try doing them one at a time:
$fileNames = Get-ChildItem "C:\config\app*\config.xml" -Recurse |
select -expand fullname
foreach ($filename in $filenames)
{
( Get-Content $fileName) -replace 'this', 'that' | Set-Content $fileName
}

I got list of files to replace text this way.
$filenames = Get-ChildItem|Select-String -Pattern
""|select Filename
This gets 12 files.
To replace this text in all files
foreach ($filename in $filesnames){ (Get-Content $filename.Filename) -replace "", ""|Set-Content $filename.Filename }
Don't forget last part for Filename. $filename.Filename

Related

Using two arrays to create registry keys/values

Trying to automate our font installation process for new PCs.
To install fonts, Windows adds the .ttf, .otf, etc. file to C:\Windows\Fonts and then creates a corresponding registry key in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts. A typical registry key would look like this:
Arial (TrueType) | Arial.ttf
To automate this, I've made two arrays using Get-ChildItem:
$names = Get-ChildItem -Path "C:\corp\install\fonts" | Select-Object name | Out-String | ForEach-Object {$_ -Replace "----","" ` -Replace "Name","" ` -Replace ".otf","" ` -Replace ".ttf","" } | ForEach-Object { $_.Trim() }
$files = Get-ChildItem -Path "C:\corp\install\fonts" | Select-Object name | Out-String | ForEach-Object {$_ -Replace "----","" ` -Replace "Name","" } | ForEach-Object { $_.Trim() }
Each $name in $names will be the name of the registry key, and each $file in $files will be the data for that registry key.
How would I go about doing this? I've attempted to use hash tables, PSObjects, nested ForEach loops, all to no avail. I have had difficulty finding anything on here and elsewhere that matches this situation exactly.
Error checking is not really necessary since there will always be a corresponding value.
REVISED FINAL SOLUTION:
Write-Host "Installing corporate fonts..."
Copy-Item -Path "C:\corp\install\fonts\*" -Destination "C:\Windows\Fonts" -Force -Recurse
$fontList = #()
$fonts = Get-ChildItem "C:\corp\install\fonts" | Select-Object -ExpandProperty Name
ForEach ( $font in $fonts ) {
$fontList += [PSCustomObject] #{
Name = $font -Replace ".otf","" ` -Replace ".ttf",""
File = $font
} |
ForEach-Object {
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" -Name $_.Name -Value $_.File
}
}
I must admit I don't fully understand your question so forgive me if this response is way off base, but it this what you're looking for? A table with both pieces of data in one?
Function CreateVariables {
$namevariables = #()
$filenames = ( Get-ChildItem "C:\corp\install\fonts" ).name
Foreach ( $name in $filenames ){
$namevariables += [PSCustomObject] #{
Name = $name -Replace "----","" ` -Replace "Name","" ` -Replace ".otf","" ` -Replace ".ttf",""
File = $name -Replace "----","" ` -Replace "Name",""
}
}
Return $namevariables
}
CreateVariables
Piping both name and value to set-itemproperty seem impossible. Foreach-object seems the way to go.
$path = 'hklm:\software\microsoft\windows nt\currentversion\fonts'
[pscustomobject]#{name='a name';value='a value'} |
foreach { set-itemproperty $path $_.name $_.value -whatif }
What if: Performing the operation "Set Property" on target "Item: HKEY_LOCAL_MACHINE\software\microsoft\windows nt\currentversion\fonts Property: a name".
You may prefer using this vbscript-like method to install fonts:
https://www.mondaiji.com/blog/other/it/10247-windows-install-fonts-via-command-line

Powershell 1.0 - loop to replace text in aspx file will not work

I have the loop:
Get-ChildItem 'C:\inetpub\wwwroot\*\login.aspx' | ForEach {
(Get-Content $_.FullName) | ForEach {
$_ -replace "<title>Welcome to Synergy!</title>","<title>Welcome!</title>"
} | Set-Content $_.FullName
}
And it does nothing to the subdirectories of wwwroot?
I answered my own question. I wasn't putting in the full path of the Get-Childitem argument

Powershell log deleted files

The script searches all folders and subfolders and delete the oldest file when the number of files is>5. Everything works fine, but I want also log all the delete Files as a record in a log-file.
How can I log the deleted files ?
Here the Script.
$path = "C:\test\1"
$keep = 3
$strLogFileName = "c:\test\yourlogfile.log";
$dirs = Get-ChildItem -Path $path -Recurse | Where-Object {$_.PsIsContainer}
foreach ($dir in $dirs) {
$files = Get-ChildItem -Path $dir.FullName | Where-Object {-not $_.PsIsContainer -and $_.name -like "*.zip"}
if ($files.Count -gt $keep) {
$files | Sort-Object CreationTime -desc| Select-Object -First ($files.Count - $keep) | Remove-Item -Force
***{write-host “Deleting File $File” -foregroundcolor “Red”; Remove-Item $File | out-null}***
}
}
First you will need a log-message type function in your script that will log the message to a .log file. Then chekc if the file exists and if not then create a file.
Then just before you delete your file with Remove-Item command you can use Log-Message function to log message to the log file.
% { (Log-Message "Deleting File $_"); $_ }
Complete script
$path = "C:\test\1"
$keep = 3
$strLogFileName = "c:\test\yourlogfile.log";
function Log-Message
{
Param ([string]$logtext)
Add-content $strLogFileName -value $logtext
}
$dirs = Get-ChildItem -Path $path -Recurse | Where-Object {$_.PsIsContainer}
foreach ($dir in $dirs) {
$files = Get-ChildItem -Path $dir.FullName | Where-Object {-not $_.PsIsContainer -and $_.name -like "*.zip"}
if ($files.Count -gt $keep) {
$files | Sort-Object CreationTime -desc| Select-Object -First ($files.Count - $keep) |
% { $dt=get-date;(Log-Message "Deleting File $_ on $dt");$_ }| Remove-Item -Force
}
}
You've got a good start here:
write-host “Deleting File $File” -foregroundcolor “Red”
Unfortunately Remove-Item doesn't have any output that you can mooch from, but you've already made your own output message so we can just build from that. You can pipe any output to a file by using Out-File. The append flag will attach the new content to the end of the file, and you do not have to check if the file exists.
Write-Output “Deleting File $File” | Out-File -Append logfile.txt
You don't even have to include Write-Output, if you want a shorter line.
Here is an example that shows where you need to add code. I've marked existing code with "...", and I've moved the deletion message into a variable so that you can reuse it at another location. This assumes that you've stored the selected filename in a variable.
...
if ($files.Count -gt $keep)
{
...
$message = "Deleting File $File at "+(Get-Date)
$message | Out-File -Append logfile.txt
}
...

Comma delimited input for a powershell function

I currently have a function that I made that gets a list of all files in a directory and then adds them to an array called $FileListArray.
I want to add an option that lets me specify the file extensions to exclude from the arrary so that I could call the function as follows ListFiles -FilesToList "c:\test" –exclude “avi,txt,bmp” and this would then ignore files with any of the file extensions I have put in.
My Function so far
Function ListFiles($FilesToList){
$FileListArray = #()
Foreach($file in Get-ChildItem $FilesToList -Force -Recurse | Where-Object {$_.attributes -notlike "Directory"})
{
$FileListArray += ,#($file.name,$file.fullname,$File.Extension)
}
}
Listfiles -FilesToList "c:\tools"
Try this:
Function ListFiles($Path,$Exclude)
{
Get-ChildItem -Path $FilesToList -Force -Recurse |
Where-Object {!$_.PSIsContainer -and $_.Extension -notmatch ($Exclude -join '|') }
}
Listfiles -Pathc:\tools –Exclude avi,txt,bmp

powershell getting the remote directory name and filename to put in destination filename

I have this bit of powershell script but i can't get the $DirectoryName to behave as expected.
1,2,3 |
foreach {
$count = $_;
$x = gci -Path \\myserver-web$count\d$\IISLogs\ -include *.log -recurse
$x | Copy-Item -Destination D:\ServerLogsAndBackups\IIS\w$count\$_.DirectoryName_$_.Name -whatIf
}
When I run this though I get
What if: Performing operation "Copy File" on Target "Item: \myserver-web1\d$\IISLogs\W3SVC1165836668\ex101224.log Destination: D:\ServerLogsAndBackups\IIS\w1\1.DirectoryName_1.Name".
What I want it to be is
W3SVC1165836668_ex101206.log
where my directory structure is like:
\\myserver-web1\d$\IISLogs\W3SVC1165836668
\\myserver-web1\d$\IISLogs\W3SVC1165837451
\\myserver-web1\d$\IISLogs\W3SVC1165836966
\\myserver-web1\d$\IISLogs\W3SVC1165812365
with files called ex101206.log in each folder
Cheers
You need to evaluate the $_.Directoryname_$_.Name part. Like so,
$x | Copy-Item -Destination $(D:\ServerLogsAndBackups\IIS\w$count\$_.DirectoryName_$_.Name) -whatIf
1,2,3 |
foreach {
$count = $_;
gci -Path \\myserver-web$count\d$\IISLogs\ -include *.log -recurse | % { $dirName = $_.directoryname.Substring($_.directoryname.LastIndexOf("\")+1); $logname = $_.Name; $_ | Copy-Item -Destination $("D:\ServerLogsAndBackups\IIS\w"+$count+"\"+$dirname+"_"+$logName) -whatif }
}

Resources