如何让 Tcl 的 exec 运行其参数带有空格的字符串的命令?



我想使用 pgrep从其命令行中找到一个过程的pid。在外壳中,这样做是这样做的:

pgrep -u andrew -fx 'some_binary -c some_config.cfg'

但是,当我从TCL尝试一下时,如下:

exec pgrep -u $user -fx $cmdLine

我得到:

pgrep:无效选项 - '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。

问题部分是'对TCL根本没有任何意义,部分原因是您正在失去对单词边界所在位置的控制。


首先,仔细检查这实际上有效:

exec pgrep -u $user -fx "some_binary -c some_config.cfg"

或也许此(TCL使用{},例如Unix shells使用单引号,但具有可嵌套的额外好处;这就是braces 真正的在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命令会产生列表,但它使用 canonical form ,它也是一个脚本片段,它是保证缺乏"惊喜"替换或单词边界。


如果您使用的是最新版本的TCL(特别是8.5或更高版本),则可以使用扩展。这是专门针对list专门工作的,并且摆脱了在所有情况中约99%使用eval的需求。这会改变:

eval exec $cmd

进入:

exec {*}$cmd

语义有些不同,除了 cmd实际运行相同的操作时持有规范列表时。(当您处理非规范列表时,差异是出现的, eval会做各种各样的事情 - 想象一下set cmd {ab [exit] cd}的破坏,这是一个有效但非典型的列表,而扩展只是迫使事物成为列表并使用列表并使用列表中的单词没有进一步的解释。)

由于您使用旧版本,因此必须确保将eval看到的内容转换为正确引用的TCL字符串。

单语引号什么都不做。exec不使用它们,也不通过。exec利用基础exec(3)系统调用,除非您故意使用以下内容: /bin/sh -c "some-cmd some-arg"调用外壳并将重新解释命令行。

您要做的就是构造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

最新更新