Windows cmd: невозможно экранировать процентную переменную с пробелами в имени файла?

Как мне правильно избежать такого пути к файлу в командной строке Windows? Тот, у которого в имени файла есть пробелы, проценты и переменные, такие как %cd% и %errorlevel%, без расширения переменных? Возможно ли это?

Например, запустите C:\Users\the impossible %cd% filepath.exe

Я искал и пробовал все вокруг для экранирования процентов и экранирования кавычек. Без переменных в имени файла это работает. Однако, если имя файла содержит проценты с такими переменными, как %cd% или %errorlevel%, ничего не работает вообще, независимо от какой-либо escape-последовательности.

Вещи, которые я пробовал:

"C:\Users\the impossible %cd% filepath.exe"

"C:\Users\the impossible %%cd%% filepath.exe"

"C:\Users\the impossible ^%cd^% filepath.exe"


2
50
2

Ответы:

Решено

В контексте командной строки существуют другие правила экранирования, чем в пакетном файле.
В контексте командной строки невозможно экранировать знак процента, но есть способ избежать его.

Итак, в вашем случае вы можете использовать:

^"C:\Users\the^ impossible^ %cd^%^ filepath.exe^"

Чтобы иметь возможность использовать курсор в имени файла, кавычки должны быть экранированы.

Но знак каретки не может избежать знака процента %cd^% Парсер пытается найти переменную с именем cd^, если такой переменной нет, он просто удаляет курсор и текст остается нетронутым.
Каретки перед пробелами необходимы для сохранения целостности имени файла.

Но если такая переменная существует, команда завершится неудачно:

set "cd^=FAIL"

Существует второй вариант, в котором можно избежать одиночных знаков процента.

for /F "tokens=1-31" %%= in ("1") do "C:\Users\the impossible %%=cd%%= filepath.exe"

Одиночный знак процента экранируется путем преобразования его в %%=

Это работает, поскольку первый знак процента просто остается на месте, а %= расширяется до нуля.
На одной из фаз пакетного парсера синтаксический анализатор пытается расширить %=cd%, но переменная не может начинаться со знака равенства, поэтому это безвредно.
Этот вариант является пуленепробиваемым, никакая предопределенная переменная не может его нарушить.

Эту технику можно адаптировать так, чтобы для выхода из %= требовался только один %.

for /F "tokens=1-31 delims= = " %%= in ("1=2=3=4=5=6=7=8=9=0=1=2=3=4=5=6=7=8=9=0=1=2=3=4=%=6=7") do (
  echo "%=cd%=cd expands to '%cd%' special chars aren't a problem &<>|"
  "C:\Users\the impossible %=cd%= filepath.exe"
)

В окне командной строки в этом случае необходимо использовать:

^"C:\Users\the^ impossible^ ^%cd^%^ filepath.exe^"`

Вывод справки по запуску cmd /? в окне командной строки на последней странице справки объясняет, что имя файла (или любая другая строка аргумента) с пробелом или одним из этих символов &()[]{}^=;!'+,`~ (или буквально для интерпретации <>|, как в строке пароля/парольной фразы) должен быть заключен в ".

Все символы внутри строки аргумента, заключенной в ", интерпретируются буквально, включая символ ^, за исключением % (всегда) и ! при включенном расширении отложенной переменной, которое отключено по умолчанию в Windows.

% интерпретируется как начало/конец ссылки на переменную среды в командной строке, введенной в окне командной строки (или как ссылка на переменную цикла в случае цикла FOR). Но по сравнению с обработкой командной строки в пакетном файле что-то вроде %cd% заменяется перед выполнением командной строки чем-то другим только в том случае, если действительно существует динамическая переменная или переменная среды с таким именем. Что-то вроде %UnknownVariable% в строке имени файла не заменяется пустой строкой при выполнении командной строки, введенной в окне командной строки, при обработке командного файла %UnknownVariable% удаляется из командной строки перед выполнением, если нет переменной с именем UnknownVariable в настоящее время определено при обработке командной строки.

