Recursive Wildcards in PowerShell - powershell

I'm trying to delete files in a specific set of folders with PowerShell. My code currently looks like this:
$tempfolders = #(
"C:\Windows\Temp\*"
"C:\Documents and Settings\*\Local Settings\temp\*"
"C:\Users\*\Appdata\Local\Temp\*"
)
Remove-Item $tempfolders -Recurse -Force -ErrorAction SilentlyContinue
I want to add a new folder to that list, with the following formula:
"C:\users\*\AppData\Local\Program\**\Subfolder"
where ** could be multiple subfolders of unknown length. For example, it could be settings\subsettings or it could be folder\settings\subsettings. Is there a way to do this?

You can feed the full path of each file to a regex; this will return only the files that match your format, which you can then pipe to Remove-Item:
ls "C:\Users" -Recurse -Hidden | Where-Object { $_.FullName -imatch '^C:\\users\\([^\\]+)\\AppData\\Local\\Program\\(.*)\\Subfolder' }
Because regexes are considered write-only, a bit of explanation:
backslashes count as escape characters inside a regex and need to be doubled.
([^\\]+) means one or more of any character except a backslash
.* means zero or more of any character

You can use Get-ChildItem -Directory -Recurse,
This will do exactly what you are asking for. Pipe it from the array to a Remove-Item
#("C:\Windows\Temp\*", "C:\Documents and Settings\*\Local Settings\temp\*", "C:\Users\*\Appdata\Local\Temp\*") |
Get-ChildItem -Directory -Recurse -Force -ErrorAction SilentlyContinue |
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
Just for a heads up in Powershell Piping | is key. PowerShell we love to chain commands.

Related

Powershell script to copy files based on filename

I have a folder that contains several thousand files. I would like to write a Powershell script that loops through the files and copies each file whose filename contains a specific keyword. In pseudocode:
For each file in C:\[Directory]
If filename contains "Presentation" Then
copy file in C:\[Directory 2]
Simply like this ?
copy-item "C:\SourceDir\*Presentation*" "C:\DestinationDir"
or like this :
copy-item "C:\SourceDir\*" "C:\DestinationDir" -Filter "*rrrr*"
But a risk exist if you have a directory with "presentation" in his name into the source directory. Then take all method proposed here and add -file in get-childitem command.
Like in this short version of Robdy code :
gci "C:\SourceDir" -file | ? Name -like "*Presentation*" | cpi -d "C:\DestinationDir"
That code should do the trick:
$files = Get-ChildItem -Path "C:\path\to\source\folder"
$files | Where-Object Name -Like "*Presentation*" | Copy-Item -Destination "C:\path\to\destination\folder"
Of course can be written in one line but I put in two for visibility.
Edit: as Esperento57 pointed out, you might want to add -ItemType File to Get-ChildItem cmdlet to not include folders with 'Presentation' in their name. Also, depending on your needs you might also want to use -Recurse param to include files in subfolders.
If you have files in subfolders and you want to keep the path in destination folder you'll have to change the script a bit to something like:
Copy-Item -Destination $_.FullName.Replace('C:\path\to\source\folder','C:\path\to\destination\folder')
And for the above you'll have to make sure that folders are actually created (e.g. by using -Force for Copy-Item.
This seems to work:
$src = "Dir1"
$dst = "Dir2"
Get-ChildItem $src -Filter "*Presentation*" -Recurse | % {
New-Item -Path $_.FullName.Replace($src,$dst) -ItemType File -Force
Copy-Item -Path $_.FullName -Destination $_.FullName.Replace($src,$dst) -Force
}
Try something like this:
Get-ChildItem "C:\Your\Directory" -File -Filter *YourKeyWordToIsolate* |
Foreach-Object { Copy-Item $_.FullName -Destination "C:\Your\New\Directory" }
... but, of course, you'll need to fill in some of the blanks left open by your pseudocode example.
Also, that's a one-liner, but I inserted a return carriage for easier readability.

PowerShell not accepting command line parameter [duplicate]

