2011-12-17 18 views
6

Estoy intentando crear un archivo por lotes que puede tener otros archivos depositados en él. Específicamente, estoy usando ffmpeg para editar archivos de audio producidos por una grabadora de voz de mano. El problema es cuando se utilizan nombres de archivos con signos y signos (&). Incluso cuando se cita la entrada, se deja todo lo que se encuentre después del &, pero solo cuando los archivos se colocan en él; si la entrada de nombre de archivo se escribe en la línea de comandos, la secuencia de comandos funciona bien. Antes de que se cierre la ventana cmd, veo brevemente el resto del nombre de archivo con un error que indica que no se reconoce como un comando válido.Script por lotes "Droplet": nombres de archivo que contienen símbolos

Aquí está mi 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 

Esto es lo que aparece en la línea de comandos, después de caer "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 

En caso es importante: estoy usando el Windows 8 Developer Preview. ¿Esto está causando mi problema? ¿Ocurre el mismo error en Windows 7 o anterior?

Respuesta

13

Existe un error de larga duración en la funcionalidad de arrastrar y soltar de Windows con respecto a las rutas de archivos que contienen & o ^ pero no contienen un <space>.

Si la ruta de un archivo contiene al menos un <space>, Windows automáticamente incluye la ruta entre comillas para que se analice correctamente. Windows debería hacer lo mismo si la ruta del archivo contiene & o ^, pero no es así.

Si crea el siguiente archivo por lotes simple y arrastra archivos sobre él, puede ver el problema.

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

The! Cmdcmdline! variable contiene el comando real que lanzó el archivo por lotes. El archivo de proceso por lotes imprime la línea de comando y el primer parámetro.

Si arrastrar y soltar un archivo llamado "a.txt" que presentamos lo

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

Si usted caso omiso de las comillas en el comando completo que se ve que no hay comillas alrededor del argumento de archivo. No hay caracteres especiales, entonces no hay problema.

Ahora arrastrar y soltar "un b.txt" y se obtiene

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

se puede ver cómo Windows detecta el espacio en el nombre y encierra el archivo entre comillas. Nuevamente no hay problema

Ahora arrastrar y soltar "un b.txt &" y se obtiene

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

Windows no encuentra un espacio en el nombre, por lo que no encierra entre comillas. ¡Gran problema! Windows pasa "C: \ prueba \ a" al archivo de proceso por lotes y trata "b.txt" como un segundo archivo que se ejecutará después de que se complete el archivo por lotes. El comando EXIT dura en el archivo por lotes impide que se ejecute cualquier nombre de archivo dividido después del lote. Por supuesto, b.txt nunca podría ejecutarse. Pero si el archivo se llamara "a & b.bat" y "b.bat" existía, entonces eso podría ser un problema si la EXIT dura no estuviera en el archivo por lotes.

Es posible arrastrar varios archivos a un archivo por lotes, y cada uno se debe pasar como un parámetro.

The! Cmdcmdline! es la única forma de acceder de manera confiable a los argumentos de arrastrar y soltar. Pero eso no funcionará si los archivos se pasan como argumentos normales en una llamada normal al archivo por lotes.

A continuación se muestra un archivo de proceso por lotes que puede detectar si se llamó utilizando arrastrar y soltar en comparación con una llamada normal. (No es a prueba de balas, pero creo que debería funcionar en la mayoría de las situaciones) Procesará cada argumento de archivo, uno a la vez, independientemente del tipo de llamada. (El proceso simplemente echos el nombre del archivo, pero puede sustituir el procesamiento que desee). Si se llamó al lote usando la función de arrastrar y soltar, hará una salida difícil para protegerse contra nombres de archivos divididos.

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

parece que su lote existente solamente está diseñado para manejar un archivo. No puedo decir si necesita hacer modificaciones en las llamadas para admitir archivos múltiples. Modifiqué el lote anterior para que solo procesara el primer argumento y lo sustituí en el ciclo de procesamiento de argumentos. Esto no se ha probado, pero creo que debería funcionar para usted.

@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

¡Maravilloso! Funciona genial. Si no es una molestia, me encantaría una explicación línea por línea de cómo funciona el script porque este es mi primer script por lotes (aunque no es mi primera experiencia en programación). ¡Gracias! – stephenwade

0

Admiro las habilidades de programación por lotes de dbenham en el temor silencioso. Probé su solución y tropezado con dos problemas que presento aquí, ya que no tengo la reputación suficiente para comentar:

  1. Parece que hay un espacio extra delante de las últimas comillas en la línea 15 del su plantilla de lote Supongo que debería decir:

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

    Alguien con conocimientos de programación por lotes no tan estelar podría tener graves problemas para encontrar esto, como yo. Intenté pero no pude editar la publicación de dbenham debido a la estúpida limitación de "Las ediciones deben tener al menos 6 caracteres".

  2. La solución generalmente no es adecuada para archivos/carpetas que contienen , (coma) o ; (punto y coma) en su ruta completa. Puede ser modificado para trabajar en caso de que sólo hay uno archivo/carpeta se dejó caer en un archivo por lotes encerrando argumentos entre comillas en la línea 20:

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

    Cuando se deja caer más de un archivo/carpeta en un archivo de proceso por lotes, me temo que no hay una solución general del error de Windows que pueda hacer frente a la coma/punto y coma en la ruta del archivo. El mecanismo SendTo de Windows obviamente tiene la misma deficiencia (error), por lo que no se puede usar para evitar el error de arrastrar y soltar. Depende de Microsoft solucionar finalmente este error.

Cuestiones relacionadas