Ticket UUID: | 0e7c09eb49912d9c58b1d64dea507f64b480e8b2 | |||
Title: | tclsh in a command pipeline: ::tcl_interactive is 0, but interpreter is in interactive mode (halfway). | |||
Type: | Bug | Version: | 8.6.4 | |
Submitter: | pooryorick | Created on: | 2015-04-14 14:55:42 | |
Subsystem: | 69. Other | Assigned To: | nobody | |
Priority: | 5 Medium | Severity: | Important | |
Status: | Open | Last Modified: | 2015-04-14 17:48:43 | |
Resolution: | None | Closed By: | nobody | |
Closed on: | ||||
Description: |
tclsh is schizophrenic about interactive mode. In the case where a file is given as an argument to tclsh, the code path is clear, and tclsh runs in true non-interactive mode, passing the script off to Tcl_FSEvalFileEx. If stdin is a terminal and a script is not provided, ::tcl_interactive is true, and tclsh runs in interactive mode. Prompts and command results are printed to stdout, error messages printed on stderr, errors do not end the REPL, and when the end of the file is reached, the exit status is 0 even if the last command resulted in an error. Here's the multiple-personality case: If stdin is not a terminal and a script is not provided, ::tcl_interactive is false but tclsh still operates mostly in interactive mode, except that it doesn't print prompts and command results to stdout. What it does do is not halt on errors, and exit with a status of 0 even if the last command in the script returns an error. This means that the exit status of a pipeline like the following is 0:
It also means that how a script is passed to the interpreter can determine its
exit status. Consider a script named
If invoked like this, the exit status is 1:
But if invoked in these ways, the short error result is displayed, the exit status is 0:
Tclsh in a pipeline is an important use case on Unix-like systems. It could behave in a more expected manner by halting on errors and passing a non-zero exit status when script input is via stdin and that channel is not a terminal. The change to accomplish that is trivial:
Apart from the modified lines in this page, it can be seen that this code path was already making decisions based on is.tty, even though the whole path is ostensibly already the interactive path. What it really is is the stream path. One additional decision based on is.tty seems reasonable enough. This places the burden of deciding whether stdin is interactive more squarely on the platform-specific parts of Tcl that live off in their own directories. The potential downside to this change is any disruption it may cause to existing processes that feed scripts to tclsh via an stdin that is not a terminal. This is probably uncommon because this strategy is playing with fire. Such processes would no longer be able to rely on the interpreter to ignore errors. The solution would be to explicitly catch errors in the scripts or to use something like Expect, which could provide a pseudo terminal. One hint that this fix is good comes from the test suite. The changes required to support this patch are for the most part to use ::tcl_interactive explicitly in order to get the desired outcome, which was the reason for introducing ::tcl_interactive in the first place. |
Attachments:
- tclsh_noninteractive_stream.diff [download] added by pooryorick on 2015-04-14 15:01:01. [details]