如何在Windows批处理文件中将双引号行拆分为多行?

32

在Windows批处理文件中,使用^可以将长命令拆分为多行,如Long commands split over multiple lines in Windows Vista batch (.bat) file所述。

但是,如果插入符号位于双引号字符串中,则此方法不起作用。例如:

echo "A very long line I want to ^
split into two lines"

以下代码将打印出"A very long line I want to ^并告诉我split是一个未知命令。

有没有办法避免这种情况?

5个回答

28

我看到三种可能的解决方法。

1) 构建包含多个for参数的行。

@echo off
SETLOCAL EnableDelayedExpansion

set "line="
for %%a in ("line1" 
"line2"
"line3"
"line4"
) do set line=!line!%%~a
echo !line!

缺点:如果文本中有问号(?)或星号(*),则会丢失这些行。

2) 在每行末尾保留引号

@echo on
SETLOCAL EnableDelayedExpansion

set "line=line1 & x#"^
 "line2 & a#"^
 "line3 & b #"^
 "line4 & c "

set "line=!line:#" "=!"
echo !line!

每行的第一个空格很重要,因为插入符号(caret)可以作为多行字符,但也会转义第一个字符,所以引号也会被转义。


所以在构建字符串后,我会替换掉不必要的 #" "。

编辑添加:3)引号消失

setlocal EnableDelayedExpansion
echo "A very long line I want to !="!^
split into two lines"

在我看来,这是最好的方法,因为解析器首先看到引号,所以最后的关 caret 就会生效,因为它似乎在引号之外。
但是这个!="!"表达式将扩展名为= "的变量,但是这样的变量名称无法存在(等号不能作为第一个字符出现),因此它会扩展为空。

您还可以创建安全表达式,它们始终会跳出引号,独立于该行中是否有引号。
!=" ^"!

echo This multiline works !="^"!^
as expected
echo "This multiline works !="^"!^
too"

如果你想避免延迟扩展,你也可以使用-FOR-Loop,例如:

for %%^" in ("") do (
echo "This multiline works %%~"^
too"
)

答案并不简单,但这些都是一些很酷的技巧!我希望我能够获得多个赞。谢谢! - Wang Dingwei
我非常喜欢第一种方法。这种方法有什么需要注意的地方吗? - djangofan
它在像*?这样的字符上失败了,因为for循环试图将它们用作通配符。但是你可以切换到FOR /F循环。 - jeb
第一种方法对我有效,我只是删除了第4行变量周围的引号,因为我收到了一个环境变量不存在的消息。之后,它运行得很好。 - midoriha_senpai

13

最直接的答案是转义引号。它们会被打印出来,但从CMD.EXE的角度来看,它们不会功能性地引用内容。

@echo off
echo ^"A very long line I want to ^
split into two lines^"

在行中出现的任何特殊字符也必须进行转义,因为它们不再具有功能性引用。

@echo off
echo ^"A very long line I want to ^
split into two lines ^
that also contains special characters ^^ ^& ^| ^> ^< ^"

正如jeb所说,行继续符会转义下一行的第一个字符。因此,如果下一行的第一个字符恰好是特殊字符,则不应再次转义它。以下代码将成功转义&,同时也引入了一个新行。

@echo off
echo ^"A very long line I want to ^
split into two lines ^
that also contains special characters ^^ ^
& ^| ^> ^< ^"

1
+1,但我更喜欢消失的引号,因为您不需要转义特殊字符。 - jeb
我也更喜欢消失的引号 :-) - dbenham
只要你知道插入符号是转义字符,我认为这种技术更容易记忆。无需理解/启用延迟扩展,也不需要理解/记忆延迟扩展hack。但每个人都有自己的偏好。 - G-Wiz
这个比被接受的答案更好、更清晰、更易懂。你总是最棒的。 - Badr Elmers
有没有办法创建一个空行? - Bernhard Döbler

4

今天我想到了自己的一种方法,我从未在其他地方见过这种建议,所以我想在这里发布。当然,客观上说它并不优雅。我会说它有自己独特的丑陋和一些限制,但与其他选择相比,我觉得它更加"符合人体工程学"。

具体而言,这种方法具有不需要转义任何内容的独特特点,同时仍然执行传统的变量替换。这使我能够采取一些看起来完全符合我的字符串,将其跨越多行,并在每行前添加一个前缀,而不改变字符串中的任何其他内容。因此,不需要试错来确定特殊字符和转义字符之间可能如何相互作用。总体而言,我认为它减少了确定性地生成复杂的多行字符串变量所需的认知负荷,这对我很好,因为我不想费力思考和沮丧于像Windows命令解释器的细微差别等意外的复杂性。此外,在Linux Bash上可以使用类似的技术,使其有些可移植性。

注意:我没有彻底测试过它,它可能无法适用于某些用例,我不知道。但是,这个示例具有相当多看起来棘手的字符,它仍然能够正常工作,因此似乎是比较健壮的。

set IMAGE_NAME=android-arm64
set CMD=docker run --rm
set CMD=%CMD% --platform=linux
set CMD=%CMD% -v %CD%:/work
set CMD=%CMD% dockcross/%IMAGE_NAME%
set CMD=%CMD% /bin/sh -c
set CMD=%CMD% "mkdir build-%IMAGE_NAME% &&
set CMD=%CMD% cd build-%IMAGE_NAME% &&
set CMD=%CMD% cmake .. &&
set CMD=%CMD% cmake --build ."

echo %CMD%

在我的情况下,这是一个命令,所以我可以使用以下命令运行它:

%CMD%

我很乐意接受有关这种方法的任何反馈或建议。也许它还可以改进。


2
@solvingJ的方法相对于Jeb的变体而言,其明显优势在于它的操作过程十分清晰,并且左边是丑陋的命令行内容,右边则是实际内容,两者有空间上的分离。如果在左右两部分之间加入更多的空格,可以提高可读性。
set      CMD=   docker run --rm
set CMD=%CMD%     --platform=linux
set CMD=%CMD%     -v %CD%:/work
set CMD=%CMD%     dockcross/%IMAGE_NAME%
set CMD=%CMD%     /bin/sh -c
set CMD=%CMD%       "mkdir build-%IMAGE_NAME% &&
set CMD=%CMD%       cd build-%IMAGE_NAME% &&
set CMD=%CMD%       cmake .. &&
set CMD=%CMD%       cmake --build ."

很遗憾,在评论中无法演示这一点,因为多个空格会被缩减为一个空格。因此,我在回答中进行演示,实际上只是对@solvingJ答案的评论,我认为这是最好的。

-3
如果您想添加空行,请确保在第二行的^之前有一个空格
echo Hello world^ ^ 祝您今天愉快!

-1:在引用模式(未转义插入符号^)中,除了\n之外的所有字符都失去了它们的意义,因此这个方法不起作用:echo "quotes break this trick^<LF>next line - ScriptKidd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接