Find filer og sorter efter størrelse i en Windows-batchfil

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har som kommandolinjeparametre til min batch script en liste over filnavne og en mappe. For hvert filnavn skal jeg udskrive alle undermapper i den mappe, hvor filen er fundet (stien til den pågældende fil). Undermappenavnene skal sorteres i faldende rækkefølge af filstørrelserne (filen kan have forskellige størrelser i forskellige undermapper).


Jeg har gjort det hidtil, men det virker ikke:


::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 \%1NUL 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

Bedste reference


Jeg føler mig ikke skyldig i at svare på dette lektie spørgsmål nu, hvor semesteret er langt forbi. Udskriftsmapper og filer rekursivt ved hjælp af Windows Batch er et lukket dobbelt spørgsmål, der diskuterer opgaven.


Min første løsning er ret lige fremad. Der er et par tricks for at sikre, at det korrekt håndterer stier med specialtegn i dem, men intet for fancy. Det eneste andet trick er at efterlade filstørrelsen med mellemrum, så SORT fungerer korrekt.


Ligesom i det oprindelige spørgsmål, bør den første parameter være en mappebane (. \ Fungerer fint), og efterfølgende argumenter repræsenterer filnavne (jokertegn er 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


Jeg havde håbet på at undgå at oprette en midlertidig fil ved at erstatte omdirigering og efterfølgende sortering med et rør direkte til sortering. Men det virker ikke. (se mit beslægtede spørgsmål: Hvorfor forsinkes ekspansionen, når den er inde i en pipeblok?)


Min første løsning fungerer godt, medmindre der er potentiale for dobbelt udgang afhængigt af, hvilken input der leveres. Jeg besluttede, at jeg ville skrive en version, der ukrudt duplikatfilrapporter.


Den grundlæggende forudsætning var enkel - gem alle output til en temp fil med filnavnet tilføjet til forsiden af ​​de sorterede strenge. Så skal jeg løbe gennem resultaterne og kun udskrive oplysninger, når filen og/eller stien ændres.


Den sidste sløjfe er den vanskelige del, fordi filnavne kan indeholde specialtegn som ! ^ & og \%, der kan forårsage problemer afhængigt af, hvilken type udvidelse der bruges. Jeg skal indstille og sammenligne variabler inden for en loop, som normalt kræver forsinket ekspansion. Men forsinket ekspansion medfører problemer med FOR-variabel ekspansion, når ! er fundet. Jeg kan undgå forsinket ekspansion ved at kalde uden for sløjfen, men så bliver FOR-variablerne utilgængelige. Jeg kan videregive variablerne som argumenter til en CALLed rutine uden forsinket ekspansion, men så kommer jeg ind i problemer med \% ^ og &. Jeg kan spille spil med SETLOCAL/ENDLOCAL, men så er jeg nødt til at bekymre mig om at passere værdier på tværs af ENDLOCAL-barrieren, hvilket kræver en ret kompleks flugtproces. Problemet bliver en stor ond cirkel.


En anden selvpålagt begrænsning er, at jeg ikke ønsker at vedlægge filen og baneffekten i citater, så det betyder, at jeg skal bruge forsinket ekspansion, FOR variabler eller undslippede værdier.


Jeg fandt en interessant løsning, der udnytter et mærkeligt træk ved FOR-variabler.


Normalt er omfanget af FOR-variabler strengt inden for løkken. Hvis du Ringer uden for sløjfen, er FOR-variableværdierne ikke længere tilgængelige. Men hvis du derefter udsteder en FOR-erklæring i den kaldte procedure, bliver opkalderen FOR-variabler synlige igen! Problemet løst!


@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