How in the heck did you create filenames longer than MAX_PATH? I've tried
everything I can think of, short of writing a C++ program that directly
connects to the NTFS FS.
Anyway, this does much of what you want, with that exception. It takes a LONG
time for large numbers of files. That is, a million or more.
[ CmdletBinding() ]
Param(
[string] $item = 'C:\'
)
function timer
{
( Get-Date ).ToLongTimeString()
}
function wv
{
Param(
[string] $str
)
Write-Verbose "$( timer ) $str"
}
## we only need to create this once
$md5 = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
function GetMyHash
{
Param(
[object] $fileObject
)
$function = 'GetMyHash:'
wv "$function begin, fileobject name '$( $fileObject.FullName )'"
if( $fileObject.NameLength -ge 256 )
{
## here you should write a helper function!!!
Write-Error "$function, filename too long '$(
$fileObject.FullName )'"
}
else
{
# you can compress this to a single line, but if you do that,
you
# can't clean up resources. if you are doing hundreds of
thousands
# or millions of files -- you gotta clean up
# $md5 is declared at the script level
try
{
[System.IO.FileStream] $file = [System.IO.File]::Open(
$fileObject.FullName, 'Open', 'Read' )
$fileObject.Hash = [BitConverter]::ToString(
$md5.ComputeHash( $file ) )
$file.Close()
$file.Dispose()
$file = $null
}
catch
{
}
# $md5.Clear() --- don't do this
}
wv "$function exit"
}
function GetFileList
{
Param(
[string] $item
)
$function = 'GetFileList:'
wv "$function begin, item '$item'"
$params = New-Object System.Collections.Arraylist
$params.AddRange( @(
'/L',
'/S', # include subdirectories, but not empty ones
'/NJH', # no job header (no logo)
'/BYTES', # show filesizes in bytes
'/FP', # include full filename of file in path output
'/NC', # don't log file classes
'/NDL', # don't log directory names
'/TS', # include source filename timestamps
'/XJ', # exclude junction points
'/R:0', # number of retries on failed I/O
'/W:0' ) ) # how long to wait between retries
$countPattern = '^\s{3}Files\s:\s+(?<Count>\d+).*'
$sizePattern = '^\s{3}Bytes\s:\s+(?<Size>\d+(?:\.?\d+)\s[a-z]?).*'
wv "$function initiating robocopy"
$roboResults = robocopy.exe $item Nothing $params
wv "$function robocopy complete"
wv "$function result from robocopy has $( $roboResults.Count ) results"
[int] $count = 0
foreach( $result in $roboResults )
{
$count++
if( ( $count % 1000 ) -eq 0 )
{
wv "$function count $count of $( $roboResults.Count )"
}
if( $result -match
'(?<Size>\d+)\s(?<Date>\S+\s\S+)\s+(?<FullName>.*)' )
{
try
{
New-Object PSObject -Property @{
FullName = $matches.FullName
NameLength = $matches.FullName.Length
Size = $matches.Size
Date = [Datetime]$matches.Date
Hash = ''
}
}
catch
{
wv "fault: line = '$result'"
wv "fault: fullname = '$( $matches.FullName )'"
wv "fault: namelen = '$(
$matches.FullName.Length )'"
wv "fault: size = '$(
$matches.Size.ToString() )'"
wv "fault: date = '$( $matches.Date )'"
}
}
else
{
wv "$function nomatch: $result $( if( $result ) {
$result.Length } else { 'null' } )"
}
}
wv "$function exit, count = $count"
}
##
## Main
##
$global:fsiresult = GetFileList $item
wv "Gonna calculate hashes"
$global:fsiresult |% { GetMyHash $_ }
wv "Done"
-----Original Message-----
From: [email protected] [mailto:[email protected]] On
Behalf Of Kurt Buff
Sent: Wednesday, December 30, 2015 5:20 PM
To: [email protected]
Subject: Re: [powershell] Long file names - again...
Ultimate goal is still the same - I want to get fullname, length and a hash of
each file (md5 or sha1, for duplicate detection only), so AFAICT PowerShell is
still the way to go.
Unless I can use md5sum from unxtools or gnuwin32 toolsets, or something like
that.
Not worried about junctions at this point. I probably should look at
New-PsDrive for grins.
I'll also take a look at .NET CORE - though I doubt I can take advantage of it
if it hasn't been made easily available to powershell.
Kurt
On Wed, Dec 30, 2015 at 1:47 PM, Michael B. Smith <[email protected]> wrote:
> Actually, in this case, I think it's the .NET MAX_PATH (260).
>
> .NET itself, in its PathHelper class, doesn't accept the '\\?\' syntax and
> explicitly throws an exception if the path exceeds 259 (ANSI) or 129
> (Unicode). This class is used all over .NET.
>
> Now, .NET CORE has been enhanced to support long paths. But that isn't
> today... for most people.
>
> There are a few libraries (third party) you can use to add this support to
> PowerShell. They basically wrap the low-level APIs using P/Invoke (which you
> can do yourself, with Add-Type). They are much lower impact than installing
> Cygwin, but still... I guess my question is this: what are you really trying
> to do?
>
> Cmd.exe and the subst command are still my "go to solution" for long paths.
> You can also fake this in PowerShell with New-PsDrive (or "net share...") if
> you absolutely must stay in PowerShell, but it gets a little nasty if you
> need to worry about junctions.
>
> -----Original Message-----
> From: [email protected]
> [mailto:[email protected]] On Behalf Of Kurt Buff
> Sent: Wednesday, December 30, 2015 4:13 PM
> To: [email protected]
> Subject: [powershell] Long file names - again...
>
> All,
>
> If anyone can help, I'd much appreciate it.
>
> I'm picking up where I left off some time ago in auditing our file server.
>
> I used robocopy to generate a list of files for each drive on our file server
> - all told over 10.3m lines, massaged the output (with findstr) to break it
> up by drive letter and to remove directories and things like $recycle.bin and
> 'system volume', then further massaged the output to remove the extraneous
> robocopy markings. I had to break it into smaller files by partition because
> processing the file in powershell overran RAM on a 16g machine.
>
> I then took each line (which looked like, e.g.
> i:\somedirectory\otherdirectory\file), then prepended '\\?\' to each
> line), because some number of the files have path lengths greater than
> 260 characters, and I'm hoping that using this specification will allow
> access to those files without adding funky 3rd party tools.
>
> So, I've ended up with a set of text files that have many lines that look
> like this:
> \\?\i:\somedirectory\otherdirectory\file
>
> What I'm trying to do is illustrated by the following, but I'm getting no
> output from it - it just returns without any output after a few moments.
>
> $files = get-content c:\batchfiles\file-i.txt
> foreach ( $file in $files )
> {
> get-childitem $file | select length, fullname
> }
>
>
> However, if I strip the '\\?\' from each line, it does what I want - but of
> course the script fails as soon as it encounters a file that has a
> name/directory specification that exceeds the Win32 API limit.
>
> I've tried surrounding the string with both double and single quotes, and
> still no joy.
>
> A simpler example tells the tale:
>
> This works, except for long file names:
> gci i:\somedirectory\otherdirectory\file
>
> These fail silently:
> gci \\?\i:\somedirectory\otherdirectory\file
> gci "\\?\i:\somedirectory\otherdirectory\file"
> gci '\\?\i:\somedirectory\otherdirectory\file'
>
> These fail with an error:
> gci "\\\\?\\i:\somedirectory\otherdirectory\file"
> gci '\\\\?\\i:\somedirectory\otherdirectory\file'
>
> The error is:
> Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet.
> Cannot process argument because the value of argument "path" is not valid.
> Change the value of the "path" argument and run the operation again.
> At line:1 char:1
> + gci "\\\\?\\i:\CFRemoteImages\Air Canada Montreal STOC.vhd" | select
> length, ful ...
> + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + CategoryInfo : InvalidArgument: (:) [Get-ChildItem],
> ParameterBindingException
> + FullyQualifiedErrorId :
> GetDynamicParametersException,Microsoft.PowerShell.Commands.GetChildIt
> emCommand
>
>
> ================================================
> Did you know you can also post and find answers on PowerShell in the forums?
> http://www.myitforum.com/forums/default.asp?catApp=1
>
>
> ================================================
> Did you know you can also post and find answers on PowerShell in the forums?
> http://www.myitforum.com/forums/default.asp?catApp=1
================================================
Did you know you can also post and find answers on PowerShell in the forums?
http://www.myitforum.com/forums/default.asp?catApp=1
================================================
Did you know you can also post and find answers on PowerShell in the forums?
http://www.myitforum.com/forums/default.asp?catApp=1