2011-12-17 19 views
6

Sto provando a creare un file batch che può contenere altri file su di esso. In particolare, sto usando ffmpeg per modificare file audio prodotti da un registratore vocale portatile. Il problema è quando si utilizzano nomi file con e commerciali (&). Anche quando si cita l'input, qualsiasi cosa dopo lo & viene cancellata, ma solo quando i file vengono rilasciati su di esso; se l'input del nome file viene digitato sulla riga di comando, lo script funziona correttamente. Prima che la finestra cmd si chiuda, visualizzo brevemente il resto del nome file con un errore che dice che non è riconosciuto come un comando valido.Script batch "Droplet" - nomi di file contenenti e commerciali

Ecco il mio script:

 
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 

Ecco ciò che appare sulla linea di comando, dopo aver lasciato "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 

Nel caso in cui è importante: sto usando il Windows 8 Developer Preview. Questo sta causando il mio problema? Lo stesso errore si verifica su Windows 7 o versioni precedenti?

risposta

13

C'è un errore di vecchia data nella funzionalità di trascinamento della selezione di Windows per i percorsi di file che contengono & o ^ ma non contengono uno <space>.

Se un percorso file contiene almeno uno <space>, Windows racchiude automaticamente il percorso tra virgolette affinché venga analizzato correttamente. Windows dovrebbe fare la stessa cosa se il percorso del file contiene & o ^, ma non è così.

Se si crea il seguente file batch semplice e si trascinano i file su di esso, è possibile visualizzare il problema.

@echo off 
setlocal enableDelayedExpansion 
echo cmd=!cmdcmdline! 
echo %%1="%~1" 
pause 
exit 

Il! Cmdcmdline! variabile contiene il comando effettivo che ha avviato il file batch. Il file batch stampa la riga di comando e il primo parametro.

Se si trascina un file denominato "a.txt" si ottiene

cmd=cmd /c ""C:\test\drag.bat" C:\test\a.txt" 
%1=C:\test\a.txt 
Press any key to continue . . . 

Se non rispetti le virgolette l'intero comando si vede che non ci sono virgolette intorno l'argomento file. Non ci sono caratteri speciali, quindi non ci sono problemi.

Ora drag and drop "un b.txt" e si ottiene

cmd=cmd /c ""C:\test\drag.bat" "C:\test\a b.txt"" 
%1="C:\test\a b.txt" 
Press any key to continue . . . 

si può vedere come Windows rileva lo spazio in nome e racchiude il file tra virgolette. Anche in questo caso non ci sono problemi.

Ora drag and drop "un b.txt &" e si ottiene

cmd=cmd /c ""C:\test\drag.bat" C:\test\a&b.txt" 
%1=C:\test\a 
Press any key to continue . . . 

di Windows non trova uno spazio nel nome, in modo che non racchiude tra virgolette. Grande problema! Windows passa "C: \ test \ a" al file batch e tratta "b.txt" come un secondo file da eseguire dopo il completamento del file batch. Il comando EXIT nel file batch impedisce l'esecuzione di eventuali nomi di file divisi dopo il batch. Ovviamente b.txt non potrebbe mai essere eseguito. Ma se il file è stato denominato "a & b.bat" e "b.bat" esisteva, questo potrebbe essere un problema se EXIT non fosse nel file batch.

E 'possibile trascinare più file su un file batch, e ognuno deve essere passato come parametro.

Il CMDCMDLINE!! è l'unico modo per accedere in modo affidabile agli argomenti di trascinamento della selezione. Ma questo non funzionerà se i file vengono passati come normali argomenti in una normale chiamata al file batch.

Qui di seguito è un file batch in grado di rilevare se è stato chiamato con il drag and drop contro una chiamata normale. (Non è a prova di proiettile, ma penso che dovrebbe funzionare nella maggior parte delle situazioni) Elaborerà ogni argomento di file, uno alla volta, indipendentemente dal tipo di chiamata. (Il processo echos semplicemente il nome del file, ma è possibile sostituire qualsiasi trattamento che si desidera). Se il lotto è stato chiamato con il drag and drop, allora lo farà un'uscita difficile per la protezione contro i nomi dei file divisi.

@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 
) 

Sembra che il gruppo esistente sia progettato per gestire un solo file. Non riesco a capire se è necessario apportare modifiche alle chiamate per supportare più file. Ho modificato il batch precedente per elaborare solo il primo argomento e sostituito il processo nel ciclo di elaborazione degli argomenti. Questo non è stato verificato, ma penso che dovrebbe funzionare per te.

@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

Meraviglioso! Funziona alla grande. Se non è un fastidio, mi piacerebbe una spiegazione riga per riga di come funziona lo script, perché questo è il mio primo script batch (anche se non è la mia prima esperienza di programmazione). Grazie! – stephenwade

0

Ammiro le abilità di programmazione batch di dbenham in soggezione silenziosa. Ho provato la sua soluzione e inciampato su due problemi che presento qui come non ho abbastanza fama di commentare:

  1. Sembra che ci sia uno spazio supplementare di fronte l'ultima virgoletta sulla linea 15 della il suo modello di batch. Suppongo si deve leggere:

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

    Qualcuno con conoscenze di programmazione batch non-così-stellare potrebbe avere gravi problemi a trovare questo, come me. Ho provato ma non sono riuscito a modificare il post di dbenham a causa della stupida limitazione "Modifica deve essere almeno di 6 caratteri".

  2. La soluzione generalmente non è adatta per file/cartelle contenenti , (virgola) o ; (punto e virgola) nel loro percorso completo. Esso può essere modificato per funzionare nel caso in cui v'è un solo uno file/cartella è sceso su un file batch racchiudendo args tra virgolette sulla linea 20:

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

    Quando più di un file/cartella viene eliminato su un file batch, temo che non sia possibile una soluzione generale del bug di Windows in grado di gestire virgola/punto e virgola nel percorso del file. Il meccanismo SendTo di Windows ha ovviamente lo stesso difetto (bug), quindi non può essere utilizzato per aggirare il bug drag-and-drop. È quindi compito di Microsoft risolvere definitivamente questo bug.

Problemi correlati