Windows拖放功能中存在一个长期存在的错误,它涉及到包含&
或^
但不包含<空格>
的文件路径。
如果文件路径至少包含一个<空格>
,则Windows会自动将路径括在引号中,以便正确解析它。如果文件路径包含&
或^
,Windows应该执行相同的操作,但它没有这样做。
如果您创建下面的简单批处理文件并将文件拖入其中,您可以看到问题。
@echo off
setlocal enableDelayedExpansion
echo cmd=!cmdcmdline!
echo %%1="%~1"
pause
exit
!cmdcmdline!变量包含启动批处理文件的实际命令。批处理文件打印出命令行和第一个参数。
如果你拖放一个名为“a.txt”的文件,你会得到:
cmd=cmd /c ""C:\test\drag.bat" C:\test\a.txt"
%1=C:\test\a.txt
Press any key to continue . . .
如果您忽略整个命令周围的引号,就会发现文件参数周围没有引号。因为没有特殊字符,所以没有问题。
现在拖放 "a b.txt",您将会得到:
cmd=cmd /c ""C:\test\drag.bat" "C:\test\a b.txt""
%1="C:\test\a b.txt"
Press any key to continue . . .
你可以看到Windows如何检测名称中的空格并用引号括起文件。再次强调这没有问题。
现在拖放"a&b.txt",你会得到:
cmd=cmd /c ""C:\test\drag.bat" C:\test\a&b.txt"
%1=C:\test\a
Press any key to continue . . .
Windows在文件名中没有找到空格,因此不会用引号括起来。这是个大问题!Windows将"C:\test\a"传递给批处理文件,并将"b.txt"视为在批处理文件完成后要执行的第二个文件。批处理文件中的硬退出命令阻止了任何分割的文件名在批处理之后执行。当然,b.txt永远都不可能被执行。但如果文件名为"a&b.bat",并且存在"b.bat",那么如果批处理文件中没有硬退出命令,就可能会出现问题。
可以将多个文件拖到批处理文件上,每个文件都将作为参数传递。
!cmdcmdline!是唯一可靠访问拖放参数的方法。但如果文件作为普通参数在正常调用批处理文件时传递,它将无法正常工作。
以下是一个批处理文件,它可以检测是否使用拖放方式调用它,而不是普通调用。(它不是绝对可靠的,但我认为它应该在大多数情况下都能正常工作)它将处理每个文件参数,无论调用类型如何,逐个进行处理。(这个过程只是回显文件名,但你可以替换为任何你想要的处理方式。)如果使用拖放方式调用批处理,则它将进行硬退出,以防止分割的文件名出现错误。
@echo off
setlocal disableDelayedExpansion
::
:: first assume normal call, get args from %*
set args=%*
set "dragDrop="
::
:: Now check if drag&drop situation by looking for %0 in !cmdcmdline!
:: if found then set drag&drop flag and get args from !cmdcmdline!
setlocal enableDelayedExpansion
set "cmd=!cmdcmdline!"
set "cmd2=!cmd:*%~f0=!"
if "!cmd2!" neq "!cmd!" (
set dragDrop=1
set "args=!cmd2:~0,-1! "
set "args=!args:* =!"
)
::
:: Process the args
for %%F in (!args!) do (
if "!!"=="" endlocal & set "dragDrop=%dragDrop%"
rem ------------------------------------------------
rem - Your file processing starts here.
rem - Each file will be processed one at a time
rem - The file path will be in %%F
rem -
echo Process file "%%~F"
rem -
rem - Your file processing ends here
rem -------------------------------------------------
)
::
:: If drag&drop then must do a hard exit to prevent unwanted execution
:: of any split drag&drop filename argument
if defined dragDrop (
pause
exit
)
看起来你现有的批处理只能处理一个文件。我不能确定你是否需要对调用进行修改以支持多个文件。我修改了上述批处理程序,使其仅处理第一个参数,并将你的进程替换到参数处理循环中。这还没有经过测试,但我认为它应该适合你。
@echo off
setlocal disableDelayedExpansion
::
:: first assume normal call, get args from %*
set args=%*
set "dragDrop="
::
:: Now check if drag&drop situation by looking for %0 in !cmdcmdline!
:: if found then set drag&drop flag and get args from !cmdcmdline!
setlocal enableDelayedExpansion
set "cmd=!cmdcmdline!"
set "cmd2=!cmd:*%~f0=!"
if "!cmd2!" neq "!cmd!" (
set dragDrop=1
set "args=!cmd2:~0,-1! "
set "args=!args:* =!"
)
::
:: Process the first argument only
for %%F in (!args!) do (
if "!!"=="" endlocal & set "dragDrop=%dragDrop%"
rem ------------------------------------------------
rem - Your file processing starts here.
rem - Use %%F wherever you would normally use %1
rem
rem Change to drive and directory of input file
%%~dF
cd %%~pF
rem ffmpeg: mix to one channel, double the volume
%HOMEDRIVE%%HOMEPATH%\ffmpeg.exe -i "%%~nxF" -ac 1 -vol 1024 "%%~nF fixed%%~xF"
rem
rem - Your file processing ends here
rem -------------------------------------------------
goto :continue
)
:continue
if defined dragDrop (
pause
exit
)