I don't feel guilty answering this homework question now that the semester is long past. Print folders and files recursively using Windows Batch is a closed duplicate question that discusses the assignment.
My initial solution is fairly straight forward. There are a few tricks to make sure it properly handles paths with special characters in them, but nothing too fancy. The only other trick is left padding the file size with spaces so that SORT works properly.
Just as in the original question, the 1st parameter should be a folder path (.\ works just fine), and subsequent arguments represent file names (wildcards are OK).
@echo off
setlocal disableDelayedExpansion
set tempfile="%temp%\_mysort%random%.txt"
set "root="
for %%F in (%*) do (
if not defined root (
pushd %%F || exit /b
set root=1
) else (
echo(
echo %%~nxF
echo --------------------------------------------
(
@echo off
for /f "eol=: delims=" %%A in ('dir /s /b "%%~nxF"') do (
set "mypath=%%~dpA"
set "size= %%~zA"
setlocal enableDelayedExpansion
set "size=!size:~-12!"
echo !size! !mypath!
endlocal
)
) >%tempfile%
sort /r %tempfile%
)
)
if exist %tempfile% del %tempfile%
if defined root popd
I had hoped to avoid creation of a temporary file by replacing the redirect and subsequent sort with a pipe directly to sort. But this does not work. (see my related question: Why does delayed expansion fail when inside a piped block of code?)
My first solution works well, except there is the potential for duplicate output depending on what input is provided. I decided I would write a version that weeds out duplicate file reports.
The basic premise was simple - save all output to one temp file with the file name added to the front of the sorted strings. Then I need to loop through the results and only print information when the file and/or the path changes.
The last loop is the tricky part, because file names can contain special characters like !
^
&
and %
that can cause problems depending on what type of expansion is used. I need to set and compare variables within a loop, which usually requires delayed expansion. But delayed expansion causes problems with FOR variable expansion when !
is found. I can avoid delayed expansion by calling outside the loop, but then the FOR variables become unavailable. I can pass the variables as arguments to a CALLed routine without delayed expansion, but then I run into problems with %
^
and &
. I can play games with SETLOCAL/ENDLOCAL, but then I need to worry about passing values across the ENDLOCAL barrier, which requires a fairly complex escape process. The problem becomes a big vicious circle.
One other self imposed constraint is I don't want to enclose the file and path output in quotes, so that means I must use delayed expansion, FOR variables, or escaped values.
I found an interesting solution that exploits an odd feature of FOR variables.
Normally the scope of FOR variables is strictly within the loop. If you CALL outside the loop, then the FOR variable values are no longer available. But if you then issue a FOR statement in the called procedure - the caller FOR variables become visible again! Problem solved!
@echo off
setlocal disableDelayedExpansion
set tempfile="%temp%\_mysort%random%.txt"
if exist %tempfile% del %tempfile%
set "root="
(
for %%F in (%*) do (
if not defined root (
pushd %%F || exit /b
set root=1
) else (
set "file=%%~nxF"
for /f "eol=: delims=" %%A in ('dir /s /b "%%~nxF"') do (
set "mypath=%%~dpA"
set "size= %%~zA"
setlocal enableDelayedExpansion
set "size=!size:~-12!"
echo(!file!/!size!/!mypath!
endlocal
)
)
)
)>%tempfile%
set "file="
set "mypath="
for /f "tokens=1-3 eol=/ delims=/" %%A in ('sort /r %tempfile%') do call :proc
if exist %tempfile% del %tempfile%
if defined root popd
exit /b
:proc
for %%Z in (1) do (
if "%file%" neq "%%A" (
set "file=%%A"
set "mypath="
echo(
echo %%A
echo --------------------------------------------
)
)
for %%Z in (1) do (
if "%mypath%" neq "%%C" (
set "mypath=%%C"
echo %%B %%C
)
)
exit /b