Причины разницы в обработке строк между двумя % между выполнением командной строки и выполнением пакетного файла просты:

  • Должна быть возможность ввести имя файла, содержащее один %, что не так уж необычно, как думают многие, или командную строку с несколькими %, например, для запуска исполняемого файла обработки изображений с такими параметрами, как width=75% height=50%. Такие параметры можно записать в пакетном файле как width=75%% height=50%%, т.е. е. знак процента для буквальной интерпретации экранируется знаком «больше процентов», чтобы не интерпретировать его как начало (или конец) переменной, цикла (внутри цикла FOR) или ссылку на строку аргумента пакетного файла.
  • Можно ожидать, что %Variable Name%, введенный пользователем в окне командной строки, предназначен для ссылки на существующую переменную. Если динамическая переменная или переменная среды с именем Variable Name отсутствует, пользователь либо допустил ошибку при вводе, либо %Variable Name% вообще не ссылается на переменную. Командный процессор Windows при обработке пакетного файла может ожидать, что создатель пакетного файла не допустил опечатки. Это также очень распространено в пакетных файлах, ссылающихся на переменные, которые в настоящее время не существуют при обработке командной строки перед ее выполнением. Подумайте о переменных, определенных с необязательными параметрами, которые не существуют, если параметры не используются, или о ссылке на переменную, где при первом выполнении командной строки переменная, на которую ссылаются, еще не существует, но существует позже при повторном выполнении той же командной строки.

Командный процессор Windows заменяет %Variable Name% всегда значением указанной переменной или пустой строкой, если нет переменной с именем %Variable Name% при обработке пакетного файла, но заменяет ссылку на переменную только в том случае, если переменная действительно существует при обработке командной строки, введенной в окно командной строки или передается cmd.exe после опции /C или опции /K при ее запуске.

Вывод справки по запуску set /? в окне командной строки также описывает наиболее часто используемые динамические переменные (не переменные среды). CD — одна из динамических переменных. Строка %cd% в имени файла, введенная в окне командной строки или прочитанная из пакетного файла, по этой причине всегда интерпретируется как ссылка на динамическую переменную CD.

В пакетном файле можно использовать:

"C:\Users\the impossible %%cd%% filepath.exe"

Но специальное правило экранирования со знаком процента, оставленным вместо знака процента для буквальной интерпретации второго знака процента, предназначено только для пакетных файлов, а не для окна командной строки (из-за возможности ссылки на строку аргумента пакетного файла).

Следующая строка не может использоваться ни в окне командной строки, ни в пакетном файле, поскольку ^ внутри строки аргумента, заключенной в ", интерпретируется буквально, а не как escape-символ.

"C:\Users\the impossible ^%cd^% filepath.exe"

Решение не заключать строку аргумента в " специальным исключением из стандартных правил синтаксиса в этом случае использования, а вместо этого экранировать ^ каждым символом, например " и %, а также пробелом и использовать:

^"C:\Users\the^ impossible^ ^%cd^%^ filepath.exe^"`
  • ^" в начале указывает cmd.exe интерпретировать " буквально, а не как начало строки аргумента в двойных кавычках.

  • ^ слева от каждого символа пробела указывает cmd.exe интерпретировать пробел буквально, а не как разделитель строк аргументов.

  • ^% предписывает cmd.exe интерпретировать знак процента в этом случае буквально, а не как начало/конец ссылки на существующую переменную (или в цикле FOR как ссылку на переменную цикла).

  • ^" в конце указывает cmd.exe интерпретировать " буквально, а не как конец строки аргумента в двойных кавычках.

В результате строка имени файла с символами курсора интерпретируется как экранирующие символы с помощью cmd.exe как строки аргумента:

"C:\Users\the impossible %cd% filepath.exe"

Эта строка теперь передается в функцию библиотеки ядра Windows CreateProcess, при этом окружающие " требуются, поскольку CreateProcess анализирует эту строку командной строки, а также, чтобы выяснить, какой исполняемый файл запускать, а имя файла содержит пробелы и знаки процента для интерпретации также буквально CreateProcess как это делается для первой строки аргумента, заключенной в двойные кавычки.