【问题标题】:Tcl: how does this proc return a value?Tcl:这个过程如何返回一个值?
【发布时间】:2020-05-19 17:52:28
【问题描述】:

我正在修改下面的代码,但我不知道它是如何工作的 - 欢迎启发。问题是其中有一个 proc (cygwin_prefix),它旨在通过

创建一个命令
  1. 不修改文件名,或
  2. 在文件名前添加一个字符串

问题是 proc 没有返回任何内容,但脚本仍然神奇地工作。如何?具体来说,set command [cygwin_prefix filter_g] 行实际上是如何正确设置command 的?

对于背景,脚本只执行 filter_g < foo.txt > foo.txt.temp。然而,从历史上看(这似乎不再是这种情况)这在 Cygwin 上不起作用,所以它改为运行 /usr/bin/env tclsh filter_g < foo.txt > foo.txt.temp。所示脚本在 Linux (Tcl 8.5) 和 Cygwin (Tcl 8.6) 上都“有效”。

谢谢。


#!/usr/bin/env tclsh

proc cygwin_prefix { file } {
    global cygwin
    if {$cygwin} {
        set status [catch { set fpath [eval exec which $file] } result ]
        if { $status != 0 } {
            puts "which error: '$result'"
            exit 1
        }
        set file "/usr/bin/env tclsh $fpath"
    }
    set file
}

set cygwin 1
set filein foo.txt
set command [cygwin_prefix filter_g]
set command "$command < $filein > $filein.temp"
set status [catch { eval exec $command } result ]
if { $status != 0 } {
    puts "filter error: '$result'"
    exit 1
}
exit 0

【问题讨论】:

  • 相关搜索:"tcl proc return".
  • @user2864740 - 谢谢 - 然后解释了最后的set。如果你想发布,我会接受。

标签: tcl


【解决方案1】:

你的问题的关键是双重的。

  1. 如果一个过程没有以return(或error,当然)结束,则该过程的结果是该过程主体中执行的最后一个命令的结果。

    (如果没有执行任何命令,则为空字符串。在这种情况下不适用。)

    这对于诸如只包装命令的过程之类的东西很有用:

    proc randomPick {list} {
        lindex $list [expr { int(rand() * [llength $list]) }]
    }
    

    是的,您可以添加 return […],但它只会为这么短的内容添加混乱。

  2. set 命令带有一个参数,读取命名变量并在 var 中生成值作为结果。

    很久以前(现在大约 30 年)这是读取所有变量的方式。对我们来说幸运的是,添加了 $… 语法,这在 99.99% 的所有情况下都更加方便。唯一有意义的地方是计算变量名,但大多数时候即使在那里也有更好的选择。

您在过程结束时看到的带有set file 而不是return $file 的表单在一段时间内流行了一段时间,因为它产生了稍微 更短的字节码。通过一个 无法访问 操作码。字节码的差异现在消失了。也没有性能差异,而且从来没有(尤其是与启动子进程并通常执行大量系统调用的exec 的权重相比!)

【讨论】:

    【解决方案2】:

    不需要eval 用于exec。将命令构建为列表将保护您免受例如包含空格的路径项的影响。这是一个快速重写来演示:

    proc cygwin_prefix { file } {
        if {$::cygwin} {
            set status [catch { set fpath [exec which $file] } result]
            if { $status != 0 } {
                error "which error: '$result'"
            }
            set file [list /usr/bin/env tclsh $fpath]
        }
        return $file
    }
    
    set cygwin 1
    set filein foo.txt
    set command [cygwin_prefix filter_g]
    lappend command "<" $filein ">" $filein.temp
    
    set status [catch { exec {*}$command } result]
    if { $status != 0 } {
        error "filter error: '$result'"
    }
    

    这使用{*} 将列表分解为单个单词以传递给exec

    【讨论】:

    • 我会使用exec {*}$command &lt; $filein &gt; $filein.temp 而不是使用单独的lappend
    • 另外,几乎所有使用eval(但不是namespace evalinterp eval)都是可能错误,而扩展是可能更好。偶尔你仍然会看到一些正确的选择,但现在很少见了。这很好,因为编写无危险的eval exec 比看起来更难。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-14
    • 2023-04-08
    • 2013-07-02
    • 1970-01-01
    • 2017-06-18
    • 1970-01-01
    相关资源
    最近更新 更多