PowerShell: Find exact folder name - powershell

I'm trying to find a way to return the full path to a given folder. The problem is that my code returns more than one folder if there is a similar named folder. e.g. searching for "Program Files", returns "Program Files" and "Programs Files (x86)". As I didn't ask for "Program Files (x86), I don't want it to return it. I am using:
$folderName = "Program Files"
(gci C:\ -Recurse | ?{$_.Name -match [regex]::Escape($folderName)}).FullName
I thought of replacing -match with -eq, but it will return $false as it's comparing the whole path.
I have thought of maybe returning all matches, then asking the user to select which one is correct, or creating an array that splits the path down and doing an -eq on each folder name and then joining the path again, but my skills are lacking in the array department and cannot get it to work.
Any help or pointers would be gratefully received.
Thanks
Here's what I have used with thanks to Frode:
$path = gci -Path "$drive\" -Filter $PartialPath -Recurse -ErrorAction SilentlyContinue #| ?{$_.PSPath -match [regex]::Escape($PartialPath)}
($path.FullName | gci -Filter $filename -Recurse -ErrorAction SilentlyContinue).FullName

-match is like asking for *Program Files*. You should be using the -Filter parameter of Get-ChildItem for something like this. It's a lot faster and doesn't require regex escape etc.
PowerShell 3:
$folderName = "Program Files"
(gci -path C:\ -filter $foldername -Recurse).FullName
PowerShell 2:
$folderName = "Program Files"
gci -path C:\ -filter $foldername -Recurse | Select-Object -Expand FullName
Also, you should not use -Recurse if you don't need it(like in this example).

Related

Create symlinks to results of folder search from get-childitem command in powershell and name them after foldername

Kind of new to powershell and am trying to search a top level folder for any subfolders that might contain a folder named "training" within them, and then create symlinks in a target folder for each result using the name of the parent folder that "training" folder is in..... Sorry if that sounds confusing.
So basically, a search of "C:\company" where if the "c:\company\test" folder had a "training" subfolder in it, the script would write a symlink to the destination named "test" but pointing to "c:\company\test\training".
I have a working command to find the folder search results I want
$folder = gci "TopSearchedFolder" -recurse -filter "training" | Select-Object Parent
And from searching it appears the following would create the symlinks..
New-Item -Itemtype SymbolicLink -path "SymlinkDestinationFolder"
I'm just not sure how to pipe the search results into the creation of the symlinks. Should I be using a For-each or something? Any help would be appreciated. Thanks in advance.
You'll need to have PowerShell 5.1 or newer.
Get-ChildItem -Path "TopSearchedFolder" -Filter 'training' -Directory -Recurse | ForEach-Object {
New-Item -ItemType SymbolicLink -Path "SymlinkDestinationFolder" -Name $_.Parent -Value $_.FullName
}
Try this:
gci "TopSearchedFolder" -recurse -filter "training" | Select-Object -ExpandProperty Parent|Foreach{
New-Item -Itemtype SymbolicLink -path "$($_)"
}
$_ is the current item in pipeline, and -ExpandProperty to expand values only.

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.

Powershell: Move file from one directory to another, based on string within file

Looking around the site and can't seem to find some answers for myself.
I'm looking to write a script, that will enable me to move files from one destination to another, based on the contents within the file.
To get into Specifics
Source Destination - V:\SW\FromSite
Copy to Destination - V:\SW\ToSW
FileType - .txt
String - test
Ideally I'd also like to ONLY have the script search files that begin with 7. These are unique identifiers to a region.
Pulling my hair out a bit trying.
I was using the below, which runs without error, but does nothing.
$DestDir = "V:\SW\FromSite"
$SrcDir = "V:\SW\ToSW"
$SearchString = "test"
gci $SrcDir -filter 7*.txt | select-string $SearchString | select path |
move-item -dest $DestDir -whatif
Here's what I would do, though I'm sure there's a more streamlined way to do it.
$files = gci $SrcDir -filter 7*.txt
$files | %{
if ((select-string -path $_.FullName -pattern $SearchString) -ne $null) {
move-item -path $_.FullName -dest $DestDir
}
}
So did some more messing around and the below is working perfectly for what I need
get-childitem "<SourceFolder>" -filter 7*.txt -
recurse | select-string -list -pattern "test" | move -dest "<DestinationFolder>"
Thanks all for the help
Not sure if I missed something (didn't tried it on my machine) - but as long as you pass the -whatif option, move-item "shows what would happen if the cmdlet runs. The cmdlet is not run."; see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/move-item.
So probably it would have been sufficient to just remove the -whatif from the initial statement.

Using Remove-Item cmdlet but excluding sub-directory

I want to remove the following files from the source, however in the source there is a sub-directory that contains files with similar names. When I run the following command it is deleting files in the sub-directory with similar file name. Is there a way to just delete the files from the source and not the sub-directory?
Example: test_1_file, test_2_file, test_3_file exists in each directory, TestFolder and TestFolder/sub
$source = testfolder
remove-item -Path $source -filter test_*_file -recurse -force
It's usually easiest to pipe the output of Get-ChildItem cmdlet into Remove-Item. You then can use the better filtering of Get-ChildItem as I think -Recurse in Remove-Item has some issues. You can even use Where-Object to further filter before passing to Remove-Item
$source = testfolder
Get-ChildItem -Path $source -Filter test_*_file -Recurse |
Where-Object {$_.Fullname -notlike "$source\sub\*"} |
Remove-Item -Force
If the files to delete:
are all located directly in $source
and no other files / directories must be deleted:
Remove-Item -Path $source/test_*_file -Force
No need for -Recurse (as #Bill_Stewart notes).
Note: For conceptual clarity I've appended the wildcard pattern (test_*_file) directly to the $source path.
Using a wildcard expression separately with -Filter is generally faster (probably won't matter here), but it has its quirks and pitfalls.

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.