System Variable path is different in command prompt - ruby

I've seen different kind of problems around, but I'm fighting with this for a whole day, so please give me some help :)
Short story: I have different PATH variable in System Variables panel and cmd. Can't run exe files?
Longer story: I'm trying to install Ruby. Tried with different versions, but the problem is the same: I have my PATH value updated, Ruby is there. BUT when using it from cmd, ruby is not recognized. echo %PATH% gives different value from the one in Environment Variables panel!
First attachment: Environment Variables panel:
Values for user variables:
C:\Users\morifey\nvmw\nodejs\v0.10.36;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;"C:\Program Files\Intel\WiFi\bin\";"C:\Program Files\Common Files\Intel\WirelessCommon\";"C:\Program Files (x86)\Skype\Phone\";"C:\Program Files (x86)\Intel\OpenCL SDK\3.0\bin\x86";"C:\Program Files (x86)\Intel\OpenCL SDK\3.0\bin\x64";"C:\Program Files\nodejs\";C:\wamp\bin\php\php5.5.12;C:\ProgramData\ComposerSetup\bin;"C:\Program Files\TortoiseGit\bin";"C:\Program Files (x86)\Git\cmd";"C:\Program Files\Intel\WiFi\bin\";"C:\Program Files\Common Files\Intel\WirelessCommon\";C:\Users\morifey\AppData\Roaming\npm;C:\Ruby21\bin
Value for system variables:
C:\Ruby21\bin;C:\Users\morifey\nvmw\nodejs\v0.10.36;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;"C:\Program Files\Intel\WiFi\bin\";"C:\Program Files\Common Files\Intel\WirelessCommon\";"C:\Program Files (x86)\Skype\Phone\";"C:\Program Files (x86)\Intel\OpenCL SDK\3.0\bin\x86";"C:\Program Files (x86)\Intel\OpenCL SDK\3.0\bin\x64";"C:\Program Files\nodejs\";C:\wamp\bin\php\php5.5.12;C:\ProgramData\ComposerSetup\bin;"C:\Program Files\TortoiseGit\bin";"C:\Program Files (x86)\Git\cmd";"C:\Program Files\Intel\WiFi\bin\";"C:\Program Files\Common Files\Intel\WirelessCommon\";"C:\Users\morifey\AppData\Roaming\npm";%SystemRoot%\system32;%SystemRoot%
I compare them to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment - they are all the same.
BUT I run cmd and use echo %PATH%. Results:
C:\Users\morifey\nvmw\nodejs\v0.10.36;C:\ProgramData\Oracle\Java\javapath;C:\Win
dows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPow
erShell\v1.0\;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Int
el\WirelessCommon\;C:\Program Files (x86)\Skype\Phone\;C:\Program Files (x86)\In
tel\OpenCL SDK\3.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\3.0\bin\x64;C
:\Program Files\nodejs\;C:\wamp\bin\php\php5.5.12;C:\ProgramData\ComposerSetup\b
in;C:\Program Files\TortoiseGit\bin;C:\Program Files (x86)\Git\cmd;C:\Program Fi
les\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;C:\Users
\morifey\AppData\Roaming\npm
Ruby is missing! It's the same for both run as administrator and not (actually I have single administrator user).
I've tried setting path (via set or setx), changing both registries AND environmental variables - nothing is working. I've tried adding quotes (") in paths that have spaces - no difference.
Except if I run cmd as administrator and use setx PATH "%PATH%;C:\Ruby21\bin"! Then, echo %PATH% returns the real (updated) path, and I am able to run ruby ONLY in this cmd. If I close it, and reopen it again as admin - changes are lost.
Please give me any kind of advise how to deal with this? I just need to run Ruby :)
Windows 7 Ultimate, Service Pack 1, 64-bit;
Downloaded Ruby (from http://rubyinstaller.org/downloads/) - Ruby 2.2.2 (32 & 64 bit), Ruby 2.1.6 (32 & 64 bit) - results are all the same.
Edit: After few guys said I don't need quotes, I've removed them, and placed exactly what #eryksun said. This is the result:
C:\Users\morifey>echo %PATH%
C:\Users\morifey\nvmw\nodejs\v0.10.36;C:\ProgramData\Oracle\Java\javapath;C:\Win
dows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPow
erShell\v1.0\;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Int
el\WirelessCommon\;C:\Program Files (x86)\Skype\Phone\;C:\Program Files (x86)\In
tel\OpenCL SDK\3.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\3.0\bin\x64;C
:\Program Files\nodejs\;C:\wamp\bin\php\php5.5.12;C:\ProgramData\ComposerSetup\b
in;C:\Program Files\TortoiseGit\bin;C:\Program Files (x86)\Git\cmd;C:\Program Fi
les\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;C:\Users
\morifey\AppData\Roaming\npm
It's pretty weird some of the inputs are repeating:
C:\Program Files\Intel\WiFi\bin\;
C:\Program Files\Intel\WiFi\bin\;
C:\Program Files\Common Files\Intel\WirelessCommon\;
C:\Program Files\Common Files\Intel\WirelessCommon\;
C:\Program Files (x86)\Intel\OpenCL SDK\3.0\bin\x86;
C:\Program Files (x86)\Intel\OpenCL SDK\3.0\bin\x64;
despite they are not like that in the lines I've pasted in the System Variables Path!
Update - I've used PowerShell and this script to check if there is a repetition in the system variables. I'm uploading image with the result. It's pretty strange that the path is exactly what should be, but as you can see, in CMD I see different thing!
Update - Today I tried running Ruby from PowerShell - all worked out. So it seems like only CMD doesn't know what's going on! And just to remind you - rebooting does not help! :)

