How to Ask Bash for the Current Options

In bash, how to get the current status of set -x?

You can check the value of $- to see the current options; if it contains an x, it was set. You can check like so:

old_setting=${-//[^x]/}
...
if [[ -n "$old_setting" ]]; then set -x; else set +x; fi

In case it's not familiar to you: the ${} above is a Bash Substring Replacement, which takes the variable - and replaces anything that's not an x with nothing, leaving just the x behind (or nothing, if there was no x)

How to prompt for yes or no in bash?

I like to use the following function:

function yes_or_no {
while true; do
read -p "$* [y/n]: " yn
case $yn in
[Yy]*) return 0 ;;
[Nn]*) echo "Aborted" ; return 1 ;;
esac
done
}

So in your script you can use like this:

yes_or_no "$message" && do_something

In case the user presses any key other than [yYnN] it will repeat the message.

An example of how to use getopts in bash

#!/bin/bash

usage() { echo "Usage: $0 [-s <45|90>] [-p <string>]" 1>&2; exit 1; }

while getopts ":s:p:" o; do
case "${o}" in
s)
s=${OPTARG}
((s == 45 || s == 90)) || usage
;;
p)
p=${OPTARG}
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))

if [ -z "${s}" ] || [ -z "${p}" ]; then
usage
fi

echo "s = ${s}"
echo "p = ${p}"

Example runs:

$ ./myscript.sh
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -h
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s "" -p ""
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 10 -p foo
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 45 -p foo
s = 45
p = foo

$ ./myscript.sh -s 90 -p bar
s = 90
p = bar

working with options in bash code

If you want long GNU like options and short ones, see this script to source in your script http://stchaz.free.fr/getopts_long.sh and an example :

Example :

#!/bin/bash
# ( long_option ) 0 or no_argument,
# 1 or required_argument, 2 or optional_argument).

Help() {
cat<<HELP
Usage :
$0 [ --install-modules <liste des modules> ] [-h|--help]
HELP
exit 0
}

. /PATH/TO/getopts_long.sh

OPTLIND=1
while getopts_long :h opt \
install-modules 1 \
help 0 "" "$@"
do
case "$opt" in
install-modules)
echo $OPTLARG
;;
h|help)
Help; exit 0
;;
:)
printf >&2 '%s: %s\n' "${0##*/}" "$OPTLERR"
Help
exit 1
;;
esac
done
shift "$(($OPTLIND - 1))"

How to determine the current interactive shell that I'm in (command-line)

  • There are three approaches to finding the name of the current shell's executable:

    Please note that all three approaches can be fooled if the executable of the shell is /bin/sh, but it's really a renamed bash, for example (which frequently happens).

    Thus your second question of whether ps output will do is answered with "not always".

    1. echo $0 - will print the program name... which in the case of the shell is the actual shell.

    2. ps -ef | grep $$ | grep -v grep - this will look for the current process ID in the list of running processes. Since the current process is the shell, it will be included.

      This is not 100% reliable, as you might have other processes whose ps listing includes the same number as shell's process ID, especially if that ID is a small number (for example, if the shell's PID is "5", you may find processes called "java5" or "perl5" in the same grep output!). This is the second problem with the "ps" approach, on top of not being able to rely on the shell name.

    3. echo $SHELL - The path to the current shell is stored as the SHELL variable for any shell. The caveat for this one is that if you launch a shell explicitly as a subprocess (for example, it's not your login shell), you will get your login shell's value instead. If that's a possibility, use the ps or $0 approach.


  • If, however, the executable doesn't match your actual shell (e.g. /bin/sh is actually bash or ksh), you need heuristics. Here are some environmental variables specific to various shells:

    • $version is set on tcsh

    • $BASH is set on bash

    • $shell (lowercase) is set to actual shell name in csh or tcsh

    • $ZSH_NAME is set on zsh

    • ksh has $PS3 and $PS4 set, whereas the normal Bourne shell (sh) only has $PS1 and $PS2 set. This generally seems like the hardest to distinguish - the only difference in the entire set of environment variables between sh and ksh we have installed on Solaris boxen is $ERRNO, $FCEDIT, $LINENO, $PPID, $PS3, $PS4, $RANDOM, $SECONDS, and $TMOUT.

How do I prompt for Yes/No/Cancel input in a Linux shell script?

The simplest and most widely available method to get user input at a shell prompt is the read command. The best way to illustrate its use is a simple demonstration:

while true; do
read -p "Do you wish to install this program? " yn
case $yn in
[Yy]* ) make install; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done

