Подтвердить что ты не робот

Экранирование двойной кавычки в опции `delims`` for/F`

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

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

<?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.

Каков правильный способ избежать этого, чтобы разбить строку на "?

4b9b3361

Ответ 1

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

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.

Ответ 2

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

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

Итак, мое решение сначала создает неиспользуемый символ, заменяя все предыдущие вхождения.
Я хочу использовать # для замены кавычек, ut, чтобы сохранить все # внутри кавычек, заменив их раньше на $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, как ожидалось

EDIT: есть способ!
Я всегда тестировал такие вещи (и это терпит неудачу)

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

Ответ 3

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

Однако одно решение состоит в том, чтобы сохранить всю строку в переменной среды и использовать встроенную функциональность "заменить" 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:"=_!, которая выполняет замену). Наконец, мы анализируем эту единственную строку, чтобы получить элемент между кавычками.

Я надеюсь, что это имеет какой-то смысл.

Ответ 4

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

for /f "tokens=3 usebackq delims== " %G in (`...`) do @echo %~G

Ответ 5

Я думаю, что в основном проще искать символы, которые окружают кавычки, и отбрасывать цитату на более позднем этапе. Если мы хотим извлечь значения из определенной строки в файле 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
)

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

Ответ 6

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

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

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