2011-12-17 101 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),所以不能用於解決拖放問題。這是由微軟最終解決這個錯誤。

相關問題