2011-12-17 120 views
6

我试图创建一个批处理文件,可以将其他文件拖放到它上面。具体来说,我使用ffmpeg来编辑手持式录音笔生成的音频文件。问题是使用带&字符的文件名(&)。即使在引用输入时,&之后的任何内容都将被删除,但仅当文件被删除时才会被删除;如果文件名输入在命令行中输入,则脚本可以正常工作。在cmd窗口关闭之前,我暂时看到文件名的其余部分,并显示一条错误消息,说明它不被识别为有效的命令。“Droplet”批处理脚本 - 包含&字符的文件名

这里是我的脚本:

 
rem Change to drive and directory of input file 
%~d1 
cd %~p1 

rem ffmpeg: mix to one channel, double the volume 
%HOMEDRIVE%%HOMEPATH%\ffmpeg.exe -i "%~nx1" -ac 1 -vol 1024 "%~n1 fixed%~x1" 

pause 

下面是在命令行上出现的内容,下探"ch17&18.mp3"后:

 
C:\Users\computergeeksjw\Desktop>C:\Users\computergeeksjw\ffmpeg.exe -i "ch17" -ac 1 -vol 1024 "ch17 fixed" 
[...] 
ch17: No such file or directory 

如果它的事项:我使用的是Windows 8的开发者预览版。这是否导致我的问题?在Windows 7或更早版本上发生同样的错误吗?

回答

13

Windows拖放功能中存在一个长期存在的错误,该功能与文件路径有关,其中包含&^但不包含<space>

如果文件路径至少包含一个<space>,则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 . . . 

。没有特殊字符,所以没有问题。

现在拖放“一个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如何检测的空间的名称和封闭在报价文件。再次没有问题。

现在拖放“一个& 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”作为第二个文件执行。批处理文件中的硬EXIT命令可防止批处理后执行任何分割文件名。当然b.txt永远不会执行。但如果该文件被命名为“a & b.bat”和“b.bat”,那么如果硬EXIT不在批处理文件中,则可能会出现问题。

是可能的拖动多个文件到一个批处理文件,而且每一个应作为一个参数传递。

的!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 
) 
+0

精彩!很棒。如果不是麻烦的话,我会喜欢逐行解释脚本的工作原理,因为这是我的第一批脚本(尽管不是我的第一次编程经验)。谢谢! – stephenwade 2011-12-26 01:41:03

0

我很佩服dbenham在沉默敬畏中的批处理编程技巧。 我想他的解决方案,在我在这里提出两个问题跌跌撞撞,因为我没有足够的信誉评论:

  1. 似乎是在最后引号的前面上的第15行的额外空间他的批处理模板。我想这应该阅读:

    set "args=!cmd2:~0,-1!" 
    

    有人用不那么恒星批处理编程知识可能有严重的问题,找到这个,像我一样。我试过,但无法编辑dbenham的帖子,因为愚蠢的“编辑必须至少有6个字符”的限制。

  2. 该解决方案通常不适用于包含,(逗号)或;(分号)的文件/文件夹的完整路径。 它可被修改的情况下工作,只有一个文件/文件夹滴到一个批处理文件由线20包围在引号ARGS:

    for %%F in ("!args!") do (
    

    多于一个文件/文件夹被丢弃到一个批处理文件,恐怕没有一个Windows bug的通用解决方法是可能的,可以处理文件路径中的逗号/分号。 Windows的SendTo机制显然具有相同的缺陷(bug),所以不能用于解决拖放问题。这是由微软最终解决这个错误。

相关问题