web-dev-qa-db-ja.com

「for / F」の「delims」オプションで二重引用符をエスケープする

構成ファイルから変数に値を解析する必要があるバッチスクリプトに少し問題があります。

適切に匿名化された、ファイルの関連行は次のようになります

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

"で文字列を分割するためにエスケープする正しい方法は何ですか?

29
Peter Taylor

次のような構文では、二重引用符を区切り文字として使用できます。

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で行われたことに注意してください。

38
rkagerer

これが可能だとは思わない-引用符(")を区切り文字として使用することはできません。

ただし、1つの解決策は、行全体を環境変数に格納し、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:"=_!を使用して変数を参照していることに注意してください)。最後に、この単一行を解析して、引用符の間の項目を取得します。

それが何らかの意味を成すことを願っています。

5
icabod

私はそれを可能にする方法を見つけていません。多分ジェブは私が持っているよりも多くの深い知識を持っている。あるいは、=とスペースを区切り文字として使用して行を切り詰め、結果の前後の引用符を削除します。

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

引用符を囲む文字を検索し、後の手順で引用符を削除する方が簡単だと思います。 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
)

一般に、引用符自体は実際の区切り文字ではないため、ほとんどの場合、これでうまくいきます。

0
user3049378