1

I have as command-line parameters to my batch script a list of filenames and a folder. For each filename, I need to print all subfolders of the folder where the file is found (the path of that file). The subfolder names should be sorted in descending order of the file sizes (the file can have various sizes in different subfolders).

I have done this so far, but it doesn't work:

::verify if the first parameter is the directory

@echo off
REM check the numbers of parameters
if "%2"=="" goto err1
REM check: is first parameter a directory?
if NOT EXIST %1\NUL goto err2
set d=%1
shift
REM iterate the rest of the parameters


for %%i in %dir  do (
find %dir /name %i > temp

if EXIST du /b temp | cut /f 1 goto err3 
 myvar=TYPE temp
echo "file " %i "is in: "

for %%j in %myvar do
 echo %j

 echo after sort
du /b %myvar | sort /nr       
)

:err1
echo Two parameters are necessary 
goto end

:err2
echo First parameter must be a directory.
goto end

:err3 
echo file does not exist.
goto end

:end
indiv
  • 17,306
  • 6
  • 61
  • 82
Radu Stejerean
  • 345
  • 2
  • 13
  • 28
  • I've done some improvements although I can't make a script that finds a files path. – Radu Stejerean Apr 10 '11 at 18:23
  • 1
    Don't create several equal question! If needed - just edit the question. – abatishchev Apr 10 '11 at 18:30
  • Is there are reason why you have to do this in DOS? if PowerShell is available you might find it easier to write this in Powershell, which has excellent facilities for sorting and filtering. You don't need a powershell console - you can run powershell commands direct from DOS if necessary by just calling powershell.exe and passing in the commands or the path to a .ps1 script – Stephen Connolly Apr 19 '11 at 12:37
  • @StephenConnolly, There are many reasons it needs to be done in DOS. See http://serverfault.com/questions/490841/how-to-display-the-first-n-lines-of-a-command-output-in-windows-the-equivalent/716619#comment551698_490842 for some examples. – Pacerier Aug 25 '15 at 14:20

1 Answers1

6

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
Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390