I built a tiny menu to use in a bash terminal with multiple options to select via number keys.
#!/bin/bash
PS3='Teleport to ... '
options=("→ option 1" "→ option 2" "Quit")
select opt in "${options[#]}"
do
case $opt in
"→ option 1")
echo "option 1"
break
;;
"→ option 2")
echo "option 2"
break
;;
"Quit")
break
;;
*) echo invalid option
break
;;
esac
done
At the moment I still need to confirm the selection by pressing enter. Is it possible to make the script respond to the input of the first pressed key directly?
read -n 1 reads one character. You cannot use select with it, though, so you have to write the while loop yourself.
Yep, with bash (and not sh!) you can use something like:
_KEY=
read -d '' -sn1 _KEY
Related
I'm trying to use getopts inside a switch case loop.
if i use only getopts or only the switch case it's work, however when i combine those two the getopts dos not trigger.
i have search a lot but i cat fins any mention for how to combine them, and problem i missing something stupid so for give me ...
here is the code essence.
#!/bin/bash
case $1 in
ver)
echo "vesion"
exit 0
;;
op)
while getopts ":a" opt; do
case $opt in
a)
echo "-a was triggered!" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
done
;;
esac
when i do that
# bash -x test.sh op -a
i get
+ case $1 in
+ getopts :a opt
(and without debug i get nothing)
what is that that i missing to combine these two
Thanks :)
You should add a shift instruction at the beginning of your op) choice, before the call to getopts, to eat the op argument itself. Else, the first argument that getopts will analyze is op and it will silently stop (end of options).
I have the following script that continuously accepts input from the user until he/she enters some form of y* or Y*.
while true; do
read -p "Are you ready? " yn
case $yn in
[Yy]* ) break;;
[Nn]* ) ;;
* ) echo "Please answer yes or no.";;
esac
done
However, I would like to break out of the while loop when the user simply presses enter. I tried using \n, \r, and \r\n but these don't seem to be the right patterns.
I am using Cygwin if that makes a difference (though I would also like to know the answer for a Linux distribution like Ubuntu).
Because read strips the newline, you'll have to match an empty string, "":
#!/bin/bash
while true; do
read -p "Are you ready? " yn
case "$yn" in
[Yy]*|"") break;;
[Nn]*) ;;
*) echo "Please answer yes or no.";;
esac
done
Depending on your application logic, you can (obviously) make that a separate case branch, like: "") break;;.
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.
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.
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