如何让 Tcl 的 exec 执行 运行 一个命令,其参数用空格引用字符串?
How do I get Tcl's exec to run a command whose arguments have quoted strings with spaces?
我想使用 pgrep
从命令行查找进程的 pid。在shell中,这样做是这样的:
pgrep -u andrew -fx 'some_binary -c some_config.cfg'
但是当我从 Tcl 尝试这个时,像这样:
exec pgrep -u $user -fx $cmdLine
我得到:
pgrep: invalid option -- 'c'
这是有道理的,因为它看到了这个:
pgrep -u andrew -fx some_binary -c some_config.cfg
但是我加上单引号也是一样的:
exec pgrep -u $user -fx '$cmdLine'
这也是有道理的,因为单引号对 Tcl 来说并不特殊。我认为它认为 'some_binary
是一个参数,然后是 -c
,然后是 some_config.cfg'
。
我也试过:
exec pgrep -u $user -fx {$cmdLine}
和
set cmd "pgrep -u $user -fx '$cmdLine'"
eval exec $cmd
无济于事。
根据我的阅读,似乎 Tcl 8.5+ 中的 {*}
功能可能有所帮助,但我公司的基础架构运行 Tcl 8.0.5。
由于您使用的是旧版本,因此您必须确保 eval
看到的内容将被转换为正确引用的 Tcl 字符串。
单引号什么都不做。 exec
没有使用它们,也没有传递它们。 exec
利用底层的 exec(3)
系统调用,除非您有意使用类似以下的内容,否则不会进行任何参数解释: /bin/sh -c "some-cmd some-arg"
其中调用 shell 并将重新解释命令行.
您需要做的是构造一个字符串,eval
将其解释为带引号的 Tcl 字符串。对于这些结构,您可以使用“{part1 part2}”或“\"part1 part2\"”。
首先,一个小测试脚本来验证参数是否正确传递:
#!/bin/bash
for i in "$@"; do
echo $i
done
然后是 Tcl 脚本:
#!/usr/bin/tclsh
exec ./t.sh -u andrew -fx "some_binary -c some_config.cfg" >@ stdout
eval exec ./t.sh -u andrew -fx "{some_binary -c some_config.cfg}" \
>@ stdout
eval exec ./t.sh -u andrew -fx "\"some_binary -c some_config.cfg\"" \
>@ stdout
# the list will be converted to a string that is already properly
# quoted for interpretation by eval.
set cmd [list ./t.sh -u andrew -fx "some_binary -c some_config.cfg"]
eval exec $cmd >@ stdout
部分原因是 '
对 Tcl 毫无意义,部分原因是您失去了对单词边界位置的控制。
首先,仔细检查这是否有效:
exec pgrep -u $user -fx "some_binary -c some_config.cfg"
或者也许这个(Tcl 使用 {
…}
就像 Unix shell 使用单引号但具有可嵌套的额外好处;这就是大括号 really 在 Tcl 中执行):
exec pgrep -u $user -fx {some_binary -c some_config.cfg}
应该起作用的是:
set cmdLine "some_binary -c some_config.cfg"
exec pgrep -u $user -fx $cmdLine
您将 cmdLine
设置为 的位置 您想要的字符(如果您不确定,请打印出来检查;重要的是变量中的值,而不是您在脚本中编写的引用版本)。我将使用下面的 set cmdLine "…"
表格,但实际上使用您需要的任何东西才能正常工作。
现在,如果你要通过这个过去 eval
,那么你应该使用 list
添加额外的引号,以确保安全:
set cmdLine "some_binary -c some_config.cfg"
set cmd [list pgrep -u $user -fx $cmdLine]
eval exec $cmd
list
命令生成列表,但它使用 规范形式 这也是 保证的脚本片段 缺少“惊喜”替换或单词边界。
如果您使用的是较新版本的 Tcl(特别是 8.5 或更高版本),则可以使用扩展。这旨在 专门 与 list
一起很好地工作,并且在大约 99% 的情况下无需使用 eval
。这会改变:
eval exec $cmd
进入:
exec {*}$cmd
语义有点不同 除了 当 cmd
持有规范列表时,它们实际上 运行 相同的操作。 (当你处理非规范列表时,差异就出现了,其中 eval
会做各种各样的事情——想象一下 set cmd {ab [exit] cd}
的破坏,这是一个有效但非规范的列表——而扩展只是强制事情是一个列表,并使用列表中的单词而不作进一步解释。)
我想使用 pgrep
从命令行查找进程的 pid。在shell中,这样做是这样的:
pgrep -u andrew -fx 'some_binary -c some_config.cfg'
但是当我从 Tcl 尝试这个时,像这样:
exec pgrep -u $user -fx $cmdLine
我得到:
pgrep: invalid option -- 'c'
这是有道理的,因为它看到了这个:
pgrep -u andrew -fx some_binary -c some_config.cfg
但是我加上单引号也是一样的:
exec pgrep -u $user -fx '$cmdLine'
这也是有道理的,因为单引号对 Tcl 来说并不特殊。我认为它认为 'some_binary
是一个参数,然后是 -c
,然后是 some_config.cfg'
。
我也试过:
exec pgrep -u $user -fx {$cmdLine}
和
set cmd "pgrep -u $user -fx '$cmdLine'"
eval exec $cmd
无济于事。
根据我的阅读,似乎 Tcl 8.5+ 中的 {*}
功能可能有所帮助,但我公司的基础架构运行 Tcl 8.0.5。
由于您使用的是旧版本,因此您必须确保 eval
看到的内容将被转换为正确引用的 Tcl 字符串。
单引号什么都不做。 exec
没有使用它们,也没有传递它们。 exec
利用底层的 exec(3)
系统调用,除非您有意使用类似以下的内容,否则不会进行任何参数解释: /bin/sh -c "some-cmd some-arg"
其中调用 shell 并将重新解释命令行.
您需要做的是构造一个字符串,eval
将其解释为带引号的 Tcl 字符串。对于这些结构,您可以使用“{part1 part2}”或“\"part1 part2\"”。
首先,一个小测试脚本来验证参数是否正确传递:
#!/bin/bash
for i in "$@"; do
echo $i
done
然后是 Tcl 脚本:
#!/usr/bin/tclsh
exec ./t.sh -u andrew -fx "some_binary -c some_config.cfg" >@ stdout
eval exec ./t.sh -u andrew -fx "{some_binary -c some_config.cfg}" \
>@ stdout
eval exec ./t.sh -u andrew -fx "\"some_binary -c some_config.cfg\"" \
>@ stdout
# the list will be converted to a string that is already properly
# quoted for interpretation by eval.
set cmd [list ./t.sh -u andrew -fx "some_binary -c some_config.cfg"]
eval exec $cmd >@ stdout
部分原因是 '
对 Tcl 毫无意义,部分原因是您失去了对单词边界位置的控制。
首先,仔细检查这是否有效:
exec pgrep -u $user -fx "some_binary -c some_config.cfg"
或者也许这个(Tcl 使用 {
…}
就像 Unix shell 使用单引号但具有可嵌套的额外好处;这就是大括号 really 在 Tcl 中执行):
exec pgrep -u $user -fx {some_binary -c some_config.cfg}
应该起作用的是:
set cmdLine "some_binary -c some_config.cfg"
exec pgrep -u $user -fx $cmdLine
您将 cmdLine
设置为 的位置 您想要的字符(如果您不确定,请打印出来检查;重要的是变量中的值,而不是您在脚本中编写的引用版本)。我将使用下面的 set cmdLine "…"
表格,但实际上使用您需要的任何东西才能正常工作。
现在,如果你要通过这个过去 eval
,那么你应该使用 list
添加额外的引号,以确保安全:
set cmdLine "some_binary -c some_config.cfg"
set cmd [list pgrep -u $user -fx $cmdLine]
eval exec $cmd
list
命令生成列表,但它使用 规范形式 这也是 保证的脚本片段 缺少“惊喜”替换或单词边界。
如果您使用的是较新版本的 Tcl(特别是 8.5 或更高版本),则可以使用扩展。这旨在 专门 与 list
一起很好地工作,并且在大约 99% 的情况下无需使用 eval
。这会改变:
eval exec $cmd
进入:
exec {*}$cmd
语义有点不同 除了 当 cmd
持有规范列表时,它们实际上 运行 相同的操作。 (当你处理非规范列表时,差异就出现了,其中 eval
会做各种各样的事情——想象一下 set cmd {ab [exit] cd}
的破坏,这是一个有效但非规范的列表——而扩展只是强制事情是一个列表,并使用列表中的单词而不作进一步解释。)