Экранирование двойных кавычек в опции `delims` для` for / F` - PullRequest
28 голосов
/ 22 сентября 2011

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

При соответствующей анонимности соответствующая строка файла выглядит как

<?define ProductShortName="Foo" ?>

Я хочу установить переменную на Foo.Строка ProductShortName достаточно уникальна, чтобы получить строку с findstr, но тогда мне нужно извлечь значение.Правильный подход выглядит как for /F, но все следующие ошибки приводят к ошибкам:

for /F "delims=^" usebackq" %%G in (`findstr /L "ProductShortName" "%~dp0Installer\Branding.wxi"`)
for /F "delims="" usebackq" %%G in (`findstr /L "ProductShortName" "%~dp0Installer\Branding.wxi"`)
for /F "delims=\" usebackq" %%G in (`findstr /L "ProductShortName" "%~dp0Installer\Branding.wxi"`)
for /F 'delims=^" usebackq' %%G in (`findstr /L "ProductShortName" "%~dp0Installer\Branding.wxi"`)
for /F 'delims=" usebackq' %%G in (`findstr /L "ProductShortName" "%~dp0Installer\Branding.wxi"`)
for /F "delims=" usebackq" %%G in (`findstr /L "ProductShortName" "%~dp0Installer\Branding.wxi"`)

в основном по линиям

usebackq" %G in (`findstr /L "ProductShortName" "C:\foo\bar\Installer\Branding.wxi"`) was unexpected at this time.

Какой правильный способ избежать его разделениястрока на "?

Ответы [ 6 ]

37 голосов
/ 04 ноября 2012

Вы можете использовать двойные кавычки в качестве разделителя с синтаксисом вроде:

FOR /F delims^=^"^ tokens^=2 %G IN ('echo I "want" a "pony"') DO @ECHO %G

При запуске в командной строке tokens^=2 даст вам want, а 4 токена - пони.

Применяя методику к исходному вопросу, это должно работать в вашем командном файле:

FOR /F delims^=^"^ tokens^=2 %%G IN ('FINDSTR /L "ProductShortName" "data.txt"')

информация

Я не эксперт по причудам синтаксического анализатора командной строки , но это может помочь представить обычный "delims=blah tokens=blah" как единый комбинированный аргумент, передаваемый FOR. Трюк с экранированием каретки в delims^=blah^ tokens^=blah обходит необходимость заключать в кавычки, все же рассматривая последовательность как один аргумент. Я использовал немного творческой аналогии, и эффект не универсален для всей оболочки. Например. вы не можете сделать dir C:^\Program^ Files (что имеет смысл, поскольку ^ является допустимым символом имени файла).

Контрольные примеры

При достаточном экранировании вы можете быстро проверить исходный образец в командной строке:

FOR /F delims^=^"^ tokens^=2 %G IN ('echo ^^^<?define ProductShortName="Foo" ?^^^>') DO @ECHO %G

Другие, кто играет с этим, могут захотеть создать файл testcases.txt :

blah blah "red"
     blah "green" blah
How about a "white" "unicorn"?

и запустите что-то вроде:

FOR /F delims^=^"^ tokens^=2 %G IN (testcases.txt) DO @ECHO %G

, чтобы проверить результаты для различных входных данных. В этом случае он должен дать:

red
green
white

Последний пример:

FOR /F delims^=^"^ tokens^=2 %G IN ('FINDSTR /L "unicorn" "testcases.txt"') ^
DO @ECHO The unicorn is %G.

Наконец, обратите внимание, что мое тестирование было выполнено на Windows Server 2003.

7 голосов
/ 23 сентября 2011

РЕДАКТИРОВАТЬ: Это неправильно, см. Мой комментарий позже : Как сказал Джои, похоже, нет возможности использовать кавычку в качестве разделителя, она может использоваться только как символ EOL.
Это кажетсябыть эффектом синтаксического анализатора FOR-LOOP cmd.exe, так как он сканирует часть параметров и останавливает сканирование после кавычки, только параметр EOL = прерывает это, поскольку он всегда читает следующий символ без каких-либо исключений.

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

Таким образом, мое решение сначала создает неиспользуемый символ, заменяя все предыдущие вхождения.
Я хочу использовать # для замены кавычек, но для сохранения всех # внутри кавычек и замены егораньше с $R, но затем он может столкнуться с существующим $R в тексте, поэтому я сначала заменяю все $ на $D, затем он абсолютно свободен от столкновений.
После извлечения "цитируемого"текст, я должен заменить $ R и $ D на их исходные значения, вот и все.

@echo off
setlocal EnableDelayedExpansion

for /F "tokens=1,2" %%1 in ("%% #") DO (
    for /f "tokens=* usebackq" %%a in ("datafile.txt") do (
        set "z=%%a"
        set "z=!z:$=$D!"
        set "z=!z:#=$R!"
        set "z=!z:"=#!"
        for /f "tokens=1-3 delims=#" %%a in ("!z!") do (
            set "value=%%b"
            if defined value (
                set "value=!value:$R=#!"
                set "value=!value:$D=$!"
                echo result='!value!'
            )
        )
    )
)

Пример текста:
<?define ProductShortName="Two #$* $D $R" ?>
приводит к Two #$* $D $R, как и ожидалось

РЕДАКТИРОВАТЬ: Есть способ!
Я всегда проверял такие вещи (и это не удается)

setlocal EnableDelayedExpansion
set "var=one"two"three"
FOR /F ^"tokens^=1-3^ delims^=^"^" %%a in ("!var!") do echo %%a--%%b--%%c

Но, удалив первую цитату, все работает.

setlocal EnableDelayedExpansion
set "var=one"two"three"
FOR /f tokens^=1-3^ delims^=^" %%a in ("!var!") do echo %%a--%%b--%%c
5 голосов
/ 22 сентября 2011

Я не верю, что это возможно - цитата (") не может быть использована в качестве разделителя.

Однако одним из решений является сохранение всей строки в переменной окружения и использование встроенной функции «замены» set для замены кавычки чем-то другим, например _. Затем вы можете использовать другой цикл for только для этой строки, чтобы разделить новый разделитель:

setlocal EnableDelayedExpansion
for /f "tokens=* usebackq" %%a in (`...`) do (
    set z=%%a
    set z=!z:"=_!
    for /f "tokens=1-3 delims=_" %%a in ("!z!") do echo %%b
)

Небольшое объяснение ... первый цикл for получает всю строку в переменную %a. Затем это копируется в переменную z. z затем устанавливается снова, используя встроенную функцию поиска / замены наборов (обратите внимание, что здесь мы ссылаемся на переменную, используя !z:"=_!, который выполняет замену). Наконец, мы анализируем эту единственную строку, чтобы получить элемент между кавычками.

Надеюсь, в этом есть какой-то смысл.

3 голосов
/ 22 сентября 2011

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

for /f "tokens=3 usebackq delims== " %G in (`...`) do @echo %~G
0 голосов
/ 19 августа 2014

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

for /F Tokens^=1^,2^-5^*^ Delims^=^" %%i in ( ...

Это должно работать.

0 голосов
/ 29 ноября 2013

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

<line x0="745" y0="1162" x1="1203" y1="1166"/>

Мы поступим следующим образом

SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "tokens=3,5,7,9 delims==/ " %%i IN ('FINDSTR line %1') DO (
SET x0=%%~i
SET y0=%%~j
SET x1=%%~k
SET y1=%%~l
)

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

...