【问题标题】:Escaping double-quote in `delims` option of `for /F`在 `for /F` 的 `delims` 选项中转义双引号
【发布时间】:2011-11-22 21:12:14
【问题描述】:

我在处理需要将配置文件中的值解析为变量的批处理脚本时遇到了一些问题。

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

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

转义它以拆分"上的字符串的正确方法是什么?

【问题讨论】:

  • 幽默一下,试试这个:for /F "USEBACKQ tokens=3 delims= =" %%G in (findstr /L ProductShortName "%~dp0Installer\Branding.wxi"`) DO (SET var=%%~G)

标签: batch-file escaping


【解决方案1】:

我还没有找到可行的方法。也许 jeb 比我拥有更深入的知识。或者,使用= 和空格作为分隔符来分割行,然后删除结果周围的引号:

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

【讨论】:

  • 这看起来是一种很有前途的方法,但我试图获得的价值可能会有空格。我将尝试使用delims==? 走这条路线,然后在使用%~ 之前修剪空间。
  • 这就是目的 ;-)。虽然我现在想不出其他或更强大的方法,而不是替换引号并在之后拆分(icabod 的解决方案)或拆分引号并删除它们
【解决方案2】:

我认为这是不可能的 - 引号 (") 不能用作分隔符。

但是,一种解决方案是将整行存储在环境变量中,并使用 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:"=_! 引用变量,它执行替换)。最后,我们解析这一行以获得引号之间的项目。

我希望这有某种意义。

【讨论】:

  • 快速搜索了一下这个链接,它做了类似的事情,但是用!替换了引用,这可能有用:wiert.wordpress.com/2010/09/02/…
  • 是的,这是有道理的。我采用了 Joey 的方法,将 " " 替换为空字符串,这更简单,但对空格更改的鲁棒性稍差。
  • OP将此标记为答案,但这是错误的。正如下面两个实例所解释的,有一种方法:使用 ^ 转义等号、双引号以及标记和分隔符之间的空格。通过这样做,您可以删除整个 tokens/delims 参数周围的双引号。
【解决方案3】:

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

示例文本:
&lt;?define ProductShortName="Two #$* $D $R" ?&gt;
结果到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

【讨论】:

    【解决方案4】:

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

    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"')
    

    详情

    我不是 quirks of the command line parser 方面的专家,但将通常的 "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 上完成的。

    【讨论】:

    • 感谢您的回答,但我是否遗漏了什么或者这与 jeb 的最终答案相同?
    • 其实是一样的技术。我想也许我更清楚地处理了它(目的是帮助未来偶然发现这个问题的程序员),但我会让你来评判:-)。
    • 你就是男人!在那个问题上,这是最好的=最失败的+最简单的解决方案!!!如果我是提问者,我肯定会把答案改成你的!
    • OK +1 到目前为止一切顺利。现在,假设我想同时使用空格和双引号作为分隔符。
    • 非常感谢。任何线索如何将跳过参数插入此解决方案?
    【解决方案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 ( ...
      

      这应该可行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-17
        • 2020-11-26
        • 1970-01-01
        • 1970-01-01
        • 2011-04-19
        相关资源
        最近更新 更多