Fail if a script expects input or entering passwor

2019-06-25 08:21发布

问题:

I'm developing a script that should not need interaction with the user, and if a program inside my script needs the user to enter something, the script should fail and exit immediately.

I have already closed the STDIN at the top of my script, and this works for some commands, but there are some that expect the input in another file descriptor (usually the ones asking for passwords like git or sudo).

Currently my script is:

#!/bin/bash

# close STDIN
exec 0<&-

# test that $1 exists and it is a valid folder containing a git repository
# ...

cd "$1"

git fetch
# if git is not correctly configured, it asks for a password and
# my script waits for it :(

So, how can I avoid this?

note: I'm not asking how to do a password-less config in git or sudo or whatever, but to prevent a program in bash (or sh) to expect user input and fail immediately if that's the case.

回答1:

Rather than trying to close standard input or standard error, you might be better off just using a short expect script that bails out if it sees a password prompt. For example:

#!/bin/bash

cd "$1"

expect -c '
    log_user 0
    spawn git fetch
    expect -timeout 30 -re "ass(phrase|word)" { exit 1 }
    '

# Do something based on the exit status of the expect script.
[[ $? -eq 1 ]] && { echo 'Password prompt detected!' >&2; exit 1; }

The expect script will return with exit status 1 if it sees a password or key passphrase prompt. The bash script is then free to do likewise, or to take other action.

Hope that helps!



回答2:

First let me say that I don't think this is the right way to approach your problem. At all. There has to be a better application-specific solution and you really should find it.

The main problem is that without knowing what programs you want to "force close" you won't know how to cause them to "error out". And if you do know the programs, CodeGnome's solution seems fine.

Also note that not all programs read input directly from STDIN. For example, Python's getpass module opens up an additional file descriptor directly to /dev/tty, so closing or redirecting STDIN doesn't matter. This is also how sudo accepts passwords and I believe this is how ncurses works as well. Reading in this manner allows you to get passwords without them being displayed on the screen.

For prompts that don't read directly from /dev/tty (e.g. bash's read) simply redirecting STDIN from /dev/null (e.g. ./prompt.sh </dev/null) might get you somewhere. Alternatively, you could do what you're doing and close STDIN, but how the program behaves when it encounters a closed STDIN or a redirected one consisting only of EOFs is out of your control. Hopefully, the program would error out as you wish, but maybe it would continue to loop, expecting valid input. Who knows?

Moreover, neither closing nor redirecting will globally be the way that works, if either does. Some programs may exit out as you hope for closed STDINs, others may need the EOFs from /dev/null

So the way to do this is not a general, catch-all type of solution, but one tailored to the programs that you put in that spot. Better yet, don't use a try (to run the program), then catch (the case where the program asks for input) method, simply call the programs in ways that you know they won't ask for input.

Anyway, all that being said, one possibility that might work is to close STDIN and background the process. For example:

#!/bin/bash

exec 0<&-
./prompt.sh &

or alternatively, redirect STDIN to /dev/null and background the process. For example:

#!/bin/bash

./prompt.sh </dev/null &

You would then need to check the return code of the programs (with $?) to see whether they exited correctly. (Hopefully the unknown list of programs you're using follows the standard return value scheme)

The closing / redirecting of STDIN will handle the case where STDIN is used to accept input and backgrounding the process will handle the case where /dev/tty is (since a backgrounded process doesn't have a tty).

You're on your own if the program prompts you using some other method (opens up an FD directly to your pseudoterminal, pops up a graphical input box, presents you with an audible prompt, etc. etc.).



回答3:

Most (all?) *NIX have a tty command which prints out the name of the shell's tty and exits 0 if the shell has a tty attached, else it exits 1 error.

if ! tty>&/dev/null; then echo "Not a terminal; goodbye."; exit 1; fi


标签: linux bash sh