How to find folders efficiently with specific name using powershell? - powershell

I want to use powershell to search for folders with specific name in some path, I have this:
get-childitem -path $path -Recurse -Directory -filter $folderName |
foreach{
write-host $_.FullName
}
It works but it is very slow, because there are a lot of files to search for. The case I am dealing is that there are huge amount of files inside the folder I want to find itself. It is wasting time to check for all these files. So I am wondering if there is a way to not dig into this folder when the folder name matches what I want to search for. Cannot do it by removing -recurse tag because the folder I want to search is not necessarily just inside $path but maybe some levels down.
Thanks!

Assuming you have access to all folders in the path, you could use Directory.GetDirectories():
$recurse = [System.IO.SearchOption]::AllDirectories
[System.IO.Directory]::GetDirectories($path,$folderName,$recurse)

Related

Apply a file to multiple folders using better PowerShell script

I'm working on a project where I have to apply a file to multiple folders every so often. I'm trying to learn some PowerShell commands to make this a little easier. I came up with the following script, which works, but I feel that this is too verbose and could be distilled down with a better script:
[string]$sourceDirectory = "C:\Setup\App Folder Files\*"
# Create an array of folders
$destinationDirectories = #(
'C:\Users\GG_RCB1\Documents\',
'C:\Users\GG_RCB2\Documents\',
'C:\Users\LA_RCB1\Documents\',
'C:\Users\PR_RCB1\Documents\',
'C:\Users\PQ_RCB1\Documents\',
'C:\Users\PQ_RCB2\Documents\',
'C:\Users\XC_RCB1\Documents\',
'C:\Users\XC_RCB2\Documents\',
'C:\Users\XC_RCB3\Documents\',
'C:\Users\XC_RCB4\Documents\',
'C:\Users\XC_RCB5\Documents\',
'C:\Users\XC_RCB6\Documents\',
'C:\Users\XC_RCB7\Documents\',
'C:\Users\XC_RCB8\Documents\')
# Perform iteration to create the same file in each folder
foreach ($i in $destinationDirectories) {
Copy-item -Force -Recurse -Verbose $sourceDirectory -Destination $i
}
I go into this process knowing that every folder in the User folder area is going to have the same format: _RCB<#>\Documents\
I know that I can loop through those files using this code:
Get-ChildItem -Path 'C:\Users'| where-object {$_.Name -match "^[A-Z][A-Z]_RCB"}
What I'm not sure how to do is to how, within that loop, drill down to the Documents folder and do the copy. I want to avoid having to keep updating the array from the first code sample, particularly when I know the naming convention of the subfolders in the Users folder. I'm just looking for a cleaner way to do this.
Thanks for any suggestions!
Ehh, I'll go ahead and post what I had in mind as well. Not to take away from #Mathias suggestion in the comments, but to offer my solution, here's my take:
Get-ChildItem -Path "C:\users\[A-Z][A-Z]_RCB*\documents" |
Copy-Item -Path $sourceDirectory -Destination { $_.FullName } -Recurse -WhatIf
Since everyone loves the "One-Liners" that can accomplish your needs. Get-ChildItem accepts wildcard-expressions in it's path which let's us accomplish this in one go. Given that your directories are...
consistent with the same naming pattern,
[A-Z][A-Z]_*
and the folder destination is the same.
Documents
Luckily, Copy-Item also has some cool features on it's own such as being able to use a script block that will allow the passing of $_.FullName property as it's destination, while they are passed down the pipeline one at a time.
Remove the -WhatIf common parameter when you've dictated the results are what you're after.

Powershell Script that recursively searches for specific file and containing directory and copy it to another location

