58

I am writing a POSIX shell script that may or may not receive input from stdin as in foo.sh < test.txt, non-interactively.

How do I check whether there is anything on stdin, to avoid halting on while read -r line...?

3
  • 1
    stdout: stackoverflow.com/questions/911168/… Commented May 11, 2017 at 9:01
  • Checking if file (like stdin) contains something without consuming it is usually referred to as "peeking" (in other words figuring out if there is something there for a read to get). This answer unix.stackexchange.com/questions/33049/… should convince you that you don't want to do that. Commented Feb 22, 2024 at 18:19
  • Also something not being there doesn't mean you're done, for example with find . -name x | your_script. It may take a long time before find puts a result in stdin. So the only conditions you should be interested in are 1. Is it a terminal or not, and 2. In the accepted answer, both conditions are checked. 1 is checked with [ -t 0 ] and 2. is checked as the while condition: read returns 1 if stdin has been closed. If you were running the script interactively and wanted to end the loop, you can close stdin by pressing 'CTRL-D' on an empty line sending the EOF character. Commented Feb 22, 2024 at 18:27

4 Answers 4

95

If I get the question right, you may try the following:

#!/bin/sh if [ -t 0 ]; then echo running interactively else while read -r line ; do echo $line done fi 
Sign up to request clarification or add additional context in comments.

4 Comments

Just a heads up, this is triggered for stdin and when your capturing stdout. (both run interactivelly) ./yourscript < ./the_input_your_giving_your_script.txt && ./yourscript > ./what_your_script_outputs.txt
@DeShawnWilliams No, @dmedvinsky's answer is correct: test -t n is true if the file descriptor n is opened on a terminal, thus test -t 0 only checks for stdin, and test -t 1 only checks for stdout.
Any idea why echo hello | interactive and interactive <(echo world) produce different outputs (in zsh)? (interactive just returns 'yes' or 'no'.) I see that interactive <<<"goodnight" and interactive <moon.txt both work as expected. (My intuition is that <(command) is syntax sugar for something that uses a TTY, but I don't know how/why.)
Oh, er, that's not working the way I remembered. <(command) is still an argument, it's just replaced by the file descriptor of the stdout of command.
12

To answer the question literally (but not what you actually want): read -t 0

Timeout, zero seconds.

  1. this is a race-condition, depending on when the left-hand-side is ready to provide data. You can specify -t 5 but on a thrashing system even that is problematic.
  2. read -t is not standardised in SUSv3. It is in BSD sh, bash, zsh. It's not in ksh or dash.

So you can't just use #!/bin/sh and expect to have this.

The basic problem is that even if there's nothing on stdin now, that doesn't mean there won't be soon. Invoking a program normally leaves stdin connected to the terminal/whatever, so there's no way to tell what's needed.

So, to answer your question literally, you can do it, but in practice your options are:

  1. test if stdin is a tty: [ -t 0 ]
  2. use argv to control behaviour

2 Comments

I just experimented with this using zsh. I noticed that read -t or equivalently read -t0 have nondeterministic behavior. So as you mentioned already, avoid using it.
But, that's to be distinguished from the test -t used in the answer.
8

You can easily implement a similar behaviour as the "cat" command, that is read from a list of provided files or if they're not provided, then read from the stdin.

Although you may not use this idea, I think this Linux Journal article will be interesting for you http://www.linuxjournal.com/content/determine-if-shell-input-coming-terminal-or-pipe

:-)

Comments

1

If you never want to run the script interactively, make it take the input file as a parameter, rather than using stdin. Some programs use a flag or a special filename to indicate that they should take input from standard input rather than from a file; that case lets you handle command line jockeying if necessary.

If you want your script to take the standard input, why don't you want to let it be interactive (or at least behave like other POSIX tools)?

1 Comment

A common way to tell programs to read from stdin instead of a file is to provide - instead of a filename.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.