Пакетный скрипт "Droplet" - имена файлов, содержащие амперсанды - PullRequest
7 голосов
/ 18 декабря 2011

Я пытаюсь создать командный файл, в который могут быть добавлены другие файлы.В частности, я использую ffmpeg для редактирования аудиофайлов, созданных портативным диктофоном.Проблема заключается в использовании имен файлов с амперсандами (&).Даже при цитировании ввода все, что находится после символа &, удаляется, но только когда на него помещаются файлы;если ввод имени файла вводится в командной строке, скрипт работает нормально.Перед закрытием окна cmd я кратко вижу оставшуюся часть имени файла с ошибкой о том, что она не распознана как допустимая команда.

Вот мой сценарий:

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"
<i>[...]</i>
ch17: No such file or directory

На случай, если это имеет значение: я использую Windows 8 Developer Preview.Это вызывает мою проблему?Такая же ошибка возникает в Windows 7 или более ранней версии?

Ответы [ 2 ]

14 голосов
/ 18 декабря 2011

В функции перетаскивания 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 . . .

Если вы проигнорируете кавычки вокругвсю команду вы видите, что вокруг аргумента файла нет кавычек.Здесь нет специальных символов, поэтому проблем нет.

Теперь перетащите «a 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 обнаруживает пространство вимя и заключает файл в кавычки.Опять нет проблем.

Теперь перетащите «a & 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
)
1 голос
/ 14 августа 2014

Я восхищаюсь навыками пакетного программирования dbenham в безмолвном страхе.Я попробовал его решение и наткнулся на две проблемы, которые я здесь представляю, поскольку у меня недостаточно репутации, чтобы комментировать:

  1. Кажется, перед последней кавычкой есть дополнительный пробелна линии 15 его шаблона партии.Я полагаю, это должно выглядеть следующим образом:

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

    Кто-то, обладающий не столь звездным знанием пакетного программирования, может столкнуться с серьезными проблемами при поиске этого, как и я.Я пытался, но не смог отредактировать пост dbenham из-за глупого ограничения "Правка должно быть не менее 6 символов".

  2. Решение обычно не подходит для файлов / папок, содержащих ,(запятая) или ; (точка с запятой) в их полном пути.Его можно изменить, чтобы он работал, если в пакетный файл помещен только один файл / папка, заключив аргументы в кавычки в строке 20:

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

    Когда больше, чемодин файл / папка перетаскивается в командный файл, я боюсь, что не существует общего обходного пути ошибки Windows, которая могла бы справиться с запятой / точкой с запятой в пути к файлу.Механизм SendTo в Windows, очевидно, имеет тот же недостаток (ошибка), поэтому его нельзя использовать для обхода ошибки перетаскивания.Таким образом, Microsoft должна окончательно исправить эту ошибку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...