TCL 脚本 - exec 将文本刷新到我的标准输出

TCL script - exec flush the text to my stdout

我如何"flush" exec 命令的 stdout 到我的脚本的 stdout 而没有 "waiting" 的结果 exec 到 return?

例如在下面的脚本中,我希望 git clone 输出立即出现在我的脚本上下文中:

#!/usr/bin/tclsh

# git outputs progress of the clone download but it isn't visible in this script's context. How can I flush it? 
exec git clone /path/to/some/repo.git

我猜我需要某种 pipe "|"tee 的组合以及文件重定向。 好像不太对。

exec 的工作是捕获命令的输出,所以我不认为你可以改变它的缓冲。这是一个在交互式 tclsh 会话中得到最好说明的测试:这两个命令都在返回输出之前等待执行过程完成,即使是我明确要求行缓冲的命令

exec sh -c {sleep 2; echo a; sleep 2; echo b; sleep 3; echo c}
exec stdbuf --output=L sh -c {sleep 2; echo a; sleep 2; echo b; sleep 3; echo c}

您将不得不打开一个管道到命令,然后逐行读取输出。这将在出现时打印上述命令的输出:

set pipe [open [list "|" sh -c {sleep 2; echo a; sleep 2; echo b; sleep 3; echo c}] r]
while {[gets $pipe line] != -1} {puts $line}
close $pipe

你这么说是什么意思"isn't visible in this script's context"?回想一下 exec 捕获 输出。默认情况下不打印。不知道你是不是只想

puts [exec git clone /path/to/some/repo.git]

但是,如果您想查看 "real-time" 中的文本,请打开一个管道并按照演示在 gets 上循环。

要立即获得输出,您需要将子命令作为管道打开。正确的(而不是 相当 显而易见的,对此我们深表歉意)的方法是使用 open |[list …]:

的这种构造
set mypipeline [open |[list git clone /path/to/some/repo.git]]

# Now to read it by line; we'll just print it out with a line number to show off
set linenum 0
while {[gets $mypipeline line] >= 0} {
    puts "Line [incr linenum]: $line"
}

# Close the pipe now we're at EOF
close $mypipeline

但是请注意,某些程序(我不知道 git 是不是一个)在管道中 运行 时会改变它们的行为,缓冲它们的输出,直到它们有一个完整的缓冲区值。 (这是 C 运行time 在输出未输出到终端时默认工作方式的一部分。)如果这是一个问题,您将不得不 运行 使用 Expect。这是一个足够大的话题,您应该寻找(并在必要时提出)一个单独的问题;唉,这在复杂性上是一个很大的变化。

另请注意,git 可能会写入其标准错误(如本问题中所述,Reading output of git push from PHP exec() function) so you might need to merge standard error into the captured standard out (as tersely documented on the exec 手册页)。

set output [exec git clone /path/to/some/repo.git 2>@1]
set mypipeline [open |[list git clone /path/to/some/repo.git 2>@1]]
# ...

也可以做 read/write 管道,但更复杂。

如果您对 TCL-program 中程序的输出不感兴趣,您也可以使用 exec 命令的 '>@' 选项将输出重定向到不同的位置,即 '> @stdout' 在你的情况下。