Turned out to be an issue with a bat file that run each time I started cmd, and it modified it's variables! Thanks to #eryksun and #ılǝ - it seems that in the registry (HKCU\Software\Microsoft\Command Processor\AutoRun) you might have something like that. My problem was with nvmw package (https://www.npmjs.com/package/nvmw) that when installed, created this .bat file and stored my current PATH variable. Then it was setting it to the cmd every time I use it, and so it never got updated.
You can see the issue I've created here: https://github.com/nanjingboy/nvmw/issues/5
SHORT STORY: check your HKCU\Software\Microsoft\Command Processor\AutoRun, and DO NOT USE NVMW package!

If your system variable PATH length is longer than some value, it is truncated in cmd prompt. According to this answer this value is 2047. Remove duplicates and trim your path, you will see that both variables will be same.

When you use SET operand, it changes environment variables only of current console, it is that what it for. To change environment variables permanent, you should use command SETX. And reboot windows. The
Technicaly, you can not reboot, just restart all services, but easily reboot.

I do software packaging for automatic deployment, and sometimes there are applications that need their folder setup in %PATH% and for that I use this Powershell script.
It checks to see if the folder is there already (so you won't have duplicates) and also removes it if needed (ie for silent uninstall).
You use ADD-PATH to add a folder and REMOVE-PATH to remove it.
Works like a charm.
#REQUIRES -Version 3.0
<#
.SYNOPSIS
A script to add or remove folders to %PATH%
.DESCRIPTION
It contains 2 functions, one for adding and one for removing. It reads the registry and does the modifications there.
.NOTES
File Name : AddRemovePath.ps1
Author : Iulian Dita (iulian.dita#gmail.com)
Prerequisite : PowerShell V3 over Vista and upper.
Copyright 2015 - Iulian Dita
.LINK
Script located at:
\\%server%\dsm2$\Work\Master\Projects\29532\Extern$
.VERSION
0.7
.VERSION_HISTORY
0.1 Initial version
0.2 Bug fixes
0.3 Cosmetic fixes, automatic removal of preceding ";"
0.4 Fixed double entries
0.5 Kill Explorer and CMD processes before making any modifications
0.6 Check if folder to be removed already exists in PATH
Escaping special characters no longer needed for the removal function
0.7 Cleaned the code and removed some syntax missmatches
Included the sendmessage function to avoid killing the explorer task
Used [reges]::escape() to avoid running into troubles with the path-string and -match methode
Contributed by Maik Krammer
#>
if (-not ("win32.nativemethods" -as [type])) {
# import sendmessagetimeout from win32
add-type -Namespace Win32 -Name NativeMethods -MemberDefinition #"
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"#
}
$HWND_BROADCAST = [intptr]0xffff;
$WM_SETTINGCHANGE = 0x1a;
$result = [uintptr]::zero
function global:ADD-PATH
{
[Cmdletbinding()]
param (
[parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0)]
[string] $Folder
)
# See if a folder variable has been supplied.
if (!$Folder -or $Folder -eq "" -or $Folder -eq $null) {
throw 'No Folder Supplied. $ENV:PATH Unchanged'
}
# Get the current search path from the environment keys in the registry.
$oldPath=$(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path
# See if the new Folder is already in the path.
if ($oldPath | Select-String -SimpleMatch $Folder){
return 'Folder already within $ENV:PATH'
}
# Set the New Path and add the ; in front
$newPath=$oldPath+';'+$Folder
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath -ErrorAction Stop
# Show our results back to the world
return 'This is the new PATH content: '+$newPath
# notify all windows of environment block change
[win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [uintptr]::Zero, "Environment", 2, 5000, [ref]$result)
}
function global:REMOVE-PATH {
[Cmdletbinding()]
param (
[parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0)]
[String] $Folder
)
# See if a folder variable has been supplied.
if (!$Folder -or $Folder -eq "" -or $Folder -eq $NULL) {
throw 'No Folder Supplied. $ENV:PATH Unchanged'
}
# add a leading ";" if missing
if ($Folder[0] -ne ";") {
$Folder = ";" + $Folder;
}
# Get the Current Search Path from the environment keys in the registry
$newPath=$(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path
# Find the value to remove, replace it with $NULL. If it's not found, nothing will change and you get a message.
if ($newPath -match [regex]::Escape($Folder)) {
$newPath=$newPath -replace [regex]::Escape($Folder),$NULL
} else {
return "The folder you mentioned does not exist in the PATH environment"
}
# Update the Environment Path
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath -ErrorAction Stop
# Show what we just did
return 'This is the new PATH content: '+$newPath
# notify all windows of environment block change
[win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [uintptr]::Zero, "Environment", 2, 5000, [ref]$result)
}
# Use ADD-PATH or REMOVE-PATH accordingly.
#Anything to Add?
ADD-PATH "%_PATH_TO_BE_ADDED%"
#Anything to Remove?
ADD-PATH "%_PATH_TO_BE_REMOVED%"
I didn't create this script from scratch, I found pieces on line and adapted them to my needs.
EDIT: I updated the code a bit, you no longer have to escape the backslashes or care about ; in front, and also other useful stuff.

Could be an issue with the Command Prompt autorun (command prompt setting the PATH on run):
https://superuser.com/questions/727316/error-in-command-line-the-system-cannot-find-the-path-specified

Related

Powershell dir command options not working

I was trying to use dir command to list recursively all files that end with .cpp in a given directory, I tried to follow various solutions but my powershell seems not to accept any options after '/' sign as seen on the picture bellow:
Example
The command I initially tried was 'dir sourcefolder "*.cpp"' but it only lists files in a given folder (because I cant provide any additional options as seen in microsoft doc), also any example command provided there does not work for me giving the same error as shown in example above.
here is how I will bring out all the files in .cpp.
Here is a small program in powershell :
$path = "C:\temp\"
$filter = "*.cpp"
$files = Get-ChildItem -Path $path -Filter $filter
Write-Host "here, all the .cpp files in '$path' :"
Write-Host $files -Separator "`r`n"
I prefer to use the cmdlet "Get-ChildItem" rather than "dir".
Here the content folder for my test
And, why so many / ?

Windows PowerShell - Input file name, output file path

I've just started using PowerShell and I have a task where I need to be able to have the file path displayed on screen when I enter the file name.
Is there a script that allows me to do the below ? :
Ex 1: I enter "test.txt" and I get "C:\Program Files...."
Ex 2: I enter a file name "My Documents" and I also get its path.
I have searched online on how to do this but I didn't quite find what I was looking for and all the queries/answers were too complicated for me to understand.
Can anyone help me out, please?
Thanks in advance!
Here is a starter sample for you.
This example search only within the confine of the paths present is the Path system environment variable. It also only looks for files and do not recurse through these path.
So anything you could access directly from the command line should be available to you through it.
Now, if you want to search the whole drive, you could replace the $DefaultPaths assignment with Get-ChildItem -Path 'C:' -Recurse but doing that each time won't be super efficient.
You could do it and it will work... but it will be slow.
To search on the whole drive or whole filesystem, there are alternative methods that might work better. Some examples of what might entice:
Using a database which you have to buld & maintain to index all the files so that when you search, results are instantaneous and / or very fast
Parsing the MFT table (if using Windows / NTFS filesystem only) instead of using Get-ChildItem (This is not somehting natively doable through a simple cmdlet though) .
Relying on a third party software and interface with (For example, Void Tools Everything search engine already parse MFT and build its own database, allowing users to search instantly through a Windows NTFS filesystem. It also have its own SDK you can plug in through Powershell and retrieve what you seek instantly. The caveats is that you need the software installed first for that solution to work.)
Example: Searching through all paths defined in the Path variable
# What you are looking for. Accept wildcards characters (*)
$Filter = 'notepad.exe'
# Get the System Environment Path variable in an array
$DefaultPaths = $env:Path -split ';'
$Paths =
Foreach ($P in $DefaultPaths) {
# Search for files matching the specified filter. Ignore errors (often if the path do not exist but is sin the Path)
$MatchingFiles = Get-ChildItem -Path $P -Filter $Filter -File -ErrorAction SilentlyContinue
if ($MatchingFiles.count -gt 0) {
$MatchingFiles.Directory.FullName
}
}
$Paths | out-string | Write-Host -ForegroundColor Cyan
Output for Notepad.exe search using this method.
C:\Windows\system32
C:\Windows

Archive all files of a certain type recursively from powershell

Is there a way to use Compress-Archive script, that when run from a path:
archives all files matching a wildcard filter (*.doc, for example)
archives such files in the current folder and all children folders
save the relative folder structure (the option to use relative or absolute would be good, though)
I am having trouble have it accomplish all three of these at once.
Edit:
The following filters and recurses, but does not maintain folder structure
Get-ChildItem -Path ".\" -Filter "*.docx" -Recurse |
Compress-Archive -CompressionLevel Optimal -DestinationPath "$pwd\doc.archive-$(Get-Date -f yyyyMMdd.hhmmss).zip"
This item does not recurse:
Compress-Archive -Path "$pwd\*.docx" -CompressionLevel Optimal -DestinationPath "$pwd\doc.archive-$(Get-Date -f yyyyMMdd.hhmmss).zip"
At some point I had a command that would recurse but not filter, but can't get back to it now.
Unfortunately, Compress-Archive is quite limited as of Windows PowerShell v5.1 / PowerShell Core 6.1.0:
The only way to preserve a subdirectory tree is pass a directory path to Compress-Archive.
Unfortunately, doing so provides no inclusion/exclusion mechanism to only select a subset of files.
Additionally, the resulting archive will internally contain a single root directory named for the input directory (e.g., if you pass C:\temp\foo to Compress-Archive, the resulting archive will contain a single foo directory containing the input directory's subtree - as opposed to containing C:\temp\foo's content at the top level).
There is no option to preserve absolute paths.
A cumbersome work around is to create a temporary copy of your directory tree with only the files of interest (Copy-Item -Recurse -Filter *.docx . $env:TEMP\tmpDir; Compress-Archive $env:TEMP\tmpDir out.zip - note that empty dirs. will be included)
Given that you'll still invariably end up with a single root directory named for the input directory inside the archive, even that may not work for you - see the alternatives at the bottom.
You may be better off with alternatives:
Use the .NET v4.5+ [System.IO.Compression.ZipFile] and [System.IO.Compression.ZipFileExtensions] types directly.
In Windows PowerShell, unlike in PowerShell Core (v6+), you most load the relevant assembly manually with Add-Type -AssemblyName System.IO.Compression.FileSystem - see below.
Use an external programs such as 7-Zip
Solving the problem with direct use of the .NET v4.5+ [System.IO.Compression.ZipFile] class:
Note:
In Windows PowerShell, unlike in PowerShell Core, you most load the relevant assembly manually with Add-Type -AssemblyName System.IO.Compression.FileSystem.
Because PowerShell doesn't support implicit use of extension methods as of Windows PowerShell v5.1 / PowerShell Core 6.1.0, you must make explicit use of the [System.IO.Compression.ZipFileExtensions] class as well.
# Windows PowerShell: must load assembly System.IO.Compression.FileSystem manually.
Add-Type -AssemblyName System.IO.Compression.FileSystem
# Create the target archive via .NET to provide more control over how files
# are added.
# Make sure that the target file doesn't already exist.
$archive = [System.IO.Compression.ZipFile]::Open(
"$pwd\doc.archive-$(Get-Date -f yyyyMMdd.hhmmss).zip",
'Create'
)
# Get the list of files to archive with their relative paths and
# add them to the target archive one by one.
$useAbsolutePaths = $False # Set this to true to use absolute paths instead.
Get-ChildItem -Recurse -Filter *.docx | ForEach-Object {
# Determine the entry path, i.e., the archive-internal path.
$entryPath = (
($_.FullName -replace ([regex]::Escape($PWD.ProviderPath) + '[/\\]'), ''),
$_.FullName
)[$useAbsolutePaths]
$null = [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile(
$archive,
$_.FullName,
$entryPath
)
}
# Close the archive.
$archive.Dispose()

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

Recursive "touch" on fileserver

There's something I want to accomplish with administrating my Windows file server:
I want to change the "Last Modified" date of all the folders on my server (just the folders and subfolders, not the files within them) to be the same as the most recent "Created" (or maybe "Last Modified") date file within the folder. (In many cases, the date on the folder is MUCH newer than the newest file within it.)
I'd like to do this recursively, from the deepest subfolder to the root. I'd also like to do this without me manually entering any dates and times.
I'm sure with a combination of scripting and a Windows port of "touch" I could maybe accomplish this. Do you have any suggestions? I could maybe accomplish this. Do you have any suggestions?
This closed topic seems really close but I'm not sure how to only touch folders without touching the files inside, or how to get the most-recent-file's date. Recursive touch to fix syncing between computers
If it's for backup purposes, in Windows there is the Archive flag (instead of modifying the timestamp). You can set it recursively with ATTRIB /S (see ATTRIB /?)
If it is for other purposes you can use some touch.exe implementation and use a recursive for:
FOR /R (see FOR /?)
http://ss64.com/nt/for_r.html
http://ss64.com/nt/touch.html
I think that you can do this in PowerShell. I just tried throwing something together and it seems to work correctly. You could invoke this in PowerShell using Set-DirectoryMaxTime(".\Directory") and it will operate recursively on each directory under that.
function Set-DirectoryMaxTime([System.IO.DirectoryInfo]$directory)
{
# Grab a list of all the files in the directory
$files = Get-ChildItem -File $directory
# Get the current CreationTime of the directory we are looking at
$maxdate = Get-Date $directory.CreationTime
# Find the most recently edited file's LastWriteTime
foreach($file in $files)
{
if($file.LastWriteTime -gt $maxdate) { $maxdate = $file.LastWriteTime }
}
# This needs to be in a try/catch block because there is a reasonable chance of it failing
# if a folder is currently in use
try
{
# Give the directory a LastWriteTime equal to the newest file's LastWriteTime
$directory.LastWriteTime = $maxdate
} catch {
# One of the directories could not be updated
Write-Host "Could not update directory: $directory"
}
# Get all the subdirectories of this directory
$subdirectories = Get-ChildItem -Directory $directory
# Jump into each of the subdirectories and do the same thing to each of their CreationTimes
foreach($subdirectory in $subdirectories)
{
Set-DirectoryMaxTime($subdirectory)
}
}

Resources