proc出力をtcl内のファイルにリダイレクトするにはどうすればよいですか。たとえば、proc fooがあり、foo出力をファイルバーにリダイレクトしたい場合です。しかし、この結果を得ました
% proc foo {} { puts "hello world" }
% foo
hello world
% foo > bar
wrong # args: should be "foo"
書き込むチャネルの名前を取得するようにコードを変更できない場合(最も堅牢なソリューション)、トリックを使用してstdout
をファイルにリダイレクトできます:reopening 。
proc foo {} { puts "hello world" }
proc reopenStdout {file} {
close stdout
open $file w ;# The standard channels are special
}
reopenStdout ./bar
foo
reopenStdout /dev/tty ;# Default destination on Unix; Win equiv is CON:
これを行うと、最初のstdout
がどこに向けられたかがわからなくなることに注意してください(コピーを保存するためにTclXの dup
を使用しない限り)。
これは機能するはずです:
% proc foo {} { return "hello world" }
% foo
hello world
% exec echo [foo] > bar
% exec cat bar
hello world
% exec echo [foo] >> bar
% exec cat bar
hello world
hello world
%
一般的に、Donal Fellowsが提案するstdoutリダイレクトをお勧めします 彼の答え 。
時々これは不可能かもしれません。たぶん、Tclインタープリターは、出力がどこに行くべきかという素晴らしいアイデアを持っている複雑なアプリケーションにリンクされていて、stdoutチャネルを復元する方法がわかりません。
そのような場合は、puts
コマンドを再定義してみてください。これを行う方法のコード例を次に示します。プレーンTclでは、コマンドは 名前を変更 安全な名前に再定義し、目的の機能に応じて、元のコマンドを安全な名前で呼び出すラッパーprocを作成することもできます。
proc redirect_file {filename cmd} {
rename puts ::tcl::orig::puts
set mode w
set destination [open $filename $mode]
proc puts args "
uplevel \"::tcl::orig::puts $destination \$args\"; return
"
uplevel $cmd
close $destination
rename puts {}
rename ::tcl::orig::puts puts
}
テキストを変数にリダイレクトすることもできます。
proc redirect_variable {varname cmd} {
rename puts ::tcl::orig::puts
global __puts_redirect
set __puts_redirect {}
proc puts args {
global __puts_redirect
set __puts_redirect [concat $__puts_redirect [lindex $args end]]
set args [lreplace $args end end]
if {[lsearch -regexp $args {^-nonewline}]<0} {
set __puts_redirect "$__puts_redirect\n"
}
return
}
uplevel $cmd
upvar $varname destination
set destination $__puts_redirect
unset __puts_redirect
rename puts {}
rename ::tcl::orig::puts puts
}
Tcl'ers Wikiには別の より複雑なアプリケーションでputs
を再定義する興味深い例 があります。多分これも刺激的です。
この方法でも試すことができます
% proc foo {} { return "hello world" }
% foo
hello world
% set fd [open "a.txt" w]
file5
% set val [foo]
hello world
% puts $fd $val
% close $fd
% set data [exec cat a.txt]
hello world
CFIを共有していただきありがとうございます。私も貢献したいと思います。そのため、必要に応じて、startlogのファイルへのダンプの配置を再定義し、endlogのファイルへのロギングを終了するようにいくつかの変更を加えました。これにより、stdoutに出力され、stdoutがファイルにリダイレクトされます。
proc startlog {filename } {
rename puts ::tcl::orig::puts
set mode w
global def destination logfilename
set destination [open $filename $mode]
set logfilename $filename
proc puts args "
uplevel 2 \"::tcl::orig::puts \$args\"
uplevel \"::tcl::orig::puts $destination \{\$args\}\"; return
"
}
proc endlog { } {
global def destination logfilename
close $destination
rename puts {}
rename ::tcl::orig::puts puts
cleanlog $logfilename
puts "name of log file is $logfilename"
}
proc cleanlog {filename } {
set f [open $filename]
set output [open $filename\.log w]
set line1 [regsub -all {\-nonewline stdout \{} [read $f] ""]
set line2 [regsub -all {stdout \{} $line1 ""]
set line3 [regsub -all {\}} $line2 ""]
set line4 [regsub -all {\{} $line3 ""]
puts $output $line4
close $output
file rename -force $filename.log $filename
}
現在、foo > bar
と入力すると、Tclは2つのパラメーターを受け取るfoo procを実行しようとしています。これは存在しないため、エラーメッセージが表示されます。この問題に取り組むには2つの方法が考えられます。
トップレベルでリダイレクトできるので、tclsh tclsh > bar
を実行すると、すべての出力がリダイレクトされますが、これが目的であるとは思えません。
Fooを変更して、開いているファイルをパラメーターとして受け入れ、それに書き込むことができます。
proc foo {fp} {
puts $fp "some text"
}
set fp [open bar w]
foo $fp
close $fp
これを実現するために、Tclprocへの呼び出しをシェルスクリプトでラップしました。これはUNIXで機能しますが、他のOSについてはよくわかりません。
ファイル-foo.tcl:
proc foo {} {puts "Hi from foo"}
file-foo.csh(任意のシェルスクリプトが機能します。この例ではcshを使用しています):enter code here
#!/ usr/bin/tclsh source foo.tcl eval "foo $ argv"
ファイル-main.tcl:
exec foo.csh> ./myoutput.txt
もちろん、これらのコマンドは、キャッチなどの安全対策でラップすることで「ファンシー」にすることができます...わかりやすくするために、これらのコマンドは含めませんでしたが、使用することをお勧めします。また、proc fooはargsを受け取らないため、必要のない$ argvを含めましたが、通常、IRLにはargsがあります。ファイルを上書きするのではなく、単にファイルに追加する場合は、必ず>>を使用してください。