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