Group: comp.lang.tcl


Subject: Expect: how to tell if stdin is redirected from /dev/null?
From: John Caruso
Date: 11/23/2007 11:55:39 PM
In expect (5.43.0), is there any way to tell if stdin has been redirected from /dev/null? I.e., what would the code be to differentiate running myscript without input redirected and running "myscript < /dev/null"? - John

Subject: Expect: how to tell if stdin is redirected from /dev/null?
From: John Caruso
Date: 11/24/2007 6:11:35 PM
On 2007-11-24, Uwe Klein <uwe_klein_habertwedt@t-online.de> wrote: > John Caruso wrote: >> In expect (5.43.0), is there any way to tell if stdin has been redirected >> from /dev/null? I.e., what would the code be to differentiate running >> myscript without input redirected and running "myscript < /dev/null"? >> >> - John > > #!/usr/bin/expect > > if {[ catch {fconfigure stdin -mode} cerr]} { > puts stderr "stdin is not a tty : $cerr" > read stdin 1 > if {[eof stdin]} { > puts stderr "stdin could be /dev/null" > } >} else { > puts stderr "stdin is a tty : $cerr" >} Perfect, many thanks. In fact I was really looking for an isatty function (just as you've provided), but I didn't want to ask the question in those words since I didn't want to constrain the solution. It's a bit surprising that expect doesn't provide one. For those wondering why: I have an expect script (below) that exhibits bad behavior when it's run with input redirected from /dev/null, apparently caused by the interact statement handling EOF (if a preceding expect statement sees the EOF, all's well). BTW, this is a minimal test script culled from a much longer script, which is the reason for some of the odd bits of code. Here are sample run lines: ./script --user test --hosts "host1 host2 host3" "date; sleep 2; id" ./script --rsh --hosts "filer1 filer2 filer3" "date; version" The key is to specify multiple hosts, which causes the script to spawn multiple remote sessions (you'll need to have trust relationships with the remote hosts for this version of the script, and you can specify a single host multiple times). When the script is run with input coming from the tty it behaves fine, but when it's run with input redirected from /dev/null it will do one of the following after the 2nd or 3rd host: 1) "error writing "stdout": invalid argument while executing "puts -nonewline $savedoutput" 2) "interact: spawn id exp0 not open" -- If the catch statement is removed from the interact statement. (In the first case it may also fail without producing the error message-- but it will also stop producing output, as though the puts statements are having their output thrown away.) The weirder failure mode is 1. In this case (with the catch protecting the interact statement), it appears that merely calling interact with stdin redirect from /dev/null causes *stdout* to be closed, which makes the puts (or send_user, if you switch it out for that) fail. Is that the expected behavior of interact? It certainly wasn't for me. As I said, the difference in whether it produces the above error or not appears to be whether or not the expect statement consumes the EOF from the child. If it does, all's well, but if the EOF is still out there when the interact statement is executed the script will die on the next host. Anyway, your code will allow me to work around this issue (by never calling interact if there's not a tty attached), but I'd certainly be interested to hear if anyone has an idea of why calling interact would cause stdout to be closed. - John #!/usr/bin/expect -- set hosts "" set remotecommand "ssh" set user "root" while {[llength $argv] > 0} { switch -regexp -- [lindex $argv 0] { ^--(h|ho|hos|host|hosts)$ { append hosts " " [lindex $argv 1] set argv [lrange $argv 1 end] } ^--(r|rs|rsh)$ { set remotecommand "rsh" } ^--(u|us|use|user)$ { set user [lindex $argv 1] set argv [lrange $argv 1 end] } default { break } } set argv [lrange $argv 1 end] } send_user "DEBUG: hosts='$hosts', argv='$argv'\n" foreach host $hosts { log_user 0 eval spawn $remotecommand -l $user $host $argv expect { -re ".+" { } } set savedoutput "$expect_out(buffer)" puts -nonewline "***** $remotecommand command output for $host *****\n" puts -nonewline $savedoutput interact catch { } wait -nowait }

Subject: Expect: how to tell if stdin is redirected from /dev/null?
From: John Caruso
Date: 11/24/2007 6:21:43 PM
On 2007-11-24, John Caruso <johnSPAMcarAWAYuso@myprivacy.ca> wrote: > puts -nonewline "***** $remotecommand command output for $host *****\n" > puts -nonewline $savedoutput > interact > catch { > } This last bit should have been: > catch { > interact > } The first way (at the top) will cause failure mode 2, and this way will cause the more interesting failure mode 1 (I was switching back and forth during testing, and didn't notice that I'd left it this way). I should have mentioned that I've seen this on expect 5.43.0 and 5.42.1, and on Linux (RHEL4) and Solaris (8). The fact that stdout is somehow closed feels like a bug; I can understand that interact wouldn't work desirably when input is redirected from a file, but closing stdout was pretty surprising behavior. - John