Bash: Difference between revisions
(Bash quick tutorial + shell commands (first shot)) |
(Extending the basic tutorial) |
||
Line 1: | Line 1: | ||
==Documentation== |
==Documentation== |
||
* [http://ldp.unixtech.be/LDP/abs/html/index.html Advanced Bash-Scripting Guide] |
* [http://ldp.unixtech.be/LDP/abs/html/index.html Advanced Bash-Scripting Guide] |
||
* http://www.ss64.com/bash/index.html |
* http://www.ss64.com/bash/index.html (find Linux shell command if you know Windows shell command, and vice-versa) |
||
* http://tille.xalasys.com/training/bash |
* http://tille.xalasys.com/training/bash |
||
Line 15: | Line 15: | ||
==Script Quick Tutorial== |
==Script Quick Tutorial== |
||
===Warning / Common pitfalls === |
|||
A set of warnings / common pitfalls when writing script in '''Bash''': |
|||
{| class="wikitable" |
|||
|- |
|||
!Description |
|||
!Example |
|||
|- |
|||
|'''Space!''' - Don't forget to add spaces whenever necessary, in particular around brace in '''function''' definition, or in test conditions for '''if'''s. |
|||
| |
|||
<tt>if <font color="red">''-space-''</font> [ <font color="red">''-space-''</font> -f /etc/foo <font color="red">''-space-''</font> ]; then ...</tt><br/> |
|||
<tt>function myfunc() { <font color="red">''-space-''</font> echo Hello, World!; }</tt> |
|||
|- |
|||
|'''Quote''' - Always quote parameters, variables passed to test in if ... then ... else: |
|||
| |
|||
<tt>if [ <font color="red">"$name"</font> -eq 5 ]; then ...</tt> |
|||
|- |
|||
|'''For loops with file''' - Use simply '''*''' to list files in for loops, not <tt>`ls *`</tt>: |
|||
| |
|||
<source lang="bash" enclose="valid"> |
|||
for file in *; cat "$file"; done # SUCCEEDS, even if white space |
|||
for file in `ls *`; cat "$file"; done # FAILS miserably |
|||
</source> |
|||
|} |
|||
===Environment=== |
===Environment=== |
||
Line 23: | Line 48: | ||
</source> |
</source> |
||
=== |
===Quotation=== |
||
{| class="wikitable" |
|||
|- |
|||
|<source lang="bash" enclose="valid">echo "var is $var"</source> |
|||
|String enclosed in double quote is treated as a '''single''' argument. Variable and special characters are expanded. |
|||
|- |
|||
|<source lang="bash" enclose="valid">echo 'it costs $10'</source> |
|||
|Same as double quote, but variables are not expanded. |
|||
|- |
|||
|<source lang="bash" enclose="valid">x=`expr $x + 1`</source> |
|||
|Back-quote is replaced by the result of the invoked command. |
|||
|- |
|||
|<source lang="bash" enclose="valid">x=$(expr $x + 1)</source> |
|||
|Same as back-quote, but somewhat clearer |
|||
|- |
|||
|<source lang="bash" enclose="valid">x=$(($x + 1))</source> |
|||
|Built-in Bash expression evaluator. Accepts operation are <tt>+, -, *, /, %</tt>. |
|||
|} |
|||
===If ... then ... [elif ...] ... else ... fi=== |
|||
The basic syntax for if-then-else sequence control is |
|||
'''if''' list; '''then''' list; ''[ '''elif''' list; '''then''' list; ]'' ... ''[ '''else''' list; ]'' '''fi''' |
|||
Test conditions can be written using command <tt>test</tt>, or equivalently using the ''square brackets''. |
|||
<source lang="bash"> |
<source lang="bash"> |
||
if test -f /etc/foo; then echo file found; else echo file NOT found; fi |
|||
if [ -f /etc/foo ]; then echo file found; else echo file NOT foudn; fi |
|||
</source> |
|||
Here's <tt>test</tt>'s options: |
|||
<source lang="bash"> |
|||
-d FILE # Check if the file is a directory |
|||
-e FILE # Check if the file exists |
|||
-f FILE # Check if the file is a regular file |
|||
-g FILE # Check if the file hash SGID permissions |
|||
-r FILE # Check if the file is readable |
|||
-s FILE # Check if the file's size is not 0 |
|||
-u FILE # Check if the file has SUID permissions |
|||
-w FILE # Check if the file is writeable |
|||
-x FILE # Check if the file is executable |
|||
NBR1 -eq NBR2 # Check is NBR1 is equals to NBR2 |
|||
NBR1 -ne NBR2 # Check if NBR1 is not equals to NBR2 |
|||
NBR1 -ge NBR2 # Check if NBR1 is greater than or equal to NBR2 |
|||
NBR1 -gt NBR2 # Check if NBR1 is greater than NBR2 |
|||
NBR1 -le NBR2 # Check if NBR1 is less than or equal to NBR2 |
|||
NBR1 -lt NBR2 # Check if NBR1 is less than NBR2 |
|||
STR1 = STR2 # Check if STR1 is the same as STR2 |
|||
STR1 != STR2 # Check if STR1 is not the same as STR2 |
|||
-n STR # Evaluates to true if STR is not null |
|||
-z STR # Evaluates to true if STR is null. |
|||
</source> |
|||
Conditions can be combined with <tt>''&&''</tt> and <tt>''||''</tt>: |
|||
<source lang="bash"> |
|||
if [ $x -ge 5] && [ $x -le 10]; then ... |
|||
if [ $x -gt 5] || [ $x -lt 10]; then ... |
|||
</source> |
|||
Some examples: |
|||
<source lang="bash"> |
|||
if [ "$name" eq 5 ]; then echo one; else echo two; fi |
|||
if [ -d ~/var ]; then touch ~/var/done; else { mkdir ~/var; touch ~/var/done; } fi # braces { ... } are optional here |
|||
</source> |
|||
====Alternative: Using <tt>&&</tt> and <tt>||</tt>==== |
|||
Using <tt>&&</tt> and <tt>||</tt>, one can also build a if ... then ... else statement: |
|||
<source lang="bash"> |
|||
[ -f /etc/hosts ] && { echo one; echo two; } || { echo three; echo four; } |
|||
</source> |
|||
===WHILE / UNTIL Loops=== |
|||
Syntax is |
|||
'''while''' list; '''do''' list; '''done''' |
|||
'''until''' list; '''do''' list; '''done''' |
|||
Some examples of '''While''' loops: |
|||
<source lang="bash"> |
|||
while true; do |
|||
echo "Press CTRL-C to quit." |
|||
done |
|||
# Faster alternative using Bash built-in colon feature |
|||
while :; do |
|||
echo "Press CTRL-C to quit." |
|||
done |
|||
# A more complete example |
|||
x=0; # initialize x to 0 |
|||
while [ "$x" -le 10 ]; do |
|||
echo "Current value of x: $x" |
|||
# increment the value of x: |
|||
x=$(expr $x + 1) |
|||
sleep 1 |
|||
done |
|||
</source> |
|||
Some examples of '''until''' loops: |
|||
<source lang="bash"> |
|||
x=0 |
|||
until [ "$x" -ge 10 ]; do |
|||
echo "Current value of x: $x" |
|||
x=$(expr $x + 1) |
|||
sleep 1 |
|||
done |
|||
</source> |
|||
===FOR Loops=== |
|||
There are 2 syntax for '''for''' loops: |
|||
'''for''' name ''[ '''in''' word ]'' ; '''do''' list ; '''done''' |
|||
'''for''' (( expr1 ; expr2 ; expr3 )) ; '''do''' list ; '''done''' |
|||
The first syntax enumerates a list, as in: |
|||
<source lang="bash"> |
|||
# Counting from 1 to 10... |
|||
for dots in 1 2 3 4 5 6 7 8 9 10; do echo -n "$dots... "; done; |
|||
echo 'Done !' |
|||
# Enumerating a list |
|||
for fruit in Apple Pear Cherry; do |
|||
echo "The value of variable fruit is: $fruit" |
|||
sleep 1 |
|||
done |
|||
# Acting on a directory list: |
|||
for file in *; do |
|||
echo "Adding .html extension to $file..." |
|||
mv $file $file.html |
|||
sleep 1 |
|||
done |
|||
# On several lines |
# On several lines |
||
for f in $(seq 33296 33330); do |
for f in $(seq 33296 33330); do |
||
Line 32: | Line 186: | ||
# On a single line |
# On a single line |
||
for i in `seq 278 328`; do wget http://i.techrepublic.com.com/gallery/33$i.jpg; done |
for i in `seq 278 328`; do wget http://i.techrepublic.com.com/gallery/33$i.jpg; done |
||
</source> |
|||
===case ... in ... esac=== |
|||
The syntax is |
|||
'''case''' ''word'' '''in''' [ [(] ''pattern'' [ | ''pattern'' ] ... ) ''list'' ;; ] ... '''esac''' |
|||
Some example |
|||
<source lang="bash"> |
|||
x=5 # initialize x to 5 |
|||
# now check the value of x: |
|||
case $x in |
|||
0) echo "Value of x is 0." |
|||
;; |
|||
5) echo "Value of x is 5." |
|||
;; |
|||
9) echo "Value of x is 9." |
|||
;; |
|||
*) echo "Unrecognized value." |
|||
esac |
|||
</source> |
|||
===Functions=== |
|||
Functions are like aliases, but accept parameters: |
|||
<source lang="bash"> |
|||
function myfunc1() { echo "$2"; echo "$1"; } |
|||
myfunc2() { MYVAR="$1"; echo Another $MYVAR function; } #The keyword ''function'' is optional |
|||
</source> |
</source> |
||
Line 70: | Line 248: | ||
PASSWORD=abcdefghijklmnopqrstuvwxyz0123456789 |
PASSWORD=abcdefghijklmnopqrstuvwxyz0123456789 |
||
PASSWORD=------------------------------------ |
PASSWORD=------------------------------------ |
||
</source> |
|||
===Signal trapping=== |
|||
Use <tt>'''trap'''</tt> to trap signals send to a Bash script, and redirect execution to a given function/command. |
|||
<source lang="bash"> |
|||
#Trap Ctrl-C (signal SIGINT) by executing function "sorry" |
|||
trap sorry INT |
|||
sorry() { echo "I'm sorry Dave. I can't do that."; sleep 3; } |
|||
</source> |
|||
Signals can also be ignored or reset. |
|||
<source lang="bash"> |
|||
#reset the trap: |
|||
trap - INT |
|||
#do nothing when SIGINT is caught: |
|||
trap " INT |
|||
</source> |
</source> |
||
===Miscellaneous=== |
===Miscellaneous=== |
||
Execute the output of a process |
|||
{| class="wikitable" |
|||
<source lang="bash">eval 'dircolors'</source> |
|||
|- |
|||
|Use <tt>eval</tt> to execute the output (stdout) of a process |
|||
|<source lang="bash" enclose="valid">eval 'dircolors'</source> |
|||
|- |
|||
|Use <tt>$$</tt> as file name suffix or prefix to create unique name for temporary file |
|||
|<source lang="bash" enclose="valid"> |
|||
touch /tmp/mytmp.$$ |
|||
echo some text>/tmp/mytmp.$$ |
|||
cat /tmp/mytmp.$$ |
|||
</source> |
|||
|} |
|||
== References == |
|||
* [http://www.filibeto.org/sun/lib/development/shell/intr_to_bash_scr.html], or with better formatting [http://www.justlinux.com/nhf/Programming/Introduction_to_bash_Shell_Scripting.html]. |
Revision as of 16:12, 22 September 2008
Documentation
- Advanced Bash-Scripting Guide
- http://www.ss64.com/bash/index.html (find Linux shell command if you know Windows shell command, and vice-versa)
- http://tille.xalasys.com/training/bash
Shell
Listing directory content:
ls --full-time # To list files full date & time
ls -d directory-name # List directory entries, not content
# eg. ls -d /etc/rc*
ls -lS | head # List biggest files first - print only first 10 entries
ls -lt | head -n 20 # List most recent files first, print only 20 first entries
Script Quick Tutorial
Warning / Common pitfalls
A set of warnings / common pitfalls when writing script in Bash:
Description | Example |
---|---|
Space! - Don't forget to add spaces whenever necessary, in particular around brace in function definition, or in test conditions for ifs. |
if -space- [ -space- -f /etc/foo -space- ]; then ... |
Quote - Always quote parameters, variables passed to test in if ... then ... else: |
if [ "$name" -eq 5 ]; then ... |
For loops with file - Use simply * to list files in for loops, not `ls *`: |
for file in *; cat "$file"; done # SUCCEEDS, even if white space
for file in `ls *`; cat "$file"; done # FAILS miserably
|
Environment
export MYVAR=myvalue && command-to-execute #MYVAR defined for the current shell and invoked shell
MYVAR=myvalue && command-to-execute #MYVAR defined for the current shell
MYVAR=myvalue command-to-execute #MYVAR only defined for the subsequent command
Quotation
echo "var is $var"
|
String enclosed in double quote is treated as a single argument. Variable and special characters are expanded. |
echo 'it costs $10'
|
Same as double quote, but variables are not expanded. |
x=`expr $x + 1`
|
Back-quote is replaced by the result of the invoked command. |
x=$(expr $x + 1)
|
Same as back-quote, but somewhat clearer |
x=$(($x + 1))
|
Built-in Bash expression evaluator. Accepts operation are +, -, *, /, %. |
If ... then ... [elif ...] ... else ... fi
The basic syntax for if-then-else sequence control is
if list; then list; [ elif list; then list; ] ... [ else list; ] fi
Test conditions can be written using command test, or equivalently using the square brackets.
if test -f /etc/foo; then echo file found; else echo file NOT found; fi
if [ -f /etc/foo ]; then echo file found; else echo file NOT foudn; fi
Here's test's options:
-d FILE # Check if the file is a directory
-e FILE # Check if the file exists
-f FILE # Check if the file is a regular file
-g FILE # Check if the file hash SGID permissions
-r FILE # Check if the file is readable
-s FILE # Check if the file's size is not 0
-u FILE # Check if the file has SUID permissions
-w FILE # Check if the file is writeable
-x FILE # Check if the file is executable
NBR1 -eq NBR2 # Check is NBR1 is equals to NBR2
NBR1 -ne NBR2 # Check if NBR1 is not equals to NBR2
NBR1 -ge NBR2 # Check if NBR1 is greater than or equal to NBR2
NBR1 -gt NBR2 # Check if NBR1 is greater than NBR2
NBR1 -le NBR2 # Check if NBR1 is less than or equal to NBR2
NBR1 -lt NBR2 # Check if NBR1 is less than NBR2
STR1 = STR2 # Check if STR1 is the same as STR2
STR1 != STR2 # Check if STR1 is not the same as STR2
-n STR # Evaluates to true if STR is not null
-z STR # Evaluates to true if STR is null.
Conditions can be combined with && and ||:
if [ $x -ge 5] && [ $x -le 10]; then ...
if [ $x -gt 5] || [ $x -lt 10]; then ...
Some examples:
if [ "$name" eq 5 ]; then echo one; else echo two; fi
if [ -d ~/var ]; then touch ~/var/done; else { mkdir ~/var; touch ~/var/done; } fi # braces { ... } are optional here
Alternative: Using && and ||
Using && and ||, one can also build a if ... then ... else statement:
[ -f /etc/hosts ] && { echo one; echo two; } || { echo three; echo four; }
WHILE / UNTIL Loops
Syntax is
while list; do list; done until list; do list; done
Some examples of While loops:
while true; do
echo "Press CTRL-C to quit."
done
# Faster alternative using Bash built-in colon feature
while :; do
echo "Press CTRL-C to quit."
done
# A more complete example
x=0; # initialize x to 0
while [ "$x" -le 10 ]; do
echo "Current value of x: $x"
# increment the value of x:
x=$(expr $x + 1)
sleep 1
done
Some examples of until loops:
x=0
until [ "$x" -ge 10 ]; do
echo "Current value of x: $x"
x=$(expr $x + 1)
sleep 1
done
FOR Loops
There are 2 syntax for for loops:
for name [ in word ] ; do list ; done for (( expr1 ; expr2 ; expr3 )) ; do list ; done
The first syntax enumerates a list, as in:
# Counting from 1 to 10...
for dots in 1 2 3 4 5 6 7 8 9 10; do echo -n "$dots... "; done;
echo 'Done !'
# Enumerating a list
for fruit in Apple Pear Cherry; do
echo "The value of variable fruit is: $fruit"
sleep 1
done
# Acting on a directory list:
for file in *; do
echo "Adding .html extension to $file..."
mv $file $file.html
sleep 1
done
# On several lines
for f in $(seq 33296 33330); do
wget "http://i.techrepublic.com.com/gallery/${f}.jpg"
done
# On a single line
for i in `seq 278 328`; do wget http://i.techrepublic.com.com/gallery/33$i.jpg; done
case ... in ... esac
The syntax is
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
Some example
x=5 # initialize x to 5
# now check the value of x:
case $x in
0) echo "Value of x is 0."
;;
5) echo "Value of x is 5."
;;
9) echo "Value of x is 9."
;;
*) echo "Unrecognized value."
esac
Functions
Functions are like aliases, but accept parameters:
function myfunc1() { echo "$2"; echo "$1"; }
myfunc2() { MYVAR="$1"; echo Another $MYVAR function; } #The keyword ''function'' is optional
Interactivity
Read User's password
Reading input from user is done with the commands read. To prevent password echo on display, one can use the command stty:
###### Preventing echo with stty ######
read -p "Username: " uname
stty -echo
read -p "Password: " passw; echo
stty echo
Preventing password echo using option -s:
###### Using -s ######
PASS="abc123"
read -s -p "Password: " mypassword
echo ""
[ "$mypassword" == "$PASS" ] && echo "Password accepted" || echo "Access denied"
Complete solution:
###### Complete solution ######
USER=wbi\\titeuf
echo "Mounting windows share... Please type password for user $USER..."
# time-out after 60sec, raw input no escaping, no echo, prompt
read -t 60 -r -s -p "Password: " PASSWORD
# delete prompt line
echo -e -n "\r"
mount -t smbfs -o username="$USER",password="$PASSWORD",iocharset=iso8859-1,codepage=cp437 //windows-host/C$ /mnt/c
mount -t smbfs -o username="$USER",password="$PASSWORD",iocharset=iso8859-1,codepage=cp437 //windows-host/D$ /mnt/d
mount -t smbfs -o username="$USER",password="$PASSWORD",iocharset=iso8859-1,codepage=cp437 //windows-host/F$ /mnt/f
# delete password variable
PASSWORD=------------------------------------
PASSWORD=abcdefghijklmnopqrstuvwxyz0123456789
PASSWORD=------------------------------------
Signal trapping
Use trap to trap signals send to a Bash script, and redirect execution to a given function/command.
#Trap Ctrl-C (signal SIGINT) by executing function "sorry"
trap sorry INT
sorry() { echo "I'm sorry Dave. I can't do that."; sleep 3; }
Signals can also be ignored or reset.
#reset the trap:
trap - INT
#do nothing when SIGINT is caught:
trap " INT
Miscellaneous
Use eval to execute the output (stdout) of a process | eval 'dircolors'
|
Use $$ as file name suffix or prefix to create unique name for temporary file | touch /tmp/mytmp.$$
echo some text>/tmp/mytmp.$$
cat /tmp/mytmp.$$
|