我有一个文件名列表和一个文件夹作为批处理脚本的命令行参数。对于每个文件名,我需要打印文件所在文件夹的所有子文件夹(该文件的路径)。子文件夹名称应按文件大小降序排序(文件在不同的子文件夹中可以有不同的大小)。
到目前为止我已经这么做了,但没用:
::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
最佳答案
既然这个学期已经过去了,我回答这个家庭作业问题并不感到内疚。Print folders and files recursively using Windows Batch是一个封闭的重复问题,讨论作业。
我的初步解决方案相当直截了当。有一些技巧可以确保它正确地处理带有特殊字符的路径,但是没有什么太花哨的。唯一的另一个技巧是用空格填充文件大小,这样排序就可以正常工作了。
正如在最初的问题中一样,第一个参数应该是文件夹路径(.\works just fine),后面的参数表示文件名(通配符是可以的)。
@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
我曾希望通过将重定向和随后的排序替换为直接排序的管道来避免创建临时文件。但这不管用。(参见我的相关问题:Why does delayed expansion fail when inside a piped block of code?)
我的第一个解决方案工作得很好,只是根据所提供的输入,可能会出现重复输出。我决定写一个能消除重复文件报告的版本。
基本前提很简单-将所有输出保存到一个临时文件中,并将文件名添加到排序字符串的前面。然后我需要遍历结果,并且只在文件和/或路径更改时打印信息。
最后一个循环是棘手的部分,因为文件名可以包含特殊字符,如
!
^
&
和%
,这些字符根据使用的扩展类型可能会导致问题。我需要在循环中设置和比较变量,这通常需要延迟扩展。但当发现!
时,延迟膨胀会导致变量膨胀问题。我可以通过在循环外调用来避免延迟扩展,但随后for变量变得不可用。我可以将变量作为参数传递给被调用的例程,而不需要延迟扩展,但随后我遇到了%
^
和&
的问题。我可以用setlocal/endlocal玩游戏,但是我需要担心的是如何通过endlocal屏障传递值,这需要一个相当复杂的转义过程。这个问题变成了一个大的恶性循环。另一个自我施加的约束是,我不想将文件和路径输出用引号括起来,所以这意味着我必须对变量或转义值使用延迟扩展。
我发现了一个有趣的解决方案,它利用了for变量的一个奇怪特性。
通常for变量的范围严格在循环内。如果在循环外部调用,则for变量值不再可用。但是,如果随后在调用过程中发出for语句,则变量的调用方将再次可见!问题解决了!
@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