more scripting
(Written by Paul Cobbaut, https://github.com/paulcobbaut/, with contributions by: Alex M. Schapelle, https://github.com/zero-pytagoras/)
eval
eval reads arguments as input to the shell, i.e. dynamically evaluates and executes code that is constructed or stored in a variable.
This allows e.g. using the value of a variable as a variable.
student@linux:~/test42$ answer=42
student@linux:~/test42$ word=answer
student@linux:~/test42$ eval x=\$$word ; echo $x
42
Both in bash and Korn the arguments can be quoted.
kahlan@solexp11$ answer=42
kahlan@solexp11$ word=answer
kahlan@solexp11$ eval "y=\$$word" ; echo $y
42
For this use case, Bash has specific syntax, referred to as indirect substitution. This may be preferrable to using eval, but it is less portable as it won't work in sh or zsh.
Sometimes the eval is needed to have correct parsing of arguments. Consider this example where the date command receives one parameter 1 week ago.
When we set this command in a variable, then executing that variable fails unless we use eval.
student@linux~$ lastweek='date --date="1 week ago"'
student@linux~$ $lastweek
date: extra operand `ago"'
Try `date --help' for more information.
student@linux~$ eval $lastweek
Thu Mar 8 21:36:39 CET 2012
Be aware that using eval is a security risk. Evaluating code based on user input opens the door to command injection and has been the root cause of real-world vulnerabilities.
(( ))
The (( )) allows for evaluation of numerical expressions.
student@linux:~/test42$ (( 42 > 33 )) && echo true || echo false
true
student@linux:~/test42$ (( 42 > 1201 )) && echo true || echo false
false
student@linux:~/test42$ var42=42
student@linux:~/test42$ (( 42 == var42 )) && echo true || echo false
true
student@linux:~/test42$ (( 42 == $var42 )) && echo true || echo false
true
student@linux:~/test42$ var42=33
student@linux:~/test42$ (( 42 == var42 )) && echo true || echo false
false
let
The let built-in shell function instructs the shell to perform an evaluation of arithmetic expressions. It will return 0 unless the last arithmetic expression evaluates to 0.
[student@linux ~]$ let x="3 + 4" ; echo $x
7
[student@linux ~]$ let x="10 + 100/10" ; echo $x
20
[student@linux ~]$ let x="10-2+100/10" ; echo $x
18
[student@linux ~]$ let x="10*2+100/10" ; echo $x
30
The shell can also convert between different bases.
[student@linux ~]$ let x="0xFF" ; echo $x
255
[student@linux ~]$ let x="0xC0" ; echo $x
192
[student@linux ~]$ let x="0xA8" ; echo $x
168
[student@linux ~]$ let x="8#70" ; echo $x
56
[student@linux ~]$ let x="8#77" ; echo $x
63
[student@linux ~]$ let x="16#c0" ; echo $x
192
There is a difference between assigning a variable directly, or using let to evaluate the arithmetic expressions (even if it is just assigning a value).
kahlan@solexp11$ dec=15 ; oct=017 ; hex=0x0f
kahlan@solexp11$ echo $dec $oct $hex
15 017 0x0f
kahlan@solexp11$ let dec=15 ; let oct=017 ; let hex=0x0f
kahlan@solexp11$ echo $dec $oct $hex
15 15 15
case
You can sometimes simplify nested if statements with a case construct.
[student@linux ~]$ ./help
What animal did you see ? lion
You better start running fast!
[student@linux ~]$ ./help
What animal did you see ? dog
Don't worry, give it a cookie.
[student@linux ~]$ cat help
#!/bin/bash
#
# Wild Animals Helpdesk Advice
#
echo -n "What animal did you see ? "
read animal
case $animal in
"lion" | "tiger")
echo "You better start running fast!"
;;
"cat")
echo "Let that mouse go..."
;;
"dog")
echo "Don't worry, give it a cookie."
;;
"chicken" | "goose" | "duck" )
echo "Eggs for breakfast!"
;;
"liger")
echo "Approach and say 'Ah you big fluffy kitty...'."
;;
"babelfish")
echo "Did it fall out your ear ?"
;;
*)
echo "You discovered an unknown animal, name it!"
;;
esac
[student@linux ~]$
shell functions
Shell functions can be used to group commands in a logical way.
kahlan@solexp11$ cat funcs.ksh
#!/bin/ksh
function greetings {
echo Hello World!
echo and hello to $USER to!
}
echo We will now call a function
greetings
echo The end
This is sample output from this script with a function.
kahlan@solexp11$ ./funcs.ksh
We will now call a function
Hello World!
and hello to kahlan to!
The end
A shell function can also receive parameters.
kahlan@solexp11$ cat addfunc.ksh
#!/bin/ksh
function plus {
let result="$1 + $2"
echo $1 + $2 = $result
}
plus 3 10
plus 20 13
plus 20 22
This script produces the following output.
practice : more scripting
-
Write a script that asks for two numbers, and outputs the sum and product (as shown here).
Enter a number: 5 Enter another number: 2
Sum: 5 + 2 = 7 Product: 5 x 2 = 10
-
Improve the previous script to test that the numbers are between 1 and 100, exit with an error if necessary.
-
Improve the previous script to congratulate the user if the sum equals the product.
-
Write a script with a case insensitive case statement, using the shopt nocasematch option. The nocasematch option is reset to the value it had before the scripts started.
-
If time permits (or if you are waiting for other students to finish this practice), take a look at Linux system scripts in /etc/init.d and /etc/rc.d and try to understand them. Where does execution of a script start in /etc/init.d/samba ? There are also some hidden scripts in ~, we will discuss them later.
solution : more scripting
-
Write a script that asks for two numbers, and outputs the sum and product (as shown here).
-
Improve the previous script to test that the numbers are between 1 and 100, exit with an error if necessary.
-
Improve the previous script to congratulate the user if the sum equals the product.
-
Write a script with a case insensitive case statement, using the shopt nocasematch option. The nocasematch option is reset to the value it had before the scripts started.
#!/bin/bash # # Wild Animals Case Insensitive Helpdesk Advice # if shopt -q nocasematch; then nocase=yes; else nocase=no; shopt -s nocasematch; fi echo -n "What animal did you see? " read animal case $animal in "lion" | "tiger") echo "You better start running fast!" ;; "cat") echo "Let that mouse go..." ;; "dog") echo "Don't worry, give it a cookie." ;; "chicken" | "goose" | "duck" ) echo "Eggs for breakfast!" ;; "liger") echo "Approach and say 'Ah you big fluffy kitty.'" ;; "babelfish") echo "Did it fall out your ear?" ;; *) echo "You discovered an unknown animal, name it!" ;; esac if [ nocase = yes ] ; then shopt -s nocasematch; else shopt -u nocasematch; fi -
If time permits (or if you are waiting for other students to finish this practice), take a look at Linux system scripts in /etc/init.d and /etc/rc.d and try to understand them. Where does execution of a script start in /etc/init.d/samba? There are also some hidden scripts in ~, we will discuss them later.