Powershell renaming special directories recursively - windows

I got this folder structure
C:\Users\myUser\Desktop
including folders called
BL-100
BL-105
BL-108
and so on...
most BL-folders storing a file.xml, but not all.
So on Desktop are much folders starting with BL- and the most, but not all, storing a file.xml.
Now I want to search all folders which are starting with BL- and store a file.xml and rename those folders to RG-100, RG-105, RG-108 and so on
At the moment I got this script:
foreach($Directory in Get-ChildItem -Path C:\Users\myUser\Desktop -Recurse | Where-Object{($_.Name.Substring(0,3) -eq 'BL-')}){
}
This does not work and is showing me error: Exception calling "Substring" with "2" argument(s): "Index and length must refer to a location within the string. Parameter name: length"
Anyone can help please?

The error you're seeing is because SubString fails for some reason. The most likely reason would be if the string is not long enough; e.g. if you had a folder with a 1 character long name. To see what I mean, try running: '1'.Substring(0,2).
To avoid this, instead you could use the like operator. e.g.
foreach($Directory in (
Get-ChildItem -LiteralPath 'C:\Users\myUser\Desktop' -Recurse `
| Where-Object{($_.Name -like 'BL-*')}
)){
#...
}

Just do it:
Get-ChildItem "C:\Users\myuser\Desktop" -directory -Filter "BL-*"

Related

Use Powershell to delete ShellIconOverlayIdentifiers on Windows

I am currently very annoyed by Dropbox and Nextcloud, which both battle the ShellIconOverlayIdentifier list. A problem which many people seem to have, when you search the internet.
Now I want to combine my annoyance with my intent to learn powershell (7.2.0).
I started with the following script, which shall retrieve all keys. And later I want to use regex via -match to find the entries I want to delete. For now I work with both Remove-Item -WhatIf and Get-ItemProperty to test it.
Currently my problem is that I can create my list as intended. But when I feed the list into the remove command I get that the path cannot be found. What am I doing wrong?
Push-Location -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers
$list = Get-ChildItem -Path .
$filteredList = $list -match "DropboxExt10"
$filteredList
# Remove-Item -WhatIf -Recurse $filteredList
Get-ItemProperty $filteredList
Pop-Location
The error is Cannot find path 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\ DropboxExt10' because it does not exist. Apparantly it adds the path as relative path to the current location. Why doesn't it interpret as an absolute path? When I ommit the push-location part it trys to add the registry path to my current working directory in which the script lives. But this is wrong as well.
Thanks for your help in advance.
Use one of the following:
$filteredList | Remove-Item -WhatIf -Recurse
# Alternatively:
Remove-Item -LiteralPath $filteredList.PSPath -WhatIf -Recurse
The above commands ensure that the registry items (keys) are bound by their full, provider-qualified paths to Remove-Item's -LiteralPath parameter, via their .PSPath property; in the case of registry paths, the path's provider prefix is Microsoft.PowerShell.Core\Registry:: (or, if identifying the originating module isn't also required, Registry::, as used in your Push-Location call)
What appears to be happening is that when the items are stringified - which is what happens when you pass them as a whole, as an argument (e.g. Remove-Item $filteredlist, which is the same as Remove-Item -Path $filteredlist), they lack the provider prefix and are represented as registry-native paths.
And given that a full registry-native path such as HKEY_LOCAL_MACHINE\... neither starts with a drive specification nor with \ or /, it is interpreted as a relative path, resulting in the symptom you saw.

How to get nested path of all empty windows directory using cmd

I have a folder and numerous nested folders are present inside that. Many of the folders are empty and I have to copy an empty.property file in those empty folders. So that the end result will be no folder will be completely empty, either it contains another folder, any other file(s) or this empty.property. I have tried to get all the paths using dir /b /s, but it is returning all the paths, not only the empty one. Can anyone help me to get that very efficiently. Thanks.
You can use powershell to do it several ways with one being:
Get-ChildItem -Path C:\Temp -Directory | Where-Object {$_.GetFileSystemInfos().Count -eq 0} |
ForEach-Object -Process { Copy-Item -Path "My\File\to\copy\Path" -Destination $_.FullName }
Basically checks to see which directory doesnt have no files or folders in it, then pipes it to a foreach to a process a Copy-Item request for whatever file/folder you want it to copy from, to the empty folder.

Is there any way to find 'metadata' on folders using powershell

So I want to know if any of the folders in a directory have any subfolders or files in them, I tried just looking at the directory in PowerShell but it gave me only mode, last write time, and name. Is there any way of adding to this list to include metadata of the folder like size or number of subfiles/folders all I want to know is if they are empty or not so there may be a simpler way I'm missing.
Thanks for any help you can give!
I see the question is tagged 'windows', so on Windows you could also use a COM object.
$fso = New-Object -ComObject Scripting.FileSystemObject
$folder = $fso.GetFolder($pathToFolder)
$folder will be an object with a bunch of interesting metadata on it, including SubFolders and Files. One of the interesting ones is Size. If Size is zero, there are no files in that directory, or in any nested subdirectories either.
If you just want to know if there are folders/subfolders and/or files then this will work:
$folder="C:\Test"
Get-ChildItem $folder -Recurse | Measure-Object
Output (in my case)
Count : 2
Average :
Sum :
Maximum :
Minimum :
Property :
If you want to see more properties then this might work for you:
Get-ChildItem -Path $folder -Recurse | Format-List *
alternatively you can also select the first x, last x, or even skip items:
Get-ChildItem -Path $folder -Recurse |Select-Object -First 2| Format-List *
*-Recurse will check all folders below

How to use PowerShell Remove-Item to delete a directory with long name? [duplicate]

I'm writing a simple script to delete USMT migration folders after a certain amount of days:
## Server List ##
$servers = "Delorean","Adelaide","Brisbane","Melbourne","Newcastle","Perth"
## Number of days (-3 is over three days ago) ##
$days = -3
$timelimit = (Get-Date).AddDays($days)
foreach ($server in $servers)
{
$deletedusers = #()
$folders = Get-ChildItem \\$server\USMT$ | where {$_.psiscontainer}
write-host "Checking server : " $server
foreach ($folder in $folders)
{
If ($folder.LastWriteTime -lt $timelimit -And $folder -ne $null)
{
$deletedusers += $folder
Remove-Item -recurse -force $folder.fullname
}
}
write-host "Users deleted : " $deletedusers
write-host
}
However I keep hitting the dreaded Remove-Item : The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
I've been looking at workarounds and alternatives but they all revolve around me caring what is in the folder.
I was hoping for a more simple solution as I don't really care about the folder contents if it is marked for deletion.
Is there any native Powershell cmdlet other than Remove-Item -recurse that can accomplish what I'm after?
I often have this issue with node projects. They nest their dependencies and once git cloned, it's difficult to delete them. A nice node utility I came across is rimraf.
npm install rimraf -g
rimraf <dir>
Just as CADII said in another answer: Robocopy is able to create paths longer than the limit of 260 characters. Robocopy is also able to delete such paths. You can just mirror some empty folder over your path containing too long names in case you want to delete it.
For example:
robocopy C:\temp\some_empty_dir E:\temp\dir_containing_very_deep_structures /MIR
Here's the Robocopy reference to know the parameters and various options.
I've created a PowerShell function that is able to delete a long path (>260) using the mentioned robocopy technique:
function Remove-PathToLongDirectory
{
Param(
[string]$directory
)
# create a temporary (empty) directory
$parent = [System.IO.Path]::GetTempPath()
[string] $name = [System.Guid]::NewGuid()
$tempDirectory = New-Item -ItemType Directory -Path (Join-Path $parent $name)
robocopy /MIR $tempDirectory.FullName $directory | out-null
Remove-Item $directory -Force | out-null
Remove-Item $tempDirectory -Force | out-null
}
Usage example:
Remove-PathToLongDirectory c:\yourlongPath
This answer on SuperUser solved it for me: https://superuser.com/a/274224/85532
Cmd /C "rmdir /S /Q $myDir"
I learnt a trick a while ago that often works to get around long file path issues. Apparently when using some Windows API's certain functions will flow through legacy code that can't handle long file names. However if you format your paths in a particular way, the legacy code is avoided. The trick that solves this problem is to reference paths using the "\\?\" prefix. It should be noted that not all API's support this but in this particular case it worked for me, see my example below:
The following example fails:
PS D:\> get-childitem -path "D:\System Volume Information\dfsr" -hidden
Directory: D:\System Volume Information\dfsr
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs 10/09/2014 11:10 PM 834424 FileIDTable_2
-a-hs 10/09/2014 8:43 PM 3211264 SimilarityTable_2
PS D:\> Remove-Item -Path "D:\System Volume Information\dfsr" -recurse -force
Remove-Item : The specified path, file name, or both are too long. The fully qualified file name must be less than 260
characters, and the directory name must be less than 248 characters.
At line:1 char:1
+ Remove-Item -Path "D:\System Volume Information\dfsr" -recurse -force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (D:\System Volume Information\dfsr:String) [Remove-Item], PathTooLongExcepti
on
+ FullyQualifiedErrorId : RemoveItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
PS D:\>
However, prefixing the path with "\\?\" makes the command work successfully:
PS D:\> Remove-Item -Path "\\?\D:\System Volume Information\dfsr" -recurse -force
PS D:\> get-childitem -path "D:\System Volume Information\dfsr" -hidden
PS D:\>
If you have ruby installed, you can use Fileman:
gem install fileman
Once installed, you can simply run the following in your command prompt:
fm rm your_folder_path
This problem is a real pain in the neck when you're developing in node.js on Windows, so fileman becomes really handy to delete all the garbage once in a while
This is a known limitation of PowerShell. The work around is to use dir cmd (sorry, but this is true).
http://asysadmin.tumblr.com/post/17654309496/powershell-path-length-limitation
or as mentioned by AaronH answer use \?\ syntax is in this example to delete build
dir -Include build -Depth 1 | Remove-Item -Recurse -Path "\\?\$($_.FullName)"
If all you're doing is deleting the files, I use a function to shorten the names, then I delete.
function ConvertTo-ShortNames{
param ([string]$folder)
$name = 1
$items = Get-ChildItem -path $folder
foreach ($item in $items){
Rename-Item -Path $item.FullName -NewName "$name"
if ($item.PSIsContainer){
$parts = $item.FullName.Split("\")
$folderPath = $parts[0]
for ($i = 1; $i -lt $parts.Count - 1; $i++){
$folderPath = $folderPath + "\" + $parts[$i]
}
$folderPath = $folderPath + "\$name"
ConvertTo-ShortNames $folderPath
}
$name++
}
}
I know this is an old question, but I thought I would put this here in case somebody needed it.
There is one workaround that uses Experimental.IO from Base Class Libraries project. You can find it over on poshcode, or download from author's blog. 260 limitation is derived from .NET, so it's either this, or using tools that do not depend on .NET (like cmd /c dir, as #Bill suggested).
Combination of tools can work best, try doing a dir /x to get the 8.3 file name instead. You could then parse out that output to a text file then build a powershell script to delete the paths that you out-file'd. Take you all of a minute. Alternatively you could just rename the 8.3 file name to something shorter then delete.
For my Robocopy worked in 1, 2 and 3
First create an empty directory lets say c:\emptydir
ROBOCOPY c:\emptydir c:\directorytodelete /purge
rmdir c:\directorytodelete
This is getting old but I recently had to work around it again. I ended up using 'subst' as it didn't require any other modules or functions be available on the PC this was running from. A little more portable.
Basically find a spare drive letter, 'subst' the long path to that letter, then use that as the base for GCI.
Only limitation is that the $_.fullname and other properties will report the drive letter as the root path.
Seems to work ok:
$location = \\path\to\long\
$driveLetter = ls function:[d-z]: -n | ?{ !(test-path $_) } | random
subst $driveLetter $location
sleep 1
Push-Location $driveLetter -ErrorAction SilentlyContinue
Get-ChildItem -Recurse
subst $driveLetter /D
That command is obviously not to delete files but can be substituted.
PowerShell can easily be used with AlphaFS.dll to do actual file I/O stuff
without the PATH TOO LONG hassle.
For example:
Import-Module <path-to-AlphaFS.dll>
[Alphaleonis.Win32.Filesystem.Directory]::Delete($path, $True)
Please see at Codeplex: https://alphafs.codeplex.com/ for this .NET project.
I had the same issue while trying to delete folders on a remote machine.
Nothing helped but... I found one trick :
# 1:let's create an empty folder
md ".\Empty" -erroraction silentlycontinue
# 2: let's MIR to the folder to delete : this will empty the folder completely.
robocopy ".\Empty" $foldertodelete /MIR /LOG+:$logname
# 3: let's delete the empty folder now:
remove-item $foldertodelete -force
# 4: we can delete now the empty folder
remove-item ".\Empty" -force
Works like a charm on local or remote folders (using UNC path)
Adding to Daniel Lee's solution,
When the $myDir has spaces in the middle it gives FILE NOT FOUND errors considering set of files splitted from space. To overcome this use quotations around the variable and put powershell escape character to skip the quatations.
PS>cmd.exe /C "rmdir /s /q <grave-accent>"$myDir<grave-accent>""
Please substitute the proper grave-accent character instead of <grave-accent>
SO plays with me and I can't add it :). Hope some one will update it for others to understand easily
Just for completeness, I have come across this a few more times and have used a combination of both 'subst' and 'New-PSDrive' to work around it in various situations.
Not exactly a solution, but if anyone is looking for alternatives this might help.
Subst seems very sensitive to which type of program you are using to access the files, sometimes it works and sometimes it doesn't, seems to be the same with New-PSDrive.
Any thing developed using .NET out of the box will fail with paths too long. You will have to move them to 8.3 names, PInVoke (Win32) calls, or use robocopy

Move list of empty folders from one volume to another with PowerShell

I am using PowerShell 2.0 on a Windows SBS 2008 machine with the latest service packs. I have a two line script that finds all empty folders in a directory:
$a = Get-ChildItem E:\File_Server -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | [what now?!]
The second line finds all folders that are empty, however I'm stumped on the last pipe. Here's what I've tried so far (keep in mind that the following were tried after the second pipe in the second line above):
$_.move(F:\path) Yes, I tried that. Yes, I'm a PowerShell noob. Of course, I received the error "Expressions are only allowed as the first element of a pipeline."
move-item -destination F:\Path I receive the lamest error ever: "Move-Item : Source and destination path must have identical roots. Move will not work across volumes." Seriously? What kind of asinine limitation is that?! Moving on...
copy-item -destination F:\empty_folders Apparently I can get around the limitation of move-item by using copy-item and then use remove-item. No such luck, however. I started the script with copy-item first. PowerShell didn't throw any errors, but also didn't do any thing else. It just sat there for the better part of an hour. No directories were moved.
Summary:
How do I take the list of empty folders that I have and, using PowerShell, move those empty directories elsewhere (across volumes!) deleting the originals after the move? Notice the caveat "using PowerShell" as I've already thought about adding RoboCopy to the mix but would like to keep it within PowerShell.
I was pretty frustrated with this as well, but like you said copy-item followed by remove-item will work:
gci e:\file_server | ?{$_.PSIsContainer -eq $true} | ?{$_.GetFiles().count -eq 0} |
%{copy-item -LiteralPath $_.fullname -Destination f:\path; remove-item $_.fullname}
Note this is not just a limitation with Move-Item and Powershell but applicable to .Net System.IO.Directory.Move and to be fair, the documentation says so:
Move-Item will move files between drives that are supported by the
same provider, but it will move directories only within the same
drive.
http://technet.microsoft.com/en-us/library/dd315310.aspx
PS: You got the "Expressions are only allowed as the first element of a pipeline." error because the $_.move(F:\path) that you were trying must be inside a foreach-object like so - %{$_.move(F:\path)}

Resources