1. 19
  1.  

  2. 13

    I originally also suppressed this output on non-terminal devices, but then prog | less will still hang without a message, which is not great. I would encourage suppressing this output with a -q or -quiet flag.

    STDIN might be a terminal while STDOUT or STDERR are not – you have different FDs, it is not a single STDIO device.

    For example in C, you can test particular FDs this way:

    #include <stdio.h>
    #include <unistd.h>
    
    void check(int fd) {
    	if (isatty(fd))  printf("FD %d is a terminal\n", fd);
    	else             printf("FD %d is a file or a pipe\n", fd);
    }
    
    void main() {
    	check(fileno(stdin));
    	check(fileno(stdout));
    	check(fileno(stderr));
    }
    

    Output:

    $ make is-a-tty
    cc     is-a-tty.c   -o is-a-tty
    
    $ ./is-a-tty 
    FD 0 is a terminal
    FD 1 is a terminal
    FD 2 is a terminal
    
    $ ./is-a-tty | cat
    FD 0 is a terminal
    FD 1 is a file or a pipe
    FD 2 is a terminal
    
    $ echo | ./is-a-tty
    FD 0 is a file or a pipe
    FD 1 is a terminal
    FD 2 is a terminal
    
    $ echo | ./is-a-tty | cat
    FD 0 is a file or a pipe
    FD 1 is a file or a pipe
    FD 2 is a terminal
    
    $ ./is-a-tty 2>/dev/null 
    FD 0 is a terminal
    FD 1 is a terminal
    FD 2 is a file or a pipe
    

    I would not recommend messing the STDOUT/STDERR with superfluous messages if there is no error.

    Indeed, there is Rule of Silence:

    When a program has nothing surprising to say, it should say nothing.

    Waiting for an input from a file or pipe is expected non-surprising behavior. Only when waiting for an input from the terminal, it could make sense to print some prompt or guide the user what he should do.

    1. 2

      I forgot you can can run isatty() on stdin, too. Previously it did check this for stdout, but I removed his earlier (isTerm is the result of isatty(stdout)).

      I’ll update the program and article; thanks.

      1. 3

        isatty on stdin is good to test if your users made a mistake, and then isatty() again on stderr to make sure your users are reading your message!

      2. 1

        Strictly speaking, this is POSIX not C. isatty has been broken in the past on Windows with some distributions of GCC, I am unsure what the status is these days.

      3. 4

        A more Unix-y approach would be only enabling this kind of warning if you run the command with a verbose option. Reading from stdin is one of the most common operations in an Unix system, there is no need to make it that explicit.

        1. 5

          That entirely defeats the point of providing a good UX when people use uni emoji and don’t know that will read from stdin instead of, say, printing everything, being interactive, or any number of other reasonable behaviours.

          1. 3

            I understand how that could help end users who are not Unix experts, and probably the guys who wrote The Unix-Haters Handbook would certainly agree with your point. By no means I am trying to imply that this is a terrible solution, because I don’t know your audience. Unix users would probably be OK with using something as echo x | xargs uni identify.

            1. 5

              I think it could help users who are unix experts, too. No one is an expert on my little program (except me), and many programs don’t read from stdin (or read on stdin only when explicitly told to with -), or would print the help on some of the commands, etc.

              There is no consistent interface, and it’s actually not so easy to tell if a program is reading from stdin outside of experimentation or carefully studying the manual. So what you’re left with is a program that appears to be working, but may also be reading from stdin, or it may just be slow; there is no easy way of knowing.

              1. 3

                many programs don’t read from stdin (or read on stdin only when explicitly told to with -)

                No. Actually, reading from STDIN and writing to STDOUT (i.e. program can be used as a filter) is the recommended and standard Unix way. See Rule of Composition:

                Design programs to be connected with other programs.

                Unix tradition strongly encourages writing programs that read and write simple, textual, stream-oriented, device-independent formats. Under classic Unix, as many programs as possible are written as simple filters, which take a simple text stream on input and process it into another simple text stream on output.

                I am not so strict about the textuality (binary streams and formats are sometimes better) but I fully support this filter approach (read from STDIN and write to STDOUT) as default behavior.

                However, this is a general advice, that should apply most of the (CLI) software – and your program might be exceptional and might require different approach…

                1. 4

                  You can throw around “rules” to no end to “win” your point. So esr wrote this book 20 years ago, so what? In spite of what esr may think of himself he is not, in fact, the ultimate arbitrator of truth regarding these things, or anything else.

                  No matter what’s in esr’s book, if you go on GitHub and download CLI tools like this then a lot of them won’t read from stdin, or they will only read on stdin when very explicitly told to do so. That’s the reality of the world today. You can either choose to ignore that reality that and pretend that esr’s 20-year old book is reality – which it’s not and probably never has been – or deal with reality it as it is. I choose the latter.

                  1. 3

                    ESR wasn’t defining the rules, he was just noting them down. Bad practice on random Github projects is not really a suitable excuse to continue with bad practice.

            2. 2

              The manpage should specify it reads it’s input from stdin.

          2. 2

            Interesting, I’m not convinced though. This is only useful in the interactive case, and it makes that messy:

            $ sh cat.sh
            cat.sh: reading from stdin...FIRST INPUT LINE
            SECOND INPUT LINE
            FIRST INPUT LINE
            SECOND INPUT LINE
            

            You get to start typing at the end of the message as if it were a prompt, and the line doesn’t get cleared because we’re elsewhere when that part gets called. (Thankfully, it doesn’t seem to get to clear any other things.)

            Regarding buffering all of stdin, a simple improvement seems to be the following:

            printf >&2 '%s: reading from stdin...' "$(basename "$0")"
            head=$(head -1 <&0)
            printf >&2 '\r\033[0K'
            
            echo "$head"
            cat
            

            This works when I try it interactively on the command line, but I’m a bit unsure whether reading twice from stdin is actually something that will work in general. Might head read more than the first line depending on buffering?

            1. 1

              Do people use it interactively like this? I’ve literally never done so and never seen anyone else do so either. I suppose it’s impossible to cover 100% of the use cases here.

              As for the buffering, yeah; there are a bunch of (relatively obvious) ways to improve on that. I just omitted that for simplicity. I think the most common way would probably to read stdin per-line and print the \r\033[0K on the first line/input.

              1. 2

                I rarely use it like that, no.

                But then, the message is basically just an error message, right? I.e., whenever you read it, what you’re actually going to do is hit ctrl-C and fix your command line.

                In that case, perhaps an alternative would be to put it behind a delay? If no input is received within the first 0.2 seconds or so, write “waiting for standard input…” to stderr. That message wouldn’t even seem out of place in the other scenarios you mention.

                1. 1

                  But then, the message is basically just an error message, right? I.e., whenever you read it, what you’re actually going to do is hit ctrl-C and fix your command line.

                  Yeah, that sounds right.

                  The input delay idea sounds interesting; maybe I’ll experiment with that later; thanks.

            2. 1

              I have thought about this problem for a few years. I considered all these ’gotcha!’s described in the article and the comments here.

              I decided that I want my OS to detect when a process is waiting for input (blocking) and play a sound–like, a tone. Globally.

              Will this cause my PC to emit the tone immediately and continuously as soon as I turn it on? Perhaps yes, if I’m using Ubuntu or something. Maybe not on the machines I’ve been building lately, that don’t really do anything unless I’ve just asked them to.

              Anyway, I imagine I’d want a set of rules. Maybe I’d even exclude from monitoring those processes that aren’t “near” an interactive terminal in the process tree. Or leave it global, but whitelist some processes that I learn block on stdin all the time as part of their normal operation.

              I think I’d learn a lot about my systems! And certainly reduce the specific “🤦” situation the article describes.

              1. 2

                I decided that I want my OS to detect when a process is waiting for input (blocking) and play a sound–like, a tone. Globally

                Well that’s not how I’d do it but I was thinking the terminal emulator could visually indicate a waiting state fairly easily if the OS told it. Like a blinking cursor vs a steady one, or a color or whatever.

                I just don’t think the OS informs it right now…

                1. 1

                  top and htop know, right? They both get their information solely from /proc, I heard.

                  I think lsof would be able to tell if a process was blocking on stdin, too, right? Not sure where it gets its information.