I´m a newbie still and learning to create PowerShell scripts to make Life in IT easier.
At present I´m trying to build a script, which runs a certain Microsoft tool, scanning defined network shares in a csv file and creating an JSON output file.
Now as the pattern of this file is always the same like "Report_Username_Hostname.vba.JSON", I would like to append either the scanned directory name or even a range of numbers, fe. "Report_Username_Hostname(100).vba.JSON" or "Report_Username_Hostname(sharename).vba.JSON"
This is neccessaray as after this renaming step, I upload this and other files within this folder to another folder on different server to upload them into a Database.
I planned to run this script in in many different locations on most automatic level and they all copy the their collected files to just one upload folder.
I already tried several options I found somewhere in the deep of the Internet, but I only came to the point where the file was renamed to 0 or similar, but not to expected result.
The Powershell script doing the work is this:
$PSpath = 'C:\temp\FileRemediation\Scripts\'
$Rpath = $PSpath +'..\Reports\1st'
$localshare = $PSpath +'..\temp\1st'
$csvinputs = Import-Csv $PSpath\fileremediation_1st.csv
$uploadshare = '\\PGC2EU-WFSFR01.eu1.1corp.org\upload\'
# This section checks if the folder for scan reports is availabe and if not will create necessary folder.
If(!(test-path $Rpath))
{
New-Item -ItemType Directory -Force -Path $Rpath
}
If(!(test-path $localshare))
{
New-Item -ItemType Directory -Force -Path $localshare
}
Set-Location $Rpath
# This section reads input from configuration file and starts Ms ReadinessReportCreator to scan shares in configuration file.
ForEach($csvinput in $csvinputs)
{
$uncshare = $csvinput.sharefolder
$Executeable = Start-Process "C:\Program Files (x86)\Microsoft xxx\xxx.exe" `
-Argumentlist "-p ""$uncshare""", "-r", "-t 10000", "-output ""$localshare"""`
-Wait
Get-ChildItem -Path $localshare -Filter '*.JSON' | Rename-Item -NewName {$_.FullName+$uncshare}
}
# This section copies the output *.JSON file of the xxx to the share, where I they will be uploaded to DB.
Get-ChildItem -Path $localshare -Filter '*.JSON' | Where {$_.Length -ge 3} | move-item -Destination '$uploadshare'
the fileremediation_1st.csv looks like
sharefolder
\\server\sharename
Can someone please help me on this, I don´t have a clue what I´m doing wrong.
Thank you!
Current error I get is
Rename-Item : Cannot rename the specified target, because it
represents a path or device name. At
C:\temp\FileRemediation\scripts\fileremediation_V2_1st.ps1:28 char:55
+ ... share -Filter '*.JSON' | Rename-Item -NewName {$_.FullName+$uncshare}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
As said before, I would also be fine with a dedicated range of numbers, which is append to the file name "Report_Username_Hostname(100).vba.JSON"
The perfect world would be if I could split the \server\sharename from csv file and append the sharename to my filename.
I think the issue is with:
Rename-Item -NewName {$_.FullName+$uncshare}
Your input file (Get-ChildItem) path is:
$PSpath = 'C:\temp\FileRemediation\Scripts\'
$localshare = $PSpath +'..\temp\1st'
The Rename-Item uses $_.FullName which resolves to something like this:
C:\temp\FileRemediation\Scripts\..\temp\1st\MyFile.JSON
The variables then contain:
$_.FullName = C:\temp\FileRemediation\Scripts\..\temp\1st\MyFile.JSON
$uncshare = "\\server\sharename"
So Your Rename-Item ... $_.FullName+$uncshare will try to rename it to:
C:\temp\FileRemediation\Scripts\..\temp\1st\MyFile.JSON\\server\sharename
Which is not a valid path.
Testing if a folder is empty is pretty simple and has been discussed many times. Mostly using test-path or get-childitem
Example: Powershell test if folder empty
I'm working on a script for auditing and correcting file NTFS permissions on SMB shares in my enterprise environment.
I've run into something that would at first appear simple but I'm not able to find a simple solution. I'm wondering if there is a cmdlet that would help.
That is, differentiating between if a given folder is empty or I don't have access to it.
The problem is all the solutions that I see assume that you have access to the folder.
I was thinking that I was going to have to write a greasy function to do multiple tests then catching and testing error codes or something.
But I thought I would ask the smart coders here at Stack Overflow before going down that rabbithole.
Is there a more elegant way to test this? A Test-MyAccess cmdlet or something?
Test-Path returns the same results for empty folders and folders with no access.
Get-childitem returns null/empty for empty folders and folders with no access. Except a no access folder has an error.
PS H:\PowerShell\NTFS> Test-Path $EmptyFolder
True
PS H:\PowerShell\NTFS> Test-Path $EmptyFolder\*
False
PS H:\PowerShell\NTFS> Test-Path $NoAccessFolder
True
PS H:\PowerShell\NTFS> Test-Path $NoAccessFolder\*
False
PS H:\PowerShell\NTFS> Get-ChildItem -Path $EmptyFolder
PS H:\PowerShell\NTFS> Get-ChildItem -Path $NoAccessFolder
Get-ChildItem : The specified network name is no longer available.
At line:1 char:1
+ Get-ChildItem -Path $NoAccessFolder
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ReadError: (\\server.domain....\Sufolder1\Sub3:String) [Get-ChildItem], IOException
......
Thanks to #Lee_Dailey I seem to have been going down the right track to begin with. It looks like the way is to check Get-ChildItem for errors.
Seems like there should be a cmdlet to do this but I guess I'll write a function.
Something like this I imagine.
Function Get-FolderProperties ($Path){
$ErrOut = $Null
$Folder = Get-ChildItem -Path $Path -ErrorAction SilentlyContinue -ErrorVariable ErrOut
If($ErrOut) {
# There was an error accessing $path. Do something with $ErrOut
}
}
I am recursing a deep folder structure in order to retreive all folder paths like so:
$subFolders = Get-ChildItem $rootFolder -Recurse -Directory -ErrorVariable folderErrors | Select-Object -ExpandProperty FullName
NOTE: $rootFolder in my case is a network share. i.e. "\\server\DeptDir$\somefolder"
The $folderErrors variable is correctly capturing all the FileTooLong exceptions so I want to create new PSDrives using the long Paths in order to recurse those long paths.
So I create a new PSDrive using this cmdlet:
new-psdrive -Name "long1" -PSProvider FileSystem -Root $folderErrors[0].CategoryInfo.TargetName
However, after creating a new PSDrive I am still getting PathTooLong Exceptions.
PS C:\>> cd long1:
PS long1:\>> dir
dir : 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
+ dir
+ ~~~
+ CategoryInfo : ReadError: (\\svr01\Dep...\Fibrebond ECO\:String) [Get-ChildItem], PathTooLongException
+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand
I see no other way around this problem. Am I doing something incorrectly? Why is the new PSDrive throwing PathTooLong when I am creating a drive at the location where the path is too long?
Thanks
There is a local policy that is now available since Windows anniversary update.
Requirements are :
Windows Management Framework 5.1
.Net Framework 4.6.2 or more recent
Windows 10 / Windows server 2016 (Build 1607 or newer)
This policy can be enabled using the following snippet.
#GPEdit location: Configuration>Administrative Templates>System>FileSystem
Set-ItemProperty 'HKLM:\System\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -value 1
Otherwise, you can actually get to the paths longer than 260 characters by making your call to the unicode version of Windows API.
There's a catch though.
This work only in Powershell 5.1 minimum.
From there, instead of making your call the standard way:
get-childitem -Path 'C:\Very long path' -Recurse
You will need to use the following prefix:
\\?\
Example
get-childitem -LiteralPath '\\?\C:\Very long path' -Recurse
For UNC path, this is slightly different, the prefix being \\?\UNC\ instead of \\
get-childitem -LiteralPath '\\?\UNC\127.0.0.1\c$\Very long path\' -Recurse
Important
When calling Get-ChildItem unicode version, you should use the -LiteralPath parameter instead of Path
From Microsoft documentation
-LiteralPath
Specifies a path to one or more locations. Unlike the -Path parameter, the value of the -LiteralPath parameter is used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose them in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any characters as escape sequences.
source
Example
(get-childitem -LiteralPath '\\?\UNC\127.0.0.1\This is a folder$' -Recurse) |
ft #{'n'='Path length';'e'={$_.FullName.length}}, FullName
output
Here is the actual functional test I made to create the very long repository, query it to produce the output above and confirm I could create repository with more than 260 characters and view them.
Function CreateVeryLongPath([String]$Root,[Switch]$IsUNC,$FolderName = 'Dummy Folder',$iterations = 200) {
$Base = '\\?\'
if ($IsUNC) {$Base = '\\?\UNC\'}
$CurrentPath = $Base + $Root + $FolderName + '\'
For ($i=0;$i -le $iterations;$i++) {
New-Item -Path $CurrentPath -Force -ItemType Directory | Out-Null
$currentPath = $CurrentPath + $FolderName + '\'
}
}
Function QueryVeryLongPath([String]$Root,[Switch]$IsUNC) {
$Base = '\\?\'
if ($IsUNC) {$Base = '\\?\UNC\';$Root = $Root.substring(2,$Root.Length -2)}
$BasePath = $Base + $Root
Get-ChildItem -LiteralPath $BasePath -Recurse | ft #{'n'='Length';'e'={$_.FullName.Length}},FullName
}
CreateVeryLongPath -Root 'C:\__tmp\' -FolderName 'This is a folder'
QueryVeryLongPath -Root 'C:\__tmp\Dummy Folder11\'
#UNC - tested on a UNC share path
CreateVeryLongPath -Root '\\ServerName\ShareName\' -FolderName 'This is a folder' -IsUNC
QueryVeryLongPath -Root '\\ServerName\ShareName\' -IsUNC
Worth to mention
During my research, I saw people mentioning using RoboCopy then parse its output. I am not particularly fond of this approach so I won't elaborate on it.
(edit: Years later, I discovered that Robocopy is part of Windows and not some third-party utility. I guess that would be an ok approach too, although I prefer a pure Powershell solution)
I also saw AlphaFS being mentionned a couple of time which is a library that allows to overcome the 260 characters limitation too. It is open sourced on Github and there's even a (I did not test it though) Get-AlphaFSChildItem built on it available on Technet here
Other references
Long Paths in .Net
Naming Files, Paths, and Namespaces
I have a simple powershell script which is supposed to ease some deployment tasks.
In an earlier portion of the script, I create a virtual drive mapped to Z: which is on a remote server. The part that is tripping up is when it tries to unzip the files on the remote server mapped to Z:
function UnzipBuild($destinationFolder)
{
Add-Type -assembly "System.IO.Compression.Filesystem"
$zipFiles = Get-ChildItem -Path $destinationFolder -Filter *.zip
foreach($zip in $zipFiles)
{
$folderName = $zip.ToString().TrimEnd(".zip")
$extractPath = Join-Path $destinationFolder $folderName
New-Item -ItemType Directory $extractPath
Write-Host "Extracting $zip to $extractPath `r`n"
[io.compression.zipfile]::ExtractToDirectory([string]$zip.FullName, "$extractPath")
}
}
When it reaches the ::ExtractToDirectory line it throws an exception
Hit Line breakpoint on 'D:\MyDeploymentScript.ps1:85'
[DBG]: PS C:\WINDOWS\system32>>
Exception calling "ExtractToDirectory" with "2" argument(s): "Could not find a part of the path
'Z:\Build_11_17_13_28\Web'."
At D:\MyDeploymentScript.ps1:85 char:9
+ [io.compression.zipfile]::ExtractToDirectory([string]$zip.FullName, "$ex ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DirectoryNotFoundException
But the crazy thing is if I breakpoint that same spot and check the path with Test-Path it returns True. I am at a loss for what can be wrong right now.
[DBG]: PS C:\WINDOWS\system32>> Test-Path Z:\Build_11_17_13_28\Web
True
It seems like you might be mapping your drive in the earlier portion of the script with New-PSDrive. The drive created with that cmdlet is only visible within PowerShell, unless you use the -Persist switch. That switch creates it as an actual mapped drive (as though you had used net use or group policy or mapped it through explorer).
The .ExtractToDirectory method you're calling can't see any of the powershell provider namespaces, so it needs a real mapped drive or UNC path that's visible to the whole operating system.
Remember that if you're using -Persist you may also want to unmap the drive manually now.
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