bash: don't prompt the select case statement from a script - possible? - bash

I have a script which I usually run by hand. It prints some variables and asks me twice if those are correct:
echo "Is this correct?"
select yn in "Yes" "No"; do
case $yn in
Yes ) break;;
No ) exit;;
esac
done
Works perfectly.
Now however, I have been asked to automate the task completely - in other words, it shouldn't prompt me anymore and just run through.
However, I would like to retain the prompting for the normal case when I run it manually.
Is it possible to set a flag to bash or something which would just skip over the select statement? I know it's kinda silly but was wanting to see if I have an easy way out without having to completely remove that section or having to write a second script without that.

You can use test -t fd to tell if the FD is connected to a terminal or not. When the test succeeds, prompt for input, otherwise skip it.
if [ -t 0 ]; then
echo "Is this correct?"
select yn in "Yes" "No"; do
case $yn in
Yes ) break;;
No ) exit;;
esac
done
fi

Related

Accept newline in bash script

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;;.

How to remember user input in a shell script?

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.

Bash Scripting, Respond to Keypress

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

Can't match any option in bash select statement

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.

Multiple compound comparison in bash

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

Resources