8

Is there a way to determine if a script is waiting on stdin and cause the command to exit if detected?

Here's an example, the command I'm executing takes a long time to run, but it will also prompt for input before starting w/o a prompt. I want to know the command is actually doing something and not just waiting.

Provided the following script called ./demo

#!/bin/bash

read

Is there a way to detect that read is waiting on stdin? Something like

failifwaitingonstdin | ./demo

Which would immediately return as soon as the read command was detected.

Updated:

Folks have suggested programs like expect and yes. After digging through yes, I see how they're able to support this style of interaction. They're constantly using fputs to write 'y' to stdout. Instead of doing this infinitely, I can simply return an error as soon as fputs returns on a write to stdout.

Noah Campbell
  • 599
  • 2
  • 8
  • 15

4 Answers4

17

It would really help if you were a lot more specific about your script and/or command. But in case what you want to do is test where stdin is coming from, this example script will demonstrate that for you:

#!/bin/bash
if [[ -p /dev/stdin ]]
then
    echo "stdin is coming from a pipe"
fi
if [[ -t 0 ]]
then
    echo "stdin is coming from the terminal"
fi
if [[ ! -t 0 && ! -p /dev/stdin ]]
then
    echo "stdin is redirected"
fi
read
echo "$REPLY"

Example runs:

$ echo "hi" | ./demo
stdin is coming from a pipe
$ ./demo
[press ctrl-d]
stdin is coming from the terminal
$ ./demo < inputfile
stdin is redirected
$ ./demo <<< hello
stdin is redirected
$ ./demo <<EOF
goodbye
EOF
stdin is redirected
Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
  • On your second command: $ ./demo...you say you need to press Ctrl-d. I was not able to reproduce (i.e. I think you need a read somewhere in your script. To my question, how can I detect when read is waiting for input? I updated my question with more detail. – Noah Campbell Jul 01 '10 at 03:58
  • If your program doesn't need any real input from stdin, then **Gerald's** suggestion of piping `yes` into it will make it continue since it will have that for input. Do you have the ability to modify the script? Can't you just add the test from my demo before the `read` in your script?: `if [[ -t 0 ]]; then exit 1; done` You can instead put a timeout on the read command: `read -t 5` will wait for input for 5 seconds then fail if there's none. – Dennis Williamson Jul 01 '10 at 07:11
  • The demo script is not mine, so it's unmodifiable. – Noah Campbell Jul 02 '10 at 17:03
  • As for `[[ -t 0 ]]` test, if you put the program in background, you can not write input to it, but the test gives true anyway. – jarno May 15 '20 at 11:11
  • @jarno: From `man bash`: "If a command is followed by a & and job control is not active, the default standard input for the command is the empty file `/dev/null`. Otherwise, the invoked command inherits the file descriptors of the calling shell as modified by redirections." I believe this is so that if you `fg` (foreground via job control) the process, stdin is still attached to the process. – Dennis Williamson Jun 28 '22 at 20:11
2

Without knowing what you're trying to do I'd argue you should be writing the script so that you always know if it's going to ask for stdin or not. Either that or pipe something into the command that's likely to be wanting something from stdin, but that's probably not the best idea.

WheresAlice
  • 5,290
  • 2
  • 23
  • 20
2

If you're on Linux you could use strace to see if the process is trying to read from stdin. Other platforms have similar programs (e.g. dtrace, ktrace, or truss).

You might be able to avoid the issue altogether by feeding the output of yes to the command in question.

Gerald Combs
  • 6,331
  • 23
  • 35
2

If you are writing the scripts: bash's read builtin takes a timeout option:

read -p prompt -t 5 var # 5s timeout

If not, you can use expect, or just yes, to fake user interaction.

Tobu
  • 4,367
  • 1
  • 23
  • 31