我对批处理脚本有点麻烦,需要将配置文件中的值解析为变量。
适当地匿名化,文件的相关行看起来像
<?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.
在"
上拆分字符串的正确方法是什么?
答案 0 :(得分:35)
您可以使用双引号作为分隔符,语法如下:
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
中的插入符号转义技巧绕过了封闭引号的必要性,同时仍然将序列视为单个参数。我在这里使用了一些创造性的类比,并且在整个shell中效果并不普遍。例如。你无法做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上完成的。
答案 1 :(得分:7)
编辑:这是错误的,请稍后再看我的评论: 正如Joey所说,似乎没有可能将引用用作delim,它只能用作EOL字符 这似乎是cmd.exe的FOR-LOOP解析器的效果,因为它扫描选项部分并在引用后停止扫描它,只有EOL =选项打破了这个,因为它总是读取下一个字符而没有任何预期。
您可以使用icabod等解决方法解决此问题 解决方案是用未使用的字符替换引号,但如果要接受引号内的任何字符,则不存在未使用的字符。
所以我的解决方案首先通过替换之前发生的所有事件来创建一个未使用的角色
我想使用#
替换引号,以保留引号内的所有#
并使用$R
替换它,但之后它可能会与现有的{{1}发生冲突在文本中,我首先将所有$R
替换为$
,然后绝对无碰撞。
在提取“引用”文本后,我必须将$ R和$ D替换回原始值,这就是全部。
$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
答案 2 :(得分: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
)
一点解释......第一个for
循环将整行输入%a
变量。然后将其复制到变量z
中。然后使用集合的内置搜索/替换功能再次设置z
(请注意,这里我们使用!z:"=_!
引用变量,这将替换它)。最后,我们解析这一行以获得引号之间的项目。
我希望这有点道理。
答案 3 :(得分:3)
我还没有找到办法实现这一目标。也许jeb会拥有比我更深入的知识。或者,使用=
和空格作为分隔符来删除该行,并删除结果周围的引号:
for /f "tokens=3 usebackq delims== " %G in (`...`) do @echo %~G
答案 4 :(得分:0)
我认为搜索引号周围的字符并在稍后的步骤中删除引号更容易。如果我们想从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
)
一般来说,引号本身并不是真正的分隔符,所以在大多数情况下这都可以解决问题。
答案 5 :(得分:-2)
使用^
避免使用双引号来转义字符串中的所有字符(包括空格)。这样您就可以将双引号添加为参数。
for /F Tokens^=1^,2^-5^*^ Delims^=^" %%i in ( ...
这应该有用。