Как избежать cmd.exe интерпретировать специальные символы оболочки, такие как <> ^ - PullRequest
12 голосов
/ 28 сентября 2010

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

launcher.exe paramX paramY %*

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

launcher.cmd param1 param2 param3 [...]

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

Два примера

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

launcher.cmd ten^^^>one

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

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

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

launcher.cmd "\"^^^^"

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

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

Ответы [ 2 ]

8 голосов
/ 06 октября 2010

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

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

Как-то так может помочь

@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!"

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

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

launcher "^
launcher ten ^> one
launcher "&" ^ &

@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 является замечанием и не имеет никакого эффекта :-)

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

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

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"^

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

0 голосов
/ 29 сентября 2010

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

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"

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

...