如何向 CMake 的 execute_process 传递包含分号的参数?
可以。execute_process() 能把含分号的字符串作为一个参数传给目标进程。最简单的写法是在 COMMAND 中给这个参数加双引号。如果需要先组装参数列表,就要在列表中保留 \;,这样展开时才能得到普通分号。这里不需要 execute_process() 专用的转义序列。
直接传递:给单个参数加双引号
假设 foo 是可执行程序,bar 和 with;semicolon 是它的两个参数:
execute_process(
COMMAND foo bar "with;semicolon"
)
参数来自变量时,也用双引号包住变量:
set(value "with;semicolon")
execute_process(
COMMAND foo bar "${value}"
)
CMake 会将双引号包住的内容作为一个参数传入命令,并保留其中的分号。目标进程收到的是 with;semicolon,不含外层双引号。参见 CMake 参数语法。
必须使用参数列表时:保留一层分号转义
如果需要继续使用 execute_process(COMMAND ${args}),可以这样构造列表:
set(args
"foo"
"bar"
"with\\;semicolon"
)
execute_process(COMMAND ${args})
CMake 会分两步处理:
- 解析
"with\\;semicolon"时,\\生成一个字面反斜杠,所以变量中保存的是with\;semicolon。 - 展开未加引号的
${args}时,CMake 按列表拆分参数。遇到\;,会保留分号,并去掉用于转义的反斜杠。
此时,args 保存的字符串相当于:
foo;bar;with\;semicolon
展开后,foo 是程序名,后面两个参数分别是 bar 和 with;semicolon。具体规则见 CMake 的列表展开规则。
如果含分号的值来自变量,可以先转义,再放入列表:
set(value "with;semicolon")
string(REPLACE ";" "\\;" escaped_value "${value}")
set(args "foo" "bar" "${escaped_value}")
execute_process(COMMAND ${args})
这种写法适用于构造列表后直接展开一次的情况。如果中间还经过函数转发或其他列表操作,需要检查转义是否已被提前消耗。
为什么原来的引号没有作用
下面的写法在 set() 调用中给各个值加了双引号:
set(args "foo" "bar" "with;semicolon")
set() 会用分号连接这些值,保存为:
foo;bar;with;semicolon
变量不会记录源码中哪些内容曾被双引号包围。随后执行:
execute_process(COMMAND ${args})
未加引号的 ${args} 会按分号拆分,with 和 semicolon 就成了两个参数。
在字符串中加入 \" 也不能保护分号。变量展开产生的引号只是普通字符,不会被重新解析为 CMake 的引用语法。
也不要改成:
execute_process(COMMAND "${args}")
这会把整个变量的内容,包括其中的分号,作为一个程序名传入,无法按预期区分程序名和参数。
注意事项
直接使用 "${value}" 时,变量中应保存原始的 with;semicolon。只有通过列表展开来传递参数时,才需要保留列表层面的 \;。
通常,execute_process() 直接通过操作系统接口启动程序,无须手动拼接带 shell 引号的命令行。如果显式调用 shell,或在 Windows 上执行 .bat、.cmd 脚本,还需要考虑命令解释器自身的解析规则。参见 execute_process() 官方说明。
问题示例中的引号和括号没有闭合,\n 还会引入换行。如果目标参数只是 with;semicolon,应去掉这些额外字符。
备注:内容仅供参考。