Another method, pointed out by Steven Huwig, is Bash's select command. Here is the same example using select:

echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
case $yn in
Yes ) make install; break;;
No ) exit;;
esac
done

With select you don't need to sanitize the input – it displays the available choices, and you type a number corresponding to your choice. It also loops automatically, so there's no need for a while true loop to retry if they give invalid input.

Also, Léa Gris demonstrated a way to make the request language agnostic in her answer. Adapting my first example to better serve multiple languages might look like this:

set -- $(locale LC_MESSAGES)
yesexpr="$1"; noexpr="$2"; yesword="$3"; noword="$4"

while true; do
read -p "Install (${yesword} / ${noword})? " yn
if [[ "$yn" =~ $yesexpr ]]; then make install; exit; fi
if [[ "$yn" =~ $noexpr ]]; then exit; fi
echo "Answer ${yesword} / ${noword}."
done

Obviously other communication strings remain untranslated here (Install, Answer) which would need to be addressed in a more fully completed translation, but even a partial translation would be helpful in many cases.

Finally, please check out the excellent answer by F. Hauri.

Bash getopts option with another

Try this: while processing commnand line options, just collect variables.
Only do things with those variables after the options are parsed.

declare f_arg o_arg s_arg

while getopts ":s:o:f:" opt; do
case $opt in
s) s_arg=$OPTARG ;;
o) o_arg=$OPTARG ;;
f) f_arg=$OPTARG ;;
esac
done
shift $((OPTIND -1))

if [[ -z $o_arg ]] || [[ -z $f_arg ]] || [[ -z $s_arg ]]; then
echo "ERROR: Options -s, -o and -f are required." >&2
exit 1
fi

# Now you can do stuff in a specific order.
o_func "$o_arg"
f_func "$f_arg"
s_func "$s_arg"

In Bash, how to add Are you sure [Y/n] to any command or alias?

These are more compact and versatile forms of Hamish's answer. They handle any mixture of upper and lower case letters:

read -r -p "Are you sure? [y/N] " response
case "$response" in
[yY][eE][sS]|[yY])
do_something
;;
*)
do_something_else
;;
esac

Or, for Bash >= version 3.2:

read -r -p "Are you sure? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
then
do_something
else
do_something_else
fi

Note: If $response is an empty string, it will give an error. To fix, simply add quotation marks: "$response". – Always use double quotes in variables containing strings (e.g.: prefer to use "$@" instead $@).

Or, Bash 4.x:

read -r -p "Are you sure? [y/N] " response
response=${response,,} # tolower
if [[ "$response" =~ ^(yes|y)$ ]]
...

Edit:

In response to your edit, here's how you'd create and use a confirm command based on the first version in my answer (it would work similarly with the other two):

confirm() {
# call with a prompt string or use a default
read -r -p "${1:-Are you sure? [y/N]} " response
case "$response" in
[yY][eE][sS]|[yY])
true
;;
*)
false
;;
esac
}

To use this function:

confirm && hg push ssh://..

or

confirm "Would you really like to do a push?" && hg push ssh://..

How to echo shell commands as they are executed

set -x or set -o xtrace expands variables and prints a little + sign before the line.

set -v or set -o verbose does not expand the variables before printing.

Use set +x and set +v to turn off the above settings.

On the first line of the script, one can put #!/bin/sh -x (or -v) to have the same effect as set -x (or -v) later in the script.

The above also works with /bin/sh.

See the bash-hackers' wiki on set attributes, and on debugging.

$ cat shl
#!/bin/bash

DIR=/tmp/so
ls $DIR

$ bash -x shl
+ DIR=/tmp/so
+ ls /tmp/so
$

What is the best way to parse command line options in bash shell?

Use shell built-in getopts or GNU command getopt.



Related Topics



Leave a reply



Submit