Comma delimited input for a powershell function - arrays

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

Related

Interpreting an extension with a wildcard IN an array

Why does this work:
$VideoExtensions = #('.mp4','.mov','.mpg','.mts','.3g2','.3gp','.avi','.mkv','.flv','.wmv')
$Files = Get-ChildItem -LiteralPath $PSScriptRoot -Recurse | Where-Object {
$_.Extension -match '.jp*' -or
$_.Extension -in $VideoExtensions
}
But not this:
$PicExtensions = #('.jp*','.png')
$VideoExtensions = #('.mp4','.mov','.mpg','.mts','.3g2','.3gp','.avi','.mkv','.flv','.wmv')
$Files = Get-ChildItem -LiteralPath $PSScriptRoot -Recurse | Where-Object {
$_.Extension -in $PicExtensions -or
$_.Extension -in $VideoExtensions
}
The .jp* wildcard is completely ignored. If I used -like will it catch files that are not exactly .png? I'm iffy about using -like operators.
I would strongly recommend using regular expressions instead of lists of strings for something like this.
$PicExtensions = 'jp.*|png'
$VideoExtensions = 'mp4|mov|mpg|mts|3g2|3gp|avi|mkv|flv|wmv'
$Files = Get-ChildItem -LiteralPath $PSScriptRoot -Recurse | Where-Object {
$_.Extension -match "^\.(${PicExtensions})$" -or
$_.Extension -match "^\.(${VideoExtensions})$"
}
If you wanted to continue using lists of strings (not recommended) you'd have to compare each element individually (which is bound to perform very poorly), because the -contains and -in operators allow only exact matches whereas you want to do wildcard matches.
$Files = Get-ChildItem -LiteralPath $PSScriptRoot -Recurse | Where-Object {
$ext = $_.Extension
($PicExtensions | Where-Object {$_ -like $ext}) -or
($VideoExtensions | Where-Object {$_ -like $ext})
}
You could just specify multiple patterns in the path, if it didn't have to be literal.
$pic = echo *.jp* *.png
$vid = echo *.mp4 *.mov *.mpg *.mts *.3g2 *.3gp *.avi *.mkv *.flv *.wmv
get-childitem -path ($pic + $vid) -recurse

wildcard in array

How can I use the 1709 as a wildcard? The value 1709 is stored in an array as $MoveItem.Version, but I can't figure out how do a -like, when the value comes from an array, as I can't put in a wildcard *. I also tried to do a match.
The file name looks like this: REFW10-X86-1709_01-12-2017.wim.
The below code works fine, but I would like to automate it, so everything comes from the array. Is that possible?
Get-ChildItem -Path $OSPathTemp -Recurse | Where {
($_.Name -eq $MoveItem.File) -and
($_.Name -like "*1709*")
} | Select-Object -ExpandProperty FullName
$MoveItem.Version contains 1607,1706,1709. I would like to choose only the one with 1709. The final output should look like this:
foreach ($MoveItem in $MoveItems) {
Get-ChildItem -Path $OSPathTemp -Recurse | Where {
($_.Name -eq $MoveItem.File) -and
($_.Name -like $MoveItem.Version)
} | Select-Object -ExpandProperty FullName
}
The Array looks like this:
$MoveItem = #(
[pscustomobject]#{File="REFW10-X86-1709_01-12-2017.wim";Version=1709}
[pscustomobject]#{File="REFW10-X86-1706_01-12-2017.wim";Version=1706}
)
So you have a hash table (or similar) named $MoveItem that has a .File property that is a filename, and you have a .Versions property that's a string array?
Test name: REFW10-X86-1709_01-12-2017.wim
Get-ChildItem -Path $OSPathTemp -Recurse |
ForEach-Object {
If ($_.Name -match '-\d{4}_') { $Version = $Matches[0] }
If ($Version -in $MoveItem.Versions -and
$_.Name -eq $MoveItem.File) { $_.FullName }
}

Copy-Item with exclude files and folders in powershell

I have a question. I want to copy content of one directory into another and made some progress. But, at the moment I'm stuck because if I can define array of files to exclude I have a problem with array of folders. So the arrays looks like:
$excludeFiles = #('app.config', 'file.exe')
$excludeFolders = #('Logs', 'Reports', 'Backup', 'Output')
It works when there is only one item in $excludeFolders array, but if I add multiple items, it copies all folders without excluding them.
Script I have:
Get-ChildItem -Path $binSolutionFolder -Recurse -Exclude $excludeFiles |
where { $excludeFolders -eq $null -or $_.FullName.Replace($binSolutionFolder, "") -notmatch $excludeFolders } |
Copy-Item -Destination {
if ($_.PSIsContainer) {
Join-Path $deployFolderDir $_.Parent.FullName.Substring($binSolutionFolder.length -1)
}
else {
Join-Path $deployFolderDir $_.FullName.Substring($binSolutionFolder.length -1)
}
} -Force -Exclude $excludeFiles
Where $binSolutionFolder is source and $deployFolderDir is target.
Files work fine, but with folders I've run out of ideas.
-notmatch uses a regex-pattern and not a collection. To match against a collection of words you could use -notin $excludedfolders, but if the path includes 2+ level of folders or just a simple \ then the test would fail.
I would use -notmatch, but first create a regex-pattern that checks all folders at ones. Ex:
$excludeFiles = #('app.config', 'file.exe')
$excludeFolders = #('Logs', 'Reports', 'Backup', 'Output','Desktop')
$excludeFoldersRegex = $excludeFolders -join '|'
Get-ChildItem -Path $binSolutionFolder -Recurse -Exclude $excludeFiles |
where { $_.FullName.Replace($binSolutionFolder, "") -notmatch $excludeFoldersRegex } |
.....

Powershell to replace text in multiple files stored in many folders

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

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

Resources