在 Tcl 中,如何防止变量的内容在传递给 exec 时被 shell 解释?

In Tcl, how do I prevent a variable's contents from being interpreted by the shell when passed to `exec`?

我正在维护一些旧代码,发现以下部分...

if {[catch {exec -- echo $html_email > $file} ret]} {
    puts $ret
    return 0
}

...由于 HTML 电子邮件的第一个字符为 <

而中断
couldn't read file "html>
    <title>cama_Investigate 00000560554PONY1</title>
    <style type="text/css">
    ...
    ...
    ...

被解释为 I/O 重定向运算符。以前这不是问题,因为我们开始发送一些 headers、 的电子邮件,例如

append html_email "Content-Type       : text/html; charset=us-ascii\n"
append html_email "Content-Disposition: inline\n"

我将重写所有这些以使用 Tcl 的本机文件 I/O,所以这个问题主要是学术性的:What is the proper way to guard a variable's contents from being interpreted by the shell 当传递给 exec?

我正在使用 Tcl 8.0.5 和 csh,但如果可能的话,我对一般答案很感兴趣。

Tcl 的 exec 很时髦,唉。它坚持将以 < 字符开头的参数解释为重定向。 (也有一些其他的,但你不太可能击中它们。)除了将数据写入临时文件并从中重定向之外,也没有好的通用解决方法。

set ctr 0
while 1 {
    set filename /tmp/[pid].[incr ctr].txt
    # POSIX-style flags; write-only, must create or generate error
    if {[catch {open $filename {WRONLY CREAT EXCL}} f] == 0} break
}
puts $f $html_email
close $f
exec echo <$filename >$file
file delete $filename

这太复杂了!通过改变我们使用的程序,我们可以做得更好。如果我们不使用 echo 而使用 cat,我们可以使用 exec 的 heredoc 语法:

exec cat <<$html_email >$file

因为在这种情况下,字符是通过管道直接传递的(Tcl 就是这样做的),所以出错的可能性要小得多。然而,仍然 愚蠢,因为 Tcl 完全能够直接写入文件,更便携,开销更少:

set f [open $file "w"]
puts $f $html_email
close $f

是的,这实际上是大大简化的版本,是对上面第一个示例的一般替换。让我们做一些简单的事情,这些事情显然是正确的,这样以后就不会那么令人惊讶了。

您可以间接调用预期的命令,通过 shell:

路由它
exec -- csh -c "echo '$html_email'" > $file

exec -- csh -c "exec echo '$html_email'" > $file