【问题标题】:TCL/Expect equivalent of Bash $@ or how to pass arguments to spawned process in TCL/ExpectTCL/Expect 相当于 Bash $@ 或如何将参数传递给 TCL/Expect 中的衍生进程
【发布时间】:2015-03-19 01:44:10
【问题描述】:

如果有人想从 Bash 调用外部程序(作为 Bash 参数传递)并传递命令行选项(也作为 Bash 参数传递),那么解决方案非常简单:

TCL_SCRIPT="$1"
shift
TCL_SCRIPT_ARGUMENTS="$@"
expect -f "$TCL_SCRIPT" "$TCL_SCRIPT_ARGUMENTS" 2>&1

在 TCL/Expect 中是否有类似的可能?

编辑: 到目前为止,我已经有了这个 hack(在 cmets 中有 Bash 等价物),它似乎正在工作。有人可以解释一下 lshift 程序吗?

# http://wiki.tcl.tk/918#pagetocc7993a2b
proc lshift {inputlist} {
  upvar $inputlist argv
  set arg  [lindex $argv 0]
  #set argv [lrange $argv 1 end] ;# below is much faster - lreplace can make use of unshared Tcl_Obj to avoid alloc'ing the result
  set argv [lreplace $argv[set argv {}] 0 0]
  return $arg
}

# BASH: TCL_SCRIPT="$1"
set script [lindex $argv 0]

# BASH: shift
lshift argv

# BASH: TCL_SCRIPT_ARGUMENTS="$@"
set arguments $argv

【问题讨论】:

  • 该脚本,如所写,不适用于带空格的参数。只是为了记录。您不会将$@ 存储在变量中,您只需在需要的地方使用它。除此之外,我不确定我是否理解这个问题。您是否在询问如何通过expect 将参数传递给命令spawned?你的expect 脚本是什么样的?
  • 如果你使用的是 tcl8.6,你可以使用exec {*}$::argv 2>@1。否则,总是有eval exec $::argv 2>@1
  • 我的意思是,如果你有一个带空格的参数,当你把它塞进一个字符串时,你将失去使$@ 安全的单词扩展行为。请尝试以下操作以了解我的意思。 c() { printf 'argc: %s\n' "$#"; printf '$@: %s\n' "$@"; TCL_SCRIPT_ARGUMENTS="$@"; printf 'TCL_SCRIPT_ARGUMENTS: %s\n' "$TCL_SCRIPT_ARGUMENTS"; }; c foo 'bar baz' quux(将set -x 添加到该函数的开头也可以更清楚地看到它。)
  • printf 反复使用该格式来消耗其所有输入。因此,当您说printf '%s\n' a b c 时,它需要使用%s\n 格式三次才能使用它。大多数命令都不是这样工作的。
  • @WakanTanka {*}$vartcl 等价于 "$@" :它将扩展列表,以便每个列表元素都是正确引用的参数。所以,假设我们有set lst {item1 "item 2" item3}MyProc {*}$lst 相当于MyProc item1 {item 2} item3

标签: bash arguments command-line-arguments expect argument-passing


【解决方案1】:

从字面上翻译你的例子

set program [lindex $argv 0]
set arguments [lrange $argv 1 end]
spawn $program {*}$arguments

{*} 是 Tcl 的“列表扩展”语法(Tcl's 12 rules of syntax 的规则 5)。它将列表拆分为当前命令中的元素。

如果$argvfoo bar baz,那么

spawn [lindex $argv 0] [lrange $argv 1 end]

将使用 1 个参数调用foo"bar baz"

spawn [lindex $argv 0] {*}[lrange $argv 1 end]

将使用 2 个参数调用 foo"bar""baz"


切线,我会像这样对你的lshift proc 进行编码:

proc lshift {varname} {      
    upvar 1 $varname var
    set var [lassign $var first]
    return $first
}

然后:

expect1.6> set argv {foo bar baz}
foo bar baz
expect1.7> set script [lshift argv]
foo
expect1.8> set script
foo
expect1.9> set argv
bar baz

【讨论】:

  • 感谢您的回复。您能解释一下{*} 在这种情况下的含义吗?
【解决方案2】:

我发现@glenn jackman 的答案对于较旧的 TCL,即 8.5 之前的版本,特别是对于运行 Expect scipts 的较旧的 Unicies 来说是不够的。我去搜了一下,发现一个说明是8.5之前的TCL中没有“lassign”功能:(http://wiki.tcl.tk/1530,在“8.5之前的Tcl中,foreach用于实现lassign的功能:”一节)。

在同一页上,在标题“示例:Perl-ish shift”下,有一句话说明“另一方面,Hemang Lavana 观察到 TclXers 已经有 lvarpop ::argv,这是 shift 的精确同义词。” ) 重定向到此页面的“lvarpop”:http://wiki.tcl.tk/1965

该页末尾的代码是一个简化版本,为方便起见,在此复制:

proc lvarpop {upVar {index 0}} {
    upvar 1 $upVar list
    if {![info exists list]} { return "-1" }
    set top [lindex $list $index]
    set list [lreplace $list $index $index]
    return $top
}

在我的测试中,这适用于零到几乎无限数量的空格分隔参数。对于像我这样的 TCL 挑战者来说,这是天赐之物。

【讨论】:

    猜你喜欢
    • 2011-11-14
    • 2015-03-21
    • 2015-05-21
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    • 2014-06-07
    相关资源
    最近更新 更多