Как избежать cmd.exe, интерпретируя специальные символы оболочки, такие как <> ^

У меня есть CMD Windows script, который принимает несколько параметров и выполняет EXE, передавая сначала некоторые жестко закодированные аргументы, а затем все параметры от пользователя. CMD script выглядит следующим образом:

launcher.exe paramX paramY %*

Пользователь выполнил бы CMD script из оболочки Windows следующим образом:

launcher.cmd param1 param2 param3 [...]

Проблема заключается в том, что если параметры CMD script содержат специальные символы оболочки, такие как < > и ^, пользователь вынужден уйти от них, если каждый из них имеет 3 каретки ^ символы запуска оболочки.

Два примера

1) Чтобы передать аргумент ten>one в EXE, пользователь должен запустить CMD следующим образом:

launcher.cmd ten^^^>one

Причиной этого является то, что специальные символы оболочки ^ и > интерпретируются командной оболочкой на двух уровнях: сначала в командной строке, а затем внутри CMD script. Таким образом, экранирование оболочки с символом escape-последовательности каретки ^ должно применяться дважды. Проблема в том, что это неочевидно для пользователя и выглядит уродливым.

В этом примере более приятным решением является окружение аргумента двойными кавычками. Тем не менее, это ломается для более сложных примеров, которые включают буквальную двойную кавычку в аргументе.

2) Чтобы передать аргумент "^ в EXE, пользователь должен запустить CMD следующим образом:

launcher.cmd "\"^^^^"

В моем случае я хочу поддерживать аргументы, содержащие любую последовательность символов с низким ASCII, за исключением управляющих символов, то есть кодовых точек от 0x20 до 0x7E. Я понимаю, что будут примеры, когда пользователю придется избегать специальных символов оболочки с кареткой. Тем не менее, я не хочу, чтобы пользователю приходилось использовать 3 каретки каждый раз в этих случаях только потому, что они, как правило, вызывают CMD script вместо EXE.

Я могу решить эту проблему, заменив CMD script на EXE, который делает то же самое. Однако есть ли способ изменить CMD script, чтобы он передавал свои параметры через EXE без интерпретации специальных символов оболочки?

Ответ 1

Один из способов - работать с задержкой расширения внутри партии, потому что тогда специальные символы теряют там "особые" значения.

Единственная проблема - получить параметры в переменной.

Что-то вроде этого может помочь

@echo off
setlocal DisableDelayedExpansion
rem ** At this point the delayedExpansion should be disabled
rem ** otherwise an exclamation mark in %1 can remove carets
set "param1=%~1"

setlocal EnableDelayedExpansion
rem ** Now you can use the param1, independent of the content, even with carets or quotes
rem ** but be careful with call's, because they start a second round of expansion
echo !param1!
set "tmp=!param1:~1,4!"

Теперь параметры могут быть окружены кавычками, поэтому там больше не нужны каретки. пример launcher.bat "abc > def & geh% ijk | lmn ^ opq!"

Единственным оставшимся проблематичным специальным символом является кавычка.

[Редактировать/улучшить] Я создаю другой способ получить параметр, я предполагаю, что он может принять любую строку и ваш второй пример.
Даже очень тяжелые строки, такие как

launcher "^
пусковая установка ten ^ > один
пусковая установка "&" &

@echo off
setlocal DisableDelayedExpansion
set "prompt=X"
for %%a in (1 ) do (
    @echo on
    for %%b in (4) do (
        rem #%1#
    ) 
) > XY.txt
@echo off
for /F "delims=" %%a in (xy.txt) DO (
  set "param=%%a"
)
setlocal EnableDelayedExpansion
set param=!param:~7,-4!
echo param='!param!'

Как это работает?
Единственный способ, которым я нашел расширение% 1 без расширения специальных символов, таких как "или ^, в выражении REM (для REM, который не является полностью истинным, но это другая история) Хорошо, единственная проблема заключается в том, что REM является замечанием и не имеет никакого эффекта:-)

Но если вы используете эхо на, то и повторные строки будут эхом, прежде чем они будут выполнены (выполнить для rem - хорошее слово).
Следующая проблема заключается в том, что она отображается, и вы не можете перенаправить этот отладочный вывод с обычным > debug.txt. Это также верно, если вы используете for-loop.

Хорошо, вы можете перенаправить вывод эха на с помощью вызова типа

echo on
call :myFunc > debug.txt

Но если вы вызываете функцию, вы больше не сможете получить доступ к% 1 пакетного файла.

Но с двойным циклом for, можно активировать перенаправление для вывода отладки, и по-прежнему можно получить доступ к% 1.
Я изменяю приглашение на "X", поэтому я знаю, что он всегда имеет только один символ.

Осталось только объяснить, почему я добавляю # в% 1. Это потому, что некоторые специальные символы распознаются в некоторых ситуациях даже в линии REM, очевидно;-)

rem This is a remark^
rem This_is_a_multiline^
rem "This is also a multiline"^

Итак, # подавляет возможную многострочную ситуацию.

Ответ 2

Помогает ли это:

EscapPipes.Cmd

@echo off 

:Start
    If [%1]==[] goto :eof
    @Echo %1
    shift
goto :Start

При запуске таким образом:

EscapPipes.Cmd Andy Pandy "Pudding | > < and pie"  

дает

Andy
Pandy
"Pudding | > < and pie"

Как только вы разделите кавычки, символы канала станут живыми.