Great explanations and solutions from Mofi
Here is a bit shorter and more direct way to do achieve a reliable solution that still relies on Mofi's fundamental technique of using the FOR /F
tokens
and delims
to strip out the root path.
My code allows no specified path, in which case it defaults to the current directory. The code to normalize, validate, and extract the needed values from the root path is greatly simplified.
I also use a totally different method for counting the number of tokens in the root path. I substitute a newline for the \
character and then count the number of lines with FIND /C
when I echo the value. It is a bit tricky, but it is very efficient and works without resorting to a GOTO
loop.
I also preserve the error message if no files are found, and don't bother deleting the resultant empty file.
@echo off
setlocal disableDelayedExpansion
::==== Get normalized values for supplied path in %1
::==== Default to current directory if not specified
for %%F in ("%~f1.") do ( %=== The trailing dot allows paths with/without trailing \ to work %
set "folder=%%~fF" ==== The normalized path for the root folder
set "archive=%%~nxF" ==== The base name of the output file
set "att=-%%~aF" ==== List of attributes for the root folder
)
::==== Validate path by checking for d in attributes
if %att:d=% == %att% (
>&2 echo Supplied path is invalid or not a folder
exit /b 1
)
::==== Special handling if drive root
if not defined archive (
set "archive=drive %folder:~0,1% root"
set cnt=1
goto start
)
::==== Normal processing for all other paths
setlocal enableDelayedExpansion
for %%L in (^"^
%==== Puts quoted newline in FOR variable L %
^") do set "folder2=!folder:\=%%~L!" ==== Substitutes newline for all \ in folder
::==== Count the number of tokens (lines) in folder2
setlocal disableDelayedExpansion
for /f %%N in ('"(cmd /v:on /c echo !folder2!)|find /c /v """') do set cnt=%%N
:start ==== Finally get the sorted list of files without root path
>"%archive%.$$$" (
for /f "delims=\ tokens=%cnt%*" %%A in ('dir "%folder%" /a:-d /b /o:n /s') do echo %%B
)
The code is quite terse without the comments and extra blank lines
@echo off
setlocal disableDelayedExpansion
for %%F in ("%~f1.") do (
set "folder=%%~fF"
set "archive=%%~nxF"
set "att=-%%~aF"
)
if %att:d=% == %att% (
>&2 echo Supplied path is invalid or not a folder
exit /b 1
)
if not defined archive (
set "archive=drive %folder:~0,1% root"
set cnt=1
goto start
)
setlocal enableDelayedExpansion
for %%L in (^"^
^") do set "folder2=!folder:\=%%~L!"
setlocal disableDelayedExpansion
for /f %%N in ('"(cmd /v:on /c echo !folder2!)|find /c /v """') do set cnt=%%N
:start
>"%archive%.$$$" (
for /f "delims=\ tokens=%cnt%*" %%A in ('dir "%folder%" /a:-d /b /o:n /s') do echo %%B
)
My original answer did not consider the possibility of wildcards in the input. Wildcards can be excluded by adding the following after the initial SETLOCAL
for /f "delims=*?<> tokens=2" %%A in (".%~1.") do (
>&2 echo Wildcards * ? ^< and ^> not supported
exit /b 1
)
Note that "<"
and ">"
are poorly documented wildcards (only when they are quoted within a path)!
But Mofi described an interesting behavior I had never seen before. At first I could not understand how C:\test\*
was yielding C:\
.
That is simply the result of %~f1
seeing the .
directory entry as the first listed file/folder in the test folder, and then when my code appends its own dot, it becomes C:\test\..
, which of course normalizes to C:\
.
For a while I was thinking that there was special processing going on when a path node is nothing but wildcards, but I posted the behavior on DosTips and a responder reminded me of the .
and ..
directory entries on all but the drive root.