I'm attempting to write a powershell script that will search recursively for a file and copy it to another location on a local drive with the date appended to it.
However that file could be in multiple different directories. (ex. c:\users\default\Bookmark.txt, c:\users\profile1\Bookmark.txt, c:\users\profile2\Bookmark.txt...etc.)
To distinguish between the different directories I was thinking of appending the directory name containing the file to the filename along with the date. (ex. filename-directoryName-date)
Here is what I have so far:
get-childitem -path "$env:userprofile\AppData\Local\Microsoft\Edge\User Data" -filter Bookmarks -Recurse -ErrorAction SilentlyContinue -Force | copy-item -Destination $env:userprofile\Bookmarks-$(get-date -UFormat %d-%m-%Y)
This works if it only finds 1 copy of the Bookmarks file and it only appends the date.
To figure out the names of the containing folders I used this command.
(get-childitem -path "$env:userprofile\AppData\Local\Microsoft\Edge\User Data" -filter Bookmarks -Recurse -ErrorAction SilentlyContinue -Force).Directory.Name
I need to somehow put this two together and so it outputs :
c:(whateverlocation)\Bookmark-DirectoryName-Date
I hope I'm making sense.
Dan,
When you use the Get-ChildItem command (alias gci) it will return to you an array of DirectoryInfo and FileInfo objects, one for each item it finds. If you are looking for files called "Bookmarks" (or bookmark.txt...can't tell from your examples which one you're looking for) then you can use the following command to get you a list of all of them:
[array]$FileList = gci -Recurse -File -Filter "Bookmarks"
The [array] designation is necessary to be sure the object is an array regardless of how many items are returned. The filter can have wildcards if you don't know the exact filename. What that leaves me with is an array object named $FileList containing all of the information about the files. You can read all about the properties and methods available in these objects at this Microsoft help page.
For this task, we'll need the .FullName property, which tells you the full path & name of each item (which can be used as your source) and the .BaseName & .Extension properties, which give you the filename and extension respectively. Using these properties, you can copy each of the files you find to a destination. Putting it all together, you get something like this:
$SourceFolder = "$($env:userprofile)\AppData\Local\Microsoft\Edge\User Data"
$DestFolder = '' #Path to Destination Folder
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "Bookmarks"
ForEach ($F in $FileList) {
Copy-Item $F.FullName (Join-Path $DestFolder ($F.Name + 'stuff to add to end of filename' + $F.Extension))
}
In this case, since the files are named 'Bookmarks', the $F.Extension should be blank. If using a more traditional filename, the pattern above will help you fit your changes in between the filename and the extension.
Hope this helps you out. If it does, please accept the answer using the check mark on the left.

Copying files defined in a list from network location

I'm trying to teach myself enough powershell or batch programming to figure out to achieve the following (I've had a search and looked through a couple hours of Youtube tutorials but can't quite piece it all together to figure out what I need - I don't get Tokens, for example, but they seem necessary in the For loop). Also, not sure if the below is best achieved by robocopy or xcopy.
Task:
Define a list of files to retrieve in a csv (file name will be listed as a 13 digit number, extension will be UNKNOWN, but will usually be .jpg but might occasionally be .png - could this be achieved with a wildcard?)
list would read something like:
9780761189931
9780761189988
9781579657159
For each line in this text file, do:
Search a network folder and all subfolders
If exact filename is found, copy to an arbitrary target (say a new folder created on desktop)
(Not 100% necessary, but nice to have) Once the For loop has completed, output a list of files copied into a text file in the newly created destination folder
I gather that I'll maybe need to do a couple of things first, like define variables for the source and destination folders? I found the below elsewhere but couldn't quite get my head around it.
set src_folder=O:\2017\By_Month\Covers
set dst_folder=c:\Users\%USERNAME&\Desktop\GetCovers
for /f "tokens=*" %%i in (ISBN.txt) DO (
xcopy /K "%src_folder%\%%i" "%dst_folder%"
)
Thanks in advance!
This solution is in powershell, by the way.
To get all subfiles of a folder, use Get-ChildItem and the pipeline, and you can then compare the name to the insides of your CSV (which you can get using import-CSV, by the way).
Get-ChildItem -path $src_folder -recurse | foreach{$_.fullname}
I'd personally then use a function to edit the name as a string, but I know this probably isn't the best way to do it. Create a function outside of the pipeline, and have it return a modified path in such a way that you can continue the previous line like this:
Get-ChildItem -path $src_folder -recurse | foreach{$_.CopyTo (edit-path $_.fullname)}
Where "edit-directory" is your function that takes in the path, and modifies it to return your destination path. Also, you can alternatively use robocopy or xcopy instead of CopyTo, but Copy-Item is a powershell native and doesn't require much string manipulation (which in my experience, the less, the better).
Edit: Here's a function that could do the trick:
function edit-path{
Param([string] $path)
$modified_path = $dst_folder + "\"
$modified_path = $path.substring($src_folder.length)
return $modified_path
}
Edit: Here's how to integrate the importing from CSV, so that the copy only happens to files that are written in the CSV (which I had left out, oops):
$csv = import-csv $CSV_path
Get-ChildItem -path $src_folder -recurse | where-object{$csv -contains $_.name} | foreach{$_.CopyTo (edit-path $_.fullname)}
Note that you have to put the whole CSV path in the $CSV_path variable, and depending on how the contents of that file are written, you may have to use $_.fullname, or other parameters.
This seems like an average enough problem:
$Arr = Import-CSV -Path $CSVPath
Get-ChildItem -Path $Folder -Recurse |
Where-Object -FilterScript { $Arr -contains $PSItem.Name.Substring(0,($PSItem.Length - 4)) } |
ForEach-Object -Process {
Copy-Item -Destination $env:UserProfile\Desktop
$PSItem.Name | Out-File -FilePath $env:UserProfile\Desktop\Results.txt -Append
}
I'm not great with string manipulation so the string bit is a bit confusing, but here's everything spelled out.