I am searching for a file in all the folders.
Copyforbuild.bat is available in many places, and I would like to search recursively.
$File = "V:\Myfolder\**\*.CopyForbuild.bat"
How can I do it in PowerShell?
Use the Get-ChildItem cmdlet with the -Recurse switch:
Get-ChildItem -Path V:\Myfolder -Filter CopyForbuild.bat -Recurse -ErrorAction SilentlyContinue -Force
I use this to find files and then have PowerShell display the entire path of the results:
dir -Path C:\FolderName -Filter FileName.fileExtension -Recurse | %{$_.FullName}
You can always use the wildcard * in the FolderName and/or FileName.fileExtension. For example:
dir -Path C:\Folder* -Filter File*.file* -Recurse | %{$_.FullName}
The above example will search any folder in the C:\ drive beginning with the word Folder. So if you have a folder named FolderFoo and FolderBar PowerShell will show results from both of those folders.
The same goes for the file name and file extension. If you want to search for a file with a certain extension, but don't know the name of the file, you can use:
dir -Path C:\FolderName -Filter *.fileExtension -Recurse | %{$_.FullName}
Or vice versa:
dir -Path C:\FolderName -Filter FileName.* -Recurse | %{$_.FullName}
When searching folders where you might get an error based on security (e.g. C:\Users), use the following command:
Get-ChildItem -Path V:\Myfolder -Filter CopyForbuild.bat -Recurse -ErrorAction SilentlyContinue -Force
Here is the method that I finally came up with after struggling:
Get-ChildItem -Recurse -Path path/with/wildc*rds/ -Include file.*
To make the output cleaner (only path), use:
(Get-ChildItem -Recurse -Path path/with/wildc*rds/ -Include file.*).fullname
To get only the first result, use:
(Get-ChildItem -Recurse -Path path/with/wildc*rds/ -Include file.*).fullname | Select -First 1
Now for the important stuff:
To search only for files/directories do not use -File or -Directory (see below why). Instead use this for files:
Get-ChildItem -Recurse -Path ./path*/ -Include name* | where {$_.PSIsContainer -eq $false}
and remove the -eq $false for directories. Do not leave a trailing wildcard like bin/*.
Why not use the built in switches? They are terrible and remove features randomly. For example, in order to use -Include with a file, you must end the path with a wildcard. However, this disables the -Recurse switch without telling you:
Get-ChildItem -File -Recurse -Path ./bin/* -Include *.lib
You'd think that would give you all *.libs in all subdirectories, but it only will search top level of bin.
In order to search for directories, you can use -Directory, but then you must remove the trailing wildcard. For whatever reason, this will not deactivate -Recurse. It is for these reasons that I recommend not using the builtin flags.
You can shorten this command considerably:
Get-ChildItem -Recurse -Path ./path*/ -Include name* | where {$_.PSIsContainer -eq $false}
becomes
gci './path*/' -s -Include 'name*' | where {$_.PSIsContainer -eq $false}
Get-ChildItem is aliased to gci
-Path is default to position 0, so you can just make first argument path
-Recurse is aliased to -s
-Include does not have a shorthand
Use single quotes for spaces in names/paths, so that you can surround the whole command with double quotes and use it in Command Prompt. Doing it the other way around (surround with single quotes) causes errors
Get-ChildItem V:\MyFolder -name -recurse *.CopyForbuild.bat
Will also work
Try this:
Get-ChildItem -Path V:\Myfolder -Filter CopyForbuild.bat -Recurse | Where-Object { $_.Attributes -ne "Directory"}
Filter using wildcards:
Get-ChildItem -Filter CopyForBuild* -Include *.bat,*.cmd -Exclude *.old.cmd,*.old.bat -Recurse
Filtering using a regular expression:
Get-ChildItem -Path "V:\Myfolder" -Recurse
| Where-Object { $_.Name -match '\ACopyForBuild\.[(bat)|(cmd)]\Z' }
To add to #user3303020 answer and output the search results into a file, you can run
Get-ChildItem V:\MyFolder -name -recurse *.CopyForbuild.bat > path_to_results_filename.txt
It may be easier to search for the correct file that way.
On a Windows system:
Search for all .py files in the 'c:\temp' dir and subdirs, type: dir -r *.py or dir *.py -r
On a *Nix (Linux / MacOs system:
at the terminal type: find /temp -name *.py
This works fine for me.
Generally, robocopy is the fastest and simplest way for searching multiple files in parallel threads. It needs a quite good Powersell code with parallelism to beat that. Here is a link to an article I have written in the past with all the different options you have: Fastest way to find a full path of a given file via Powershell? Check the accepted answer for the best code.

Searching Logs for Filename From List in a Directory w/ Powershell

I'm trying to search text file content in a log directory for matching file names that exist in another directory.
I know I can do a Get-ChildItem $Path -file -name and get a list returned. I also know how to perform a Get-Content ... | Select-String -Pattern
However, I don't know how to feed the file list to the -Pattern.
What I've tried without success:
# Delete all Files in C:\Data\Uploads older than 90 day(s)
$Path = "C:\the_path"
$LogPath = "C:\logs"
Get-Content $LogPath + "\*.log" | Select-String -Pattern (Get-ChildItem $Path -name)
But I know this is just a blind attempt because Get-ChildItem is returning an iterative and not a usable pattern.
How can I do what I'm attempting to do and that is take a list of file names and recursively search for them in a directory of log files? #wishingitwasgrep
Select-String essentially is PowerShell's implementation of grep. Except it can't recurse by itself. That's where Get-ChildItem comes into play.
Get-ChildItem -Path "$LogPath\*.log" -Recurse |
Select-String -Pattern (Get-ChildItem $Path -Name) -SimpleMatch
You can make the statement a little less verbose by using aliases as well as positional instead of named parameters (not recommended for use in scripts, though).
ls "$LogPath\*.log" -r | sls (ls $Path -n) -s
If you want a regular expression match instead of a simple string match remove the -SimpleMatch switch.
You're close, but here's something that should work:
#(Get-Content -Path C:\logs\*.log) |
Where-Object { $_ -in #(Get-ChildItem -Path C:\the_path -Name) }
Now you have a list of files.
How can I do what I'm attempting to do and that is take a list of file names and recursively search for them in a directory of log files?
$List = Get-Content -Path 'C:\LogList.txt'
$LogList = #(Get-ChildItem -Path 'C:\Logs' -Recurse |
Where-Object { $_.Name -in $List })
This assumes your LogList.txt has a newline separated list of log file names with an extension (such as MyLog.txt). $LogList will then have an array of System.IO.FileInfo objects which you can utilize to do whatever you want with these files. For example:
$LogList | Remove-Item

Windows10/Powershell: How to use -include parameter when using Get-Childitem?

When using "-filter":
Get-ChildItem -file -filter "*.txt" | foreach-object { write-host $_.FullName }
I get a listing of the 4 .txt files that's in the current folder.
I tried using "-include"
Get-ChildItem -file -include *.txt | foreach-object { write-host $_.FullName }
Get-ChildItem -file -include *txt | foreach-object { write-host $_.FullName }
and I get nothing. I tried with and without the "-file" parameter and it makes no difference.
I've looked at various guides/examples (ss64.com/TechNet and etc) and supposedly I am doing it right.
Any ideas what I could be doing wrong? Thanks!
From the Get-Help page for Get-ChildItem:
The -Include parameter is effective only when the command includes the -Recurse parameter or the path leads to the contents of a directory, such as C:\Windows*, where the "*" wildcard character specifies the contents of the C:\Windows directory.
You'll note that you don't get a syntax error if you specify -include and don't specify -recurse in spite of the fact that whatever it does is literally undefined. You'll also note that C:\Windows* is not a normal wildcard expression for "all files in the C:\Windows directory". It's a wildcard expression for "all items that start with 'Windows' in the C:\ directory and may or may not have an extension". I have no idea what the authors of Get-ChildItem think this parameter is supposed to do. They've done a fantastically poor job of documenting it and implementing it.
Consequently, I avoid the -Include parameter as broken/badly documented. I don't know what it's supposed to do that -Filter doesn't. I've read articles about what it does exactly. It "passes the value to the underlying provider to filter at that level" in some manner. I don't know why they assume that a sysadmin will know what that really means. My understanding is that it's the difference between calling DirectoryInfo.GetFiles() on each directory item and calling DirectoryInfo.GetFiles('*.txt') on each directory item, but most sysadmins aren't going to know what that means. However, it's so oddly behaved that I don't trust it, so even though I am about 95% sure of what it does... I still never use it.
Instead, I just pipe to Where-Object:
Get-ChildItem -file | Where-Object Extension -eq '.txt' | [...]
Also note that Get-ChildItem is broken with -LiteralPath, -Recurse and -Include in some versions of PowerShell, and will instead return all items.
Compare:
Get-ChildItem -LiteralPath $PSHOME *.exe -Recurse # works
Get-ChildItem -Path $PSHOME -Include *.exe -Recurse # works
Get-ChildItem -LiteralPath $PSHOME -Include *.exe -Recurse # does NOT work
Issue reported here for v6.
These work for me without recursion:
Get-ChildItem -Path "C:\Users\Athom\Desktop\*.txt"
Get-ChildItem -Path ".\*.txt"
Or Just add the recursion parameter:
Get-ChildItem -Include *.txt -Recurse

How to keep a specific folder and delete rest of the files using powershell

I am trying delete all files within a folder but there is 1 folder called pictures which I would like to keep but don't know how to do that. I am using the following script , it deletes everything in a folder
if ($message -eq 'y')
{
get-childitem "C:\test" -recurse | % {
remove-item $_.FullName -recurse
}
}
One solution is to use something like:
Get-ChildItem -Path "c:\test" -Recurse | Where-Object { $_.FullName -cnotmatch "\\Pictures($|\\)" -and (Get-ChildItem $_.FullName -Include "Pictures" -Recurse).Length -eq 0 } | Remove-Item -Recurse -ErrorAction SilentlyContinue;
I suspect there must be a way more elegant way to do this. Here's what this does: it enumerates all files in the C:\test folder recursively (Get-ChildItem), then it removes all items from the result list using Where-Object where the path contains the directory to be excluded (specified using regex syntax) or when the item in question has child items that contains the file or directory to be excluded. The resulting list is fed to Remove-Item for removal. The -ErrorAction SilentlyContinue switch is applied to prevent errors being logged with recursive removal.
Get-ChildItem $PSScriptRoot -Force| Where-Object {$_.Name -ne "Pictures"} | Remove-Item -Recurse
I just tried this, and it worked for me. If you want to change what is deleted just change the "Pictures". This uses $PSScriptRoot for the path, which is the execution path of the Powershell script. You can rename that to be the path of where you want to delete.