Unix "not in" a set - validation

I am trying to do input validation for a script and basically I don't want it to preceed unless the input is one of 6 possible inputs (matching exactly). Basically I want to make it dummy proof so I don't have to train co-workers how to use it too much. Is there a way other than using an infinite loop with a huge stack of nested if statements? Is there a way to do a while x not (1 2 3 4 5 6) or something like that until the answer is one of those?

To work with any POSIX-compliant shell (assuming that this is actually a shell question -- it's currently tagged only "unix", which isn't so clear, as UNIX has no shortage of non-shell scripting languages):
while :; do
read x
case $x in
1|2|3|4|5|6) break ;;
*) echo "Invalid input" >&2 ;;
esac
done

Related

Is it good practice to use if block in while case statement in getopts

#!/bin/bash
while getopts ':s:r:' c
do
case $c in
s) if [ "$OPTARG" -ge 0 ]; then
echo "value is $OPTARG"
else
echo "enter value greater than 0";;
r) echo "something";;
esac
done
Or should i remove if block from case statment and put it after the case while loop?
What is the good practice? Any documentation I can refer?
Is it good practice to use if block in while case statement in getopts
Subjectively: sure, why not. Except you are missing an fi.
should i remove if block from case statement and put it after the case while loop?
Subjectively: it would be preferred. For me.
What is the good practice?
I believe a common way is to set only variables within options parsing, then write sanity checks after options are parsed. But it's not an universal way of doing it and it may be preferable for some applications to do some logic while parsing options. As always, the ultimate answer is: "it depends".
Any documentation I can refer?
Linux man pages and POSIX Programmers manual. man 1p getopts has an excellent example that shows how to set flags first and then write sanity checks.

#( in shell case statement

I am trying to learn how a configure script in the gimp source code works by reading and trying to understand what each statement does. I am at the beginning of the file and came across a case statement to that sets posix to on. I cant undstand what the "#(" means. Is it a comment or something else?
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
esac
Yes, anything that is after # in a line is considered as a comment. See this for the switch case syntax.

Sequential case autoincrementing its variable

This potentially is a stupid question or an impossible request. Anyway, I'm writing a little script to launch a program chosen from a list of installed software. Basically, my script presents me a numbered list, in which the programs are listed alphabetically, and I input the number corresponding to the program I want to launch. The variable in which my choice is stored is sent to a case, which launches the corresponding software, having its location stored in each case's command list.
i=1
echo -e "Which program to launch?\n"$((i++))". Program 1\n"$((i++))". Program 2\n"
read choice
case $choice in
1) path to program 1
2) path to program 2
esac
As you see, in the echo I've used a variable which gets incremented every time a new program is listed. This, to avoid having to manually write static numbers I have to personally shift every time a new program is installed and has to be inserted in the list between two existing programs. All I need to do is copy the universal $((i++)) index and the list adjusts itself.
The problem is I don't know how to implement this in the case cycle. Supposing I install a Program 3 which has to be alphabetically put between the two existing ones, the echo gets modified this way
echo -e "Which program to launch?\n"$((i++))". Program 1\n"$((i++))". Program 3\n"$((i++))". Program 2\n"
But in the case, I manually have to change the 2) before the second program into a 3).
case $choice in
1) path to program 1
2) path to program 3
3) path to program 2
esac
This may not be a problem in his example, but it is now that I have dozens of programs, and I have to change the 5 into a 6, the 6 into a 7 and so on until more than 20.
How can I automatize the case numbering, so that the cycle understands on its own that has to execute the n-th case if the variable value is n?
Bash already has select which does what you need:
#!/bin/bash
select choice in ls date 'ls /' ; do
$choice
break
done
If you want to present something different to what you run, you can use an associative array:
#!/bin/bash
declare -A choices=(
[show files]=ls
[show date]=date
[list root dir]='ls /'
)
select choice in "${!choices[#]}" exit ; do
[[ $choice == exit ]] && break
${choices[$choice]}
done
exit is handled outsice of the associative array as we want to keep it last, but associative arrays are unordered.

using logical and in unix shell script for switch case

Is it possible to use a logical AND in a unix shell script switch case. If so, how?
switch $var in
1 AND $var2 EQUALS 2)
some code
You can use:
case "$var1:$var2" in
(1:2) # $var1 = 1 && $var2 == 2
;;
...
esac
In this case, I added the colon (an arbitrary non-digit) so you can be reasonably sure that the values are as stated. However, if someone achieved var1="1:2" and var2="", then the test would pass but the values would not be as required. If, however, you're in charge of the variables, this can be perfectly sensible. It is probably cleaner to use if for simple value tests, but if you needed range tests, this could be more succinct than the equivalent if without being much less clear:
case "$var1:$var2" in
([1-3]:[4-9]) # $var1 in {1,2,3} && $var2 in {4,5,6,7,8,9}
;;
...
esac
case "$var1:$var2" in
(*amber*:*gold*) # $var1 contains 'amber' && $var2 contains 'gold'
;;
...
esac
It's likely that bash has the built-in capabilities to handle even the latter; older shells did not. This technique might be useful if you need to port to machines with bash installed by default (perhaps AIX, HP-UX, Solaris).
You need to use if statements for that.
See this.
"Each case is an expression matching a pattern. The commands in the
COMMAND-LIST for the first match are executed. The "|" symbol is used
for separating multiple patterns, and the ")" operator terminates a
pattern list. "
So you need to have patterns, not an expression to be evaluated, for each case.

Shell: Arithmetic expressions in case possible without setting a variable? Example inside, please help :)

I am currently learning shell thanks to the Bash-Beginners-Guide provided by tldp.
Currently I am doing some exercises to improve my Shell scripting skills, in this case I am learning how to use if and case statements.
I have written some code and want to know if it is possible to use arithmetic expressions directly in case expressions without setting a variable beforehand like I did in my code.
Note that I don't want to have a solution for self-assigned 'homework', but rather get enlightenment in regard to syntax basics in Shell.
The code, free to use:
echo "Starting exercise 2..."
day=`date +%e`
year=`date +%Y`
echo "Number of days in this month: $day days"
echo "Calculating leap years:"
calc1=$[$year % 400]
case $calc1 in
0) echo "$leap";;
*)
calc2=$[$year % 100]
case $calc2 in
0) echo "$noleap" ;;
*)
calc3=$[$year % 4]
case $calc3 in
0) echo "$leap" ;;
*) echo "$noleap" ;;
esac
esac
leap and noleap are just some Strings, they don't matter for my problem.
I want to get rid of those calc* vars and do something like:
case $[$year % 400] in
0) echo "$noleap";;
...
Is this possible?
Mind you that I am an ABSOLUTE beginner, I've started learning Shell 2 days ago.
Edit: I think I answered my own question. Looks like
case $[arithmetic expression] in
works. However there is need in validation, yet.
Edit2: Fixed irrelevant error noleap -> leap in year%400 case.
Try arithmetic expansion: case $(($year%400)) in .... This syntax can be used almost anywhere that variable expansion can, including in double quotes.

Resources