Windows file search within a search, how? App, script, GREP, powershell, notepad hack?

I am trying to search for folders created within a certain date range, then search for files with certain attributes in only those folders. I thought with Windows 8's "advanced query system" this would be a 2 minute job...it isn't!
Can anyone recommend an easy way to do this? I'm thinking along the lines of regular expressions i can input into AstroGrep, or a Notepad++ hack, as it's easy to copy folder paths from windows search into a text document.
Thanks!
EDIT: To clarify, I am trying to find files which were added to the system during a certain date range. Searching by file created/modified attributes does not help as these attributes are carried over when the file is moved. However a folder's date attributes do change when files are moved in and out. Therefore I need to search for folders by date, then (because of the huge number of files and subfolders) search within the resulting folders for my files.
You could use the Get-ChildItem cmldet to retrieve all directories during a certain date range (for example: Now and a Month ago):
$dateNow = Get-Date
$dateaMonthAgo = $dateNow.AddMonths(-1)
$directories = Get-ChildItem -Path 'C:\' -Directory -Recurse |
Where { $_.LastAccessTime -le $dateNow -and $_.LastAccessTime -ge $dateaMonthAgo }
Now you have all directories that matches the date range. You can iterate over them and search for your files:
$directories | Get-ChildItem -Filter 'yourFile.txt'

PowerShell - bulk move of some subfolders to new server

I have been asked to move around 200 folders to a new server, but I'm not sure how to script it, or if it is possible.
I have a duplicate folder structure at the destination, but the problem is I have to move only 1 subfolder and its contents in each of the parent folders across. Looks like this:
FolderA
Folder1
Folder2
FolderB
Folder1
Folder2
Is it possible to move only 'Folder1' from Folders a-z and place 'Folder1' in its corresponding new parent folder?
I'd use Robocopy, particularly if you want to preserve the ownership and permissions.
I would be very tempted to use RoboCopy to do the copying, because you can set it to bring over the original file created and modified dates and times, and security permissions.
Although it can't do the Folder1 thing natively. So I would be looking at using PowerShell to generate a batch file of RoboCopy commands, then running that. e.g. something that looks like this, although I haven't tested it.
$sourceFolder = "\\server1\share"
$destFolder = "\\server2\share"
foreach ($folder in (Get-ChildItem -Directory $sourceFolder)) {
"robocopy `"$($sourceFolder)\$($folder)\Folder1`" `"$($destFolder)\$($folder)\Folder1`" /E /COPYALL /DCOPY:T" | Out-File roboscript.bat -Append
}
Then check over, and run roboscript.bat to do the copying.
More a comment on TessellatingHeckler's code than an answer here. Please give any credit to him that you would attribute to this since he had the answer first.
Since you are working with outputting strings to a text file you probably want to work with strings. In regards to your ForEach I would like to suggest:
foreach ($folder in (Get-ChildItem -Directory $sourceFolder | Select -ExpandProperty FullName)) {
$TargetFolder = $Folder -replace [regex]::Escape($sourceFolder), "$destFolder"
"robocopy `"$Folder\Folder1`" `"$TargetFolder\Folder1`" /E /COPYALL /DCOPY:T" | Out-File roboscript.bat -Append
}
That selects the full path for the folder in question as a part of the ForEach. Then it declares a variable as the target in which it replaces the source path with the destination path (source is escaped since this is a RegEx match). This likely makes it more flexible.
Also, you don't need to make subexpressions this way since it's just a string that you're referencing and not a [FileInfo] object.
Lastly, I thought I'd add that you can use two consecutive double quotes instead of escaping them if you prefer, though I would suggest escaping them as TH suggested.