在'for/F`的`delims`选项中转义双引号

Pet*_*lor 30 escaping batch-file

我对批处理脚本有点麻烦,需要将配置文件中的值解析为变量.

适当地匿名化,文件的相关行看起来像

<?define ProductShortName="Foo" ?>
Run Code Online (Sandbox Code Playgroud)

我想设置一个变量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"`)
Run Code Online (Sandbox Code Playgroud)

大部分沿着

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

什么是逃避分裂字符串的正确方法"

rka*_*rer 38

您可以使用双引号作为分隔符,其语法如下:

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

在命令行上运行时,使用tokens^=2应该给你want,并且4个令牌会让你变成一匹小马.

将该技术应用于您的原始问题,这应该在您的批处理文件中有效:

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

细节

我不是命令行解析器的怪癖的专家,但它可能有助于将通常"delims=blah tokens=blah"视为传递给FOR的单个组合参数.插入符号中的插入符号delims^=blah^ tokens^=blah绕过了封闭引号的需要,同时仍然将序列视为单个参数.我在这里使用了一些创造性的类比,并且在整个shell中效果并不普遍.例如,你不能这样做dir C:^\Program^ Files(这是有道理的,因为它^是一个有效的文件名字符).

测试用例

通过足够的转义,您可以在命令行上快速检查原始样本:

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

其他玩这个的人可能想创建一个文件testcases.txt:

blah blah "red"
     blah "green" blah
How about a "white" "unicorn"?
Run Code Online (Sandbox Code Playgroud)

并运行如下:

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

检查各种输入的结果.在这种情况下,它应该产生:

red
green
white
Run Code Online (Sandbox Code Playgroud)

最后一个例子:

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

最后,请注意我的测试是在Windows Server 2003上完成的.


jeb*_*jeb 8

编辑:这是错误的,请稍后看我的评论:正如乔伊所说,似乎没有可能将报价用作delim,它只能用作EOL字符.
这似乎是cmd.exe的FOR-LOOP解析器的效果,因为它扫描选项部分并在引用后停止扫描它,只有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!'
            )
        )
    )
)
Run Code Online (Sandbox Code Playgroud)

示例文本:
<?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
Run Code Online (Sandbox Code Playgroud)

但删除第一个引用,它的工作原理.

setlocal EnableDelayedExpansion
set "var=one"two"three"
FOR /f tokens^=1-3^ delims^=^" %%a in ("!var!") do echo %%a--%%b--%%c
Run Code Online (Sandbox Code Playgroud)


ica*_*bod 5

我不相信这是可能的 - 引号(")不能用作分隔符.

但是,一种解决方案是将整行存储在环境变量中,并使用内置的"替换"功能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
)
Run Code Online (Sandbox Code Playgroud)

一点解释......第一个for循环将整行引入%a变量.然后将其复制到变量中z.z然后使用集合的内置搜索/替换功能再次设置(注意,这里我们引用变量using !z:"=_!,替换它).最后,我们解析这一行以获取引号之间的项目.

我希望这有点道理.