I'm trying to use the bash select statement for a command loop. The variable in the select statement is always blank. Here is a simple script that illustrates the problem:
#!/bin/bash
select term in one two exit
do
echo you selected $term
case $term in
one ) echo one; break;;
two ) echo two; break;;
exit ) echo will exit; return;;
esac
done
Here is what happens when I run this script:
$ ./test.sh
1) one
2) two
3) exit
#? one
you selected
#? two
you selected
#? exit
you selected
#? ^D
Anyone know what I might be doing wrong? I'm on Mac OS X 10.7.3. /bin/bash --version shows: GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin11)
The script works if you type in "1" or "2" rather than "one" or "two".
#jedwards gave you the immediate answer. However, if you want to protect yourself from other users having the same error, you could do something like this
select term in first second exit; do
[[ -z $term ]] && casevar=$REPLY || casevar=$term. # or, shorter, casevar=${term:-$REPLY}
case $casevar in
1|first) echo "the first option"; break ;;
2|second) echo "option no. 2"; break ;;
3|exit) echo bye; break ;;
esac
done
Note this from the bash manual:
a line is read from the standard input. If the line consists of a
number corresponding to one of the displayed words, then the value of
name is set to that word. If the line is empty, the words and prompt
are displayed again. If EOF is read, the select command completes. Any
other value read causes name to be set to null. The line read is saved
in the variable REPLY.
Related
I have below bash script using getopts. My code works as expected, i.e.,when an invalid option is specified, $myopt variable is set to '?' character and the case statement currectly processes the code matching the '?'.
Else where I see similar code using '\?' instead of just '?' in the case statement (please see the last three commented lines of the code block).
Is there any specific reason why the'?' is escaped ? I am aware any charcter can be escaped using '\' but my code works perfectly fine without escaping the '?'.
#!/bin/bash
while getopts ":abc" myopt; do
case $myopt in
[a-c])
echo "$myopt is a valid option!" >&2
;;
?)
echo "$OPTARG is an invalid option!" >&2
;;
# \?)
# echo "$OPTARG is an invalid option!" >&2
# ;;
esac
done
For the benefit of other reading this, below code will only work if '?' is escaped (see the accepted answer for a good explanation).
#!/bin/bash
while getopts ":abc" myopt; do
case $myopt in
\?)
echo "$OPTARG is an invalid option!" >&2
;;
[a-c])
echo "$myopt is a valid option!" >&2
;;
esac
done
if the script file is myscript.sh it can be tested like this -
./myscript.sh -a -z -a
For the Bash case statement the ? is a wildcard matching any single character/digit.
If you escape it with \? it specifically is trying to match the '?' char.
The case will continue to find a match, so in this case if the variable isn't [a-c] (a, b or c) then it will match if it the variable is 1 char.
I want to create simple witch case in which I can execute functions based on user prompts:
echo Would you like us to perform the option: "(Y|N)"
read inPut
case $inPut in
# echoing a command encapsulated by
# backticks (``) executes the command
"Y") echo 'Starting.....'
donwload_source_code
# depending on the scenario, execute the other option
# or leave as default
"N") echo 'Stopping execution'
exit
esac
But when I execute the script I get error:
Would you like us to perform the option: (Y|N)
n
run.sh: line 27: syntax error near unexpected token `)'
run.sh: line 27: `"N") echo 'Stopping execution''
EMP-SOF-LT099:Genesis Plamen$
Dow you know how I can fix this issue?
Multiple issues.
Add a ;; at end of each case construct
The exit command is misplaced within the switch-case construct without the ;; present . It should be at the end of case or above.
read has a own option to print the message for user prompt, can avoid a unnecessary echo.
Error-free script
#!/bin/bash
read -p "Would you like us to perform the option: \"(Y|N)\" " inPut
case $inPut in
# echoing a command encapsulated by
# backticks (``) executes the command
"Y") echo 'Starting.....'
donwload_source_code
;;
# depending on the scenario, execute the other option
# or leave as default
"N") echo 'Stopping execution'
exit
;;
esac
add ;;
#!/bin/bash
echo Would you like us to perform the option: "(Y|N)"
read inPut
case $inPut in
# echoing a command encapsulated by
# backticks (``) executes the command
"Y") echo 'Starting.....'
donwload_source_code
# depending on the scenario, execute the other option
# or leave as default
;;
"N") echo 'Stopping execution'
exit
;;
esac
I have user settings in my shell script. How can I ask user for input and remember it? In example i have in code:
ALTERNATEMEMORY="false"
I need to ask user for input (when running script in 'configure' mode):
echo "Use alternate memory?"
select yn in "true" "false"; do
case $yn in
Yes ) [permament save ALTERNATEMEMORY as "true"];;
No ) [permament save ALTERNATEMEMORY as "false"];;
esac
done
Script should ask user as above, read his input, and depending of choice set ALTERNATEMEMORY to corresponding state. Next time when running script (not configuring, just running) it should remember that setting. What should I put in these square brackets?
If I'm making mistakes when posting question, please forgive me - this is first time when I'm using stackoverflow.
The standard way to remember a setting from one run to the next, even after reboots, is to use a configuration file. For system-wide settings, these configuration files are usually in /etc and could be named, for example, /etc/myprog.conf. For user-specific settings, the file is usually in the home directory with a name that starts with ., such as $HOME/.myprog.conf. Pick one of those paths and create the configuration file:
echo "ALTERNATEMEMORY=false" >path/myprog.conf
Now, have the configuration section of your script update that file as desired:
echo "Use alternate memory?"
select yn in "true" "false"; do
case "$yn" in
true)
sed -i '/ALTERNATEMEMORY/ s/.*/ALTERNATEMEMORY=true/' path/myprog.conf
break
;;
false)
sed -i '/ALTERNATEMEMORY/ s/.*/ALTERNATEMEMORY=false/' path/myprog.conf
break
;;
esac
done
In the above, we used sed -i to update the file in-place. This works on linux. If you are on a BSD system (OSX), then you will need to add two quotes to that command like sed -i "" ...
Doing substitutions with an arbitrary string
If we want to set ALTERNATEMEMORY to have the value $sel where sel is a shell variable containing arbitrary characters, then we need to escape them before doing the substitution. This can be done as follows (assuming the shell is bash):
escaped_sel=$(sed 's/[&/\]/\\&/g' <<< "$sel")
sed -i "/ALTERNATEMEMORY/ s/.*/ALTERNATEMEMORY=$escaped_sel/" path/myprog.conf
Have a mem.conf file with the below lines
UNSET
TRUE
First line can be SET/UNSET which is to check if user has already configured and second line can be TRUE/FALSE which is to check whether to use alternate memory.
Initially the first line should be set to UNSET.
Then write the script configure.sh like below :
is_set=`head -n1 mem.conf`
if [[ $is_set == "SET" ]]
then
{
echo "Your have already configured the settings."
}
else
{
echo "Use Alternate memory?"
options=("TRUE" "FALSE" "QUIT")
select opt in "${options[#]}"
do
case $opt in
"TRUE")
echo "SET" > mem.conf
echo "TRUE" >> mem.conf
echo "Configuration Saved !!"
break;
;;
"FALSE")
echo "SET" > mem.conf
echo "FALSE" >> mem.conf
echo "Configuration Saved !!"
break;
;;
"QUIT")
echo "Seems like you have not made up your mind yet !"
echo "Please come back later."
break;
;;
*) echo "invalid option"
;;
esac
done
}
fi
echo "Configuration File : "`pwd`"/mem.conf"
The advantage here is that you could force edit mem.conf to start from scratch.
Is it possible to implement a boolean cli option using getopts in bash? Basically I want to do one thing if -x is specified and another if it is not.
Of course it is possible. #JonathanLeffler already pretty much gave the answer in the comments to the question, so all I'm going to do here is add an example of the implementation and a few niceties to consider:
#!/usr/bin/env bash
# Initialise option flag with a false value
OPT_X='false'
# Process all options supplied on the command line
while getopts ':x' 'OPTKEY'; do
case ${OPTKEY} in
'x')
# Update the value of the option x flag we defined above
OPT_X='true'
;;
'?')
echo "INVALID OPTION -- ${OPTARG}" >&2
exit 1
;;
':')
echo "MISSING ARGUMENT for option -- ${OPTARG}" >&2
exit 1
;;
*)
echo "UNIMPLEMENTED OPTION -- ${OPTKEY}" >&2
exit 1
;;
esac
done
# [optional] Remove all options processed by getopts.
shift $(( OPTIND - 1 ))
[[ "${1}" == "--" ]] && shift
# "do one thing if -x is specified and another if it is not"
if ${OPT_X}; then
echo "Option x was supplied on the command line"
else
echo "Option x was not supplied on the command line"
fi
A few notes about the above example:
true and false are used as option x indicators because both are valid UNIX commands. This makes the test for the option presence more readable, in my opinion.
getopts is configured to run in silent error reporting mode because it suppressed default error messages and allows for a more precise error handling.
the example includes fragments of code for dealing with missing option arguments and post-getopts command line arguments. These are not part of the OP's question.
They are added for the sake of completeness as this code will be required in any reasonably complex script.
For more information about getopts see Bash Hackers Wiki: Small getopts tutorial
I have a bash script which is getting options (using getopts). Let say they are '-n' , '-d' , '-u' . I only want to have one of the option being chosen, if not, it will prompt the user error.
The code is like this:
while getopts ":dun" name; do
case $name in
d )
DELETE='YES'
;;
u )
UPDATE='YES'
;;
n )
NEW='YES'
;;
esac
done
I can only have $DELETE or $UPDATE or $NEW being 'YES' in one time, meaning the user cannot specific '-n' and '-d' in the same time or '-u' and '-n' in the same time, how do I achieve that in a IF statement ?
I have been looking for this in stackoverflow, but can't find any. Thanks for the help, mate!
You can increment a counter every time getopts() senses one of the valid commandline options. Then, after the loop test the counter's value. If it is greater than one, then multiple options were specified.
This is a complete hack, and depends on the option variables being either unset or "YES" (and in the form I've written it, is bash-only):
if [[ "$DELETE$UPDATE$NEW" == YESYES* ]]; then
echo "Please only use one of -d, -u, and -n" >&2
exit 1
fi
(If you were using the brand-x shell instead of bash, it'd be something like if [ "$DELETE$UPDATE$NEW" = "YESYES" -o "$DELETE$UPDATE$NEW" = "YESYESYES" ]; then)
You're better off setting your variables to true or false, so you can simply write
if $DELETE; then
echo "You've already specified UPDATE!"
fi
and similar.
However, options are called that because they are supposed to be optional, and usually orthogonal. (There are a few instances where options of common utilities exclude each other, but the vast majority don't). What you want is really a mandatory mode of operation, so you shouldn't receive it as an option at all, but simply as the first command-line argument.
You can use some getopt-kludge to manage having only one of the option being chosen.
#!/bin/bash
while [ $# -gt 0 ]; do
case "$1" in
-u)
echo "got -u"
break
;;
-d)
echo "got -d"
break
;;
-n)
echo "got -n"
break
;;
*)
echo "some error"
break
;;
esac
done