Testing if a command's input / output is interactive

Project:JNode Shell
Component:Code
Category:feature request
Priority:minor
Assigned:Stephen Crawley
Status:closed
Description

On IRC Josh Stratton asked some questions about the shell. He wanted to know if a command can check if it is reading or writing from/to a pipe, file or stdin/stdout. He brought up one interessting example, the linux command ls.

If you type "ls" you get multiple files per line but if you type "ls|grep ." you get one file per line. The code of ls indeed checks if it's writing to a tty or not.
I think we can agree that this is no good practice and that you could also type "ls -1" if you want one file per line. I also think it would be bad to do stuff like that in JNode. But in case we want to directly execute bash-scripts this is a problem and there might be further linux-commands that "behave strange" in that regard.

Stephen, what do you think about that?

Getting terminal width and height

peda recommended I post that I wanted to know the width and height of a given terminal in characters. He recommended using:

  • TextConsole tc = (TextConsole) shell.getConsole();
  • tc.getWidth/getHeight();

He also recommended posting this so Crawley would know I plan to use this functionality.

Possible issues with what you are suggesting

Thank you for letting me know. We do indeed need a standard pattern for getting the console dimensions for an application to use, and your approach looks reasonable.

AFAIK, it should work for now, though there may be issues in the future. For a start, it means that we would need to figure out how to implement TextConsole for terminals / terminal emulators.

There is also the issue of how to get the TextConsole when an application doesn't know its shell. ShellUtils.getCurrentShell() is not the answer! A robust mechanism for getting the underlying TextConsole from a "tty" stream is needed.

#1

I think this is a good question.

First, let us be clear that UNIX applications are not testing whether the stream has been redirected. Rather they are testing whether it "is a tty". IIRC, there is a C library function for this, and there will be an underlying syscall or syscalls that allow it to find this out.

We currently don't have a standard way to make an "is a tty" test, and we don't have syscalls to make this determination. Instead, a JNode application has just got Java *Stream (or Reader/Writer) objects to look at. And there are wrappers and proxies in the way. (At a minimum, a Command.exeute method will see any OutputStream wrapped as a PrintStream ... and the standard PrintStream API does not allow you to find out the wrapped OutputStream.)

I can think of three approaches:

  1. Create a Helper class that makes use of reflection, etc to expose the internals of the stream classes and figure out if a given stream is a "tty" stream.
  2. Rejig the Command.execute method so that in/out/err use interface types. (Reader/Writer is one option, but that means the command only gets stream-of-char. Another would be to invent our own stream-of-byte interfaces modeled on *Stream, but done better.) Then we add an interface that exposes a method to test for "tty-ness".
  3. Extend the Command API with methods for asking if the in/out/err streams are "tty", and implement them by having the shell, etc remember how the streams were set up.

Note that whichever way we implement this, we need to be able to cope with "tty" streams that come from physical TTY ports or remote network connections and possibly even from input scripting systems.

#1

Project:JNode Core» JNode Shell

Moving to appropriate Project.
I tested "dir|grep jnode" which still did not work. A NPE in GrepCommand and I think instead of "jnode" a "null" is passed to grep.

#2

I just tried it, and "dir | grep jnode" works for me. Is your sandbox up to date?

#3

Title:Shell Redirection» Testing if a command's input / output is interactive

Changing issue title to better reflect its content.

#4

Assigned to:Anonymous» Stephen Crawley

I'm getting started on this (these) issues together with other problems to do with command I/O.

#5

I've now committed the 1st and 2nd stages. The first stage was an overhaul of the Command API so that JNode commands can access the 'standard' in/out/err streams as Streams or Reader/Writers. The second stage (committed a few minutes ago) was to change the console i/o channels to be character oriented rather than byte oriented ... and propagate the required API changes throughout.

I've tested the new code pretty thoroughly, but if you notice any new problems with the latest checkin(s), please add them as comments to this one so that I can find them.

#6

Fixed a problem in the PcTextConsole layer that was exposed by my earlier checkins. The previous implementation was failing to 'sync' the screen after a write operation had caused the 'y' offset of text window to move. This was noticable in the boot sequence. Instead of appearing immediately, messages from the log4j 'info' console appender came out in multi-line chunks. (The puzzle for me is why this wasn't happening before ... and why my earlier changes revealed it.)

With that out of the way I can finish off this issue.

#7

I've committed a working implementation of the isTTY() method on CommandIO objects.

I've also added a new method 'getBaseStream' that will (for example) return the KeyboardReader or ConsoleWriter associated with a 'tty' CommandIO object. This allows you to get to the TextConsole and hence to find the console screen width and height; e.g.

TextConsole console = ((ConsoleWriter) ios[0].getBaseStream()).getTextConsole();

This a better alternative to shell.getConsole() because it should be future-proof against cases where the console you are trying to write to is not the shell console; e.g. when you do the equivalent of "cat < someFile > /dev/console".

#8

Status:active» fixed

I'm marking this issue as 'fixed'.

#9

Status:fixed» closed

Auto-close failed ... doing it by hand.