使用格式命令的参数扩展

Argument expansion with format command

我遇到了一个场景,我需要在 format 命令中使用参数扩展。在 Tcl8.5 中,我使用的是 {*},它的效果非常好。

但是,对于 Tcl8.4,我已经尝试过 eval。但是,没有运气。

% set x "This is Whosebug"
This is Whosebug
% format "%10s %5s %20s" {*}$x
      This    is        Whosebug
% eval format "%10s %5s %20s" $x
   %5s
% eval list format "%10s %5s %20s" $x
format %10s %5s %20s This is Whosebug
% eval [list format "%10s %5s %20s" $x]
not enough arguments for all format specifiers
% eval format "%10s %5s %20s" [list $x]
       %5s
% eval format "%10s %5s %20s" [concat $x]
       %5s

我这里的错误是什么?

请参阅文章的 eval and double substitution 部分。

正确的方法是:

eval "format {%10s %5s %20s} $x"

此处 eval 无法连接任何参数,因此解释器得到的是替换形式:format {%10s %5s %20s} This is Whosebug。您当然可以使用 eval "format \"%10s %5s %20s\" $x" 但我更喜欢尽可能避免使用反斜杠转义。


eval format "%10s %5s %20s" $x

是错误的,因为 eval 首先将字符串连接在一起,所以你最终得到:

eval format %10s %5s %20s $x

这就像说 format %10s %5s %20s $x,这就是为什么你得到你所得到的)


eval list format "%10s %5s %20s" $x

变为:

eval list format %10s %5s %20s $x

这就像说 list format %10s %5s %20s $x 并没有让 format 实际做某事。


eval [list format "%10s %5s %20s" $x]

方向正确,但 $x 没有得到扩展,所以你正在做 format "%10s %5s %20s" $x


其他人只将 $x 放入更深的列表中,或者在这种情况下不做任何更改。

如果编写诸如eval之类的concat-and-evaluate命令的调用方式不清楚,通常可以将命令替换为concat以查看有关内容待评估,例如concat format "%10s %5s %20s" $x 而不是 eval format "%10s %5s %20s" $x.

这些形式扁平化格式化字符串,使第二个格式说明符成为第一个值参数(前两个是等效的,$x 是一个扁平列表,因此与 [concat $x] 相同):

concat format "%10s %5s %20s" $x
# -> format %10s %5s %20s This is Whosebug
concat format "%10s %5s %20s" [concat $x]
# -> format %10s %5s %20s This is Whosebug
concat format "%10s %5s %20s" [list $x]
# -> format %10s %5s %20s {This is Whosebug}

此表单还插入了一个不属于此处的列表命令:

concat list format "%10s %5s %20s" $x
# -> list format %10s %5s %20s This is Whosebug

这种形式保留了格式化字符串的列表结构,可惜也保留了$x:

的列表结构
concat [list format "%10s %5s %20s" $x]
# -> format {%10s %5s %20s} {This is Whosebug}

这两种形式做了正确的事情:它们保留了格式字符串但展平了参数列表:

concat "format {%10s %5s %20s} $x"
# -> format {%10s %5s %20s} This is Whosebug
concat [list format {%10s %5s %20s}] $x
# -> format {%10s %5s %20s} This is Whosebug

文档:concat, eval, format, list

8.4 习惯用法是使用 linsert 创建要评估的命令

set x "This is Whosebug"
set args [split $x]
set cmd [linsert $args 0 format "%10s %5s %20s"]
set formatted [eval $cmd]

或者,嵌套在一行中

set formatted [eval [linsert [split $x] 0 format "%10s %5s %20s"]]