Strange Powershell GCI Recurse Results with Wildcard - powershell

I'm trying to use GCI with the Recurse argument to get a list of all files WITHIN the subfolders of a specified path.
I'm using the following line:
gci 'C:\temp\TestRecurse\*\*' -Recurse
Underneath the TestRecurse folder, I have the following:
TestRecurse
|---b.txt
|---dir1
|------a.txt
I expect a.txt to be returned. However, I'm getting a.txt and b.txt. Stranger still to me, if I put a.txt into another subfolder:
TestRecurse
|---b.txt
|---dir1
|------dir2
|---------a.txt
The same statement above only returns a.txt. I'm baffled as to how messing with the location of a.txt changes when b.txt is returned. Can someone explain to me why this happens, and why b.txt is ever returned at all?
Update
I should mention, while they're appreciated, I'm not necessarily looking for a workaround. This is part of a larger script in our environment that is in charge of moving files around in various ways while trying stay flexible. It's not behaving as I expected it would, so I'm trying to understand why it's working the way it is. As pointed out by PetSerAl, understanding Get-ChildItem may be more trouble than it's worth.
Thanks!

You're including a wildcard for the parent directory (TestRecurse\*), so you are getting files contained in it as well. Try getting the folder list of the TestRecurse, then iterating through them.
Structure:
TestRecurse\b.txt
TestRecurse\dir1
TestRecurse\dir1\a.txt
Code:
Get-ChildItem 'C:\tmp\TestRecurse\' | ` # Get the list of items within TestRecurse
? {$_.PSIsContainer} | ` # Filter items that are folders
% {Get-ChildItem $_.FullName -Recurse} # Iterate through and get all items within those folders
This only returns folders and files within dir1, but not dir1 itself.

Related

How to remove files from several locations with similar paths but not identical paths

I'm trying to come up with a way in order to quickly remove multiple files that are in similar child directories. I need to accomplish the removals quickly to several domain controllers so they don't replicate and replace the deleted files.
The file paths vary slightly but enough and I was hoping to use wildcards (*) to cut down the six possible path variations to only two.
something along these lines:
The {} actually exist since they are hash values
C:\Windows\SYSVOL\domain\Policies\{*}\User*\Script\Logon\abc*
C:\Windows\SYSVOL\sysvol\xxxx\Policies\{*}\User*\Script\Logon\abc*
Initially I was thinking along the lines of using a recursive select-string search and pipe it to remove-item but I was getting an access denial error when i was just hypothesizing the search without the pipe to remove-item.
gci -path C:\Windows\SYSVOL\ -rec | select-string -pattern "domain\Policies\{*}\User*\Script\Logon\abc*
and
gci -path C:\Windows\SYSVOL\ -rec | select-string -pattern "sysvol\xxxx\Policies\{*}\User*\Script\Logon\abc*" | remove-item $_
(the remove-item is basically pseudo code)
I tried delimiting the {'s and 's but that didn't work either.
Once I have this working in theory I would then need to use the invoke-command to try to get this removal process to work its way through the list of systems that need this operation.
I don't know if this is the right approach / best approach, or if it is even doable.
Any advise would be greatly appreciated.

Powershell: Rename a folder when only partial name is known

I need to rename a folder without knowing the full folder name.
For Example C:\myfolder-2021-5-1 (I know the first part of the folder name)
I would like to rename it to c:\myfolder... Again, the script wont always know the full folder name.
Edit: I am new to Powershell. I have spent a few hours looking on Google and I don't see examples of people trying to rename a folder using a wildcard. There are very few folder renaming examples that I could find. Most of what I find pertains to renaming files not folders.
I get it people wanting me to "try" first and then ask questions. But, sometimes, especially for us newbies, we don't even know where to start.
I tried using several filename examples and just using a directory name with a wildcard and that did not work.
Don't know what else to say.
Any ideas?
Thanks!
You cant rename a directory if you dont know it's name - thats just not how files and directories work. What you can do is search for a directory that matches your criteria, then you can rename it.
(I'm assuming the reason you dont know the exact name is because its a date, but im also assuming the format of the directory name is consistent)
At its most basic level, this would work for your example:
get-childitem -Path c:\ -Directory -Filter "myfolder-????-*-*" | Move-Item -Destination "renamed-myfolder"
This will search for directories in C:\ that match the pattern "myfolder-????-*-*" - so this pattern would match your example folder C:\myfolder-2021-5-1. Then pass that Directory down the pipeline (|) into Move-Item where the directory is renamed to c:\renamed-myfolder.
This code has some major drawbacks though! It doesnt check if the new name exists before trying to rename the directory so it might fail. Also if more than 1 folder matches the filter only the first rename (Move-Item) will succeed. Its upto you to think about these edge cases and add suitable logic to detect/prevent them.
Its a good idea to use Test-Path command to check if the destination name already exists or not:
if(Test-Path -Path c:\renamed-myfolder){
throw "ERROR - c:\renamed-myfolder already exists!";
}
NOTE: Get-ChildItem filters * means "any characters" and ? means "one character" so "myfolder-*-*-*" would also work, but if the year is always 4 digits then use the ???? as its more specific. Ive used the filter "myfolder-????-*-*" as im assuming some days/months will be 2 digits like myfolder-2021-12-12.

dir listing with path and total files filtered by date

Thanks in advance if you can help, I've searched here and found very close solutions but not exactly what I'm looking for...
What I need to do is this:
From a given directory, list files filtered by last modification date. I already know how to do that.
It needs to recurse into all sub-directories, and I can do that as well.
I'm looking for a listing that looks like this:
File-count of the files fitting the criteria. AbsolutePath
So:
7815 C:\yadda\yaddayadda etc.
And if there's a way to insert something like " files in " instead of the space or tab, even better.
So the listing would look like:
14,627 files in C:\WildBehemoth
9,243 files in C:\TameBehemoth
etc...
Any ideas?
Thanks again!
Create a custom object with the file count and the corresponding path, something like:
$output = #{
filecount = 1000;
path = "C:\..."
}
Then pipe these objects to Format-List or Format-table cmdlets to print it in the way you prefer.
Thanks so much everybody! I was able to resolve the problem and came up with this, single line in Powershell:
Get-Childitem -Path C:\PROGRA~2\beasts\ -Recurse -File | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-22)} | Group-Object directory | Select-Object count, name
The output I'm getting is exactly what I was looking for:
Count Name
2 C:\Program Files (x86)\beasts\largebeast\pachiderm
13 C:\Program Files (x86)\beasts\smallbeast\rodent
9631 C:\Program Files (x86)\beasts\tinybeast\gnat
So I have a count of files older than 21 days, and the absolute path to them next to the count.
Thanks again!

Renaming files in bulk and in ascending order in CMD

I know this question was already asked by someone but I will ask again.
Can someone tell me how to rename in bulk and in ascending order if possible in CMD. I already tried renaming in powershell but to no avail. It only let me use once and I need to rename another folder files but to no avail. It didn't let it rename files in another folder. This is the code I use in powershell:
$i = 1
Get-ChildItem *.mkv | %{Rename-Item $_ -NewName ('Haikyuu - {0:D2}.mkv' -f $i++)}
I'm renaming my anime series per folder and some of my copies have 100+ videos. and somehow you could teach me what each code mean (the code that must use in CMD). The ones I've searched can't understand it in layman's term or doesn't tell the user how it's supposed to work. Thank you in advance. by the way, the folder is placed in an external drive.
so from the beginning:
$i= variable for storing the initial value 1
Get-ChildItem = is like "dir" which lists the files and folder under a certain path.
In this case, it is listing all the files which starts with anything but have the extension .mkv
* indicates wildcard.
| = pipeline which passes the output of the first command as an input of the next command.
% = ForEach-Object is iterating each object one by one coming from the pipeline.
$_= Current pipeline object . Here it is taking each object one by one and renaming it using Rename-Item
-NewName = is the parameter of the Rename-Item which asks for the new name to pass.
Hope it clarifies your need.
The reason why I can't rename my video files is there were [brackets] on the filename.
So I use this:
Get-ChildItem -Recurse -Include *.mkv | Rename-Item -NewName { $_.Name.replace("[","").replace("]","").replace("(","").replace(")","") }
Which on the same directories, I can access subfolders too to omit brackets and parethesis. then I proceed using the code above in the question to rename my files in every folder. The Reason why I'm doing the 'renaming' per folder is that, each folder is different anime series. but the code above is working.
if anyone can give me less code than repeating the 'replace' and concatenating it, I will gladly accept and choose that as the best answer. :)
If you use the parameter -LiteralPath for the source, no prior renaming is necessary.
%i = 1
Get-ChildItem *.mkv |
ForEach {Rename-Item -LiteralPath "$_" -NewName ('Haikyuu - {0:D2}.mkv' -f $i++)}
A hint on sorting, I hope the present numbering of the source files has a constant width, otherwise the result is mixed up as an alphabetic sort (which is inherent to ntfs formatted drives) will sort the number 10 in front of 2.
To check this append the parameter -whatif to the Rename-Item command

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.