• Many available shells: ksh, pdksh, bash, zsh, csh, tcsh, ... • It is a command interpreter : each line is interpreted and executed immediately • A few commands are specific to the shell (cd, kill,...) • Most Linux commands are programs (ls, grep, echo,...) located in /bin or /usr/bin • Such commands are independent of the shell 1
2. Make each program do one thing well. 3. Build a prototype as soon as possible. 4. Choose portability over efficiency. 5. Store data in flat text files. 6. Use software leverage to your advantage. 7. Use shell scripts to increase leverage and portability. 8. Avoid captive user interfaces. 9. Make every program a filter. (see Wikipedia: Unix Philosophy) 2
login shell it executes the /etc/profile file followed by the .bash_profile file located in the user's home directory • The the .bashrc file is executed (even for non-login shells) • When a login shell exits, the .bash_logout file is executed 5
fork : the current bash process creates a copy of itself and both processes execute concurrently (in the same process group) • The original bash process waits for an interruption of the@ new process (background) • The new bash process transforms itself (exec) into the called program (foreground) • Ctrl-C sends a TERM signal to the process in the foreground, asking the process to terminate • Ctrl-Z stops the running process, and the background process comes to the foreground 6
in the background $ jobs [1]+ Running program_name program_arguments & Job number 1 is running in the background $ fg %1 Bring job number 1 in the foreground [Ctrl-Z] $ bg Stop job number 1, return to the shell and resume it in the background 7
exit code is 0 if the process ends normally. Otherwise, the exit code can be used to understand the abnormal termination. $ ls toto && echo OK toto OK $ ls titi && echo OK ls: cannot access titi: No such file or directory Runs the command ls toto. If the exit code is zero, run echo OK. && is interpreted as and. 8
ls titi || echo Failed ls: cannot access titi: No such file or directory Failed If the exit code is not zero, run echo Failed. || is interpreted as or. $ ls toto && echo OK || echo Failed toto OK $ ls titi && echo OK || echo Failed ls: cannot access titi: No such file or directory Failed 9
: Matches any character@ • [a-f] : Matches any of a,b,c,d,e,f • [4-8] : Matches any of 4,5,6,7,8 • [G-I] : Matches any of G,H,I • [azerty] : Matches any of a,z,e,r,t,y • [^azerty] : Matches everything except a,z,e,r,t,y $ ls *.tex bash.tex $ ls bash.??? bash.tex bash.aux bash.dvi bash.pdf $ ls bash.*[^x] bash.dvi bash.pdf bash.text 10
EMPTY= Variables can be unset: $ unset TMPDIR The value of a variable is obtained by the $ operator: $ echo TMPDIR $TMPDIR TMPDIR /tmp New values can be appended to a variable: $ TMPDIR+=/my_tmp ; echo $TMPDIR /tmp/my_tmp 12
named values that can affect the way running processes will behave on a computer. • HOME : Home directory • SHELL : Name of the current shell • USER : Current user name • PATH : List of directories where to search for executables • LD_LIBRARY_PATH : List of directories where to search for shared libraries Variables can be declared using the declare keyword declare [-aAfFgilrtux] [-p] [name[=value] ...] • -a : Indexed array variable • -A : Associative array variable 13
• -l : All upper-case characters are converted to lower-case • -u : All lower-case characters are converted to upper-case • -r : Read-only • -x : Export to the environment $ declare -i int_var=1 $ text_var=1 $ text_var+=2 ; int_var+=2 $ echo $text_var $int_var 12 3 $ declare -u upper_var="This is an Upper Case example" $ echo $upper_var THIS IS AN UPPER CASE EXAMPLE $ declare -r read_only_var="Unchangeable" $ read_only_var="Modified" bash: read_only_var: readonly variable 14
scripts section) • ? : Exit status of the most recently executed process, • $ : Process ID of the shell • ! : Process ID of the most recently executed background command. • _ : Last argument to the previous command • RANDOM : Returns a integer uniform random number between 0 and 32767 $ ./a.out & [1] 20941 $ echo $! 20941 $ echo $$ 20717 $ ls titi ls: cannot access titi: No such file or directory 15
• declared with declare -a • affected like: A[3]=three • used like: ${A[3]} The braces around A[3] shows on what the $ operator acts. • If the subscript is less than zero, it is used as an offset from the end • Associative arrays are referenced using integers • declared with declare -A • affected as: A[name]=Anthony • used as: ${A[name]} Indexed arrays can be affected in a compound statement: 17
command (file descriptor 0) is redirected to file input_file $ command > output_file The standard output of command (file descriptor 1) is redirected to file output_file $ command >> output_file The standard output of command (file descriptor 1) is appended to file output_file $ command 2> error_file 19
to file error_file $ command < input > output_file 2> error_file Multiple outputs can be merged : $ command > output_file 2>&1 The output of command is redirected to output_file, and then the standard error is redirected to the standard output. This can be re-written as $ command &> output_file Example using file descriptors $ echo 1234567890 > File # Write string to "File" $ exec 3<> File # Open "File" with fd 3 $ read -n 4 <&3 # Read only 4 chars $ echo -n . >&3 # Write a decimal point $ exec 3>&- # Close fd 3 20
mkfifo creates a named pipe on the file system. $ mkfifo my_pipe $ ls -l prw-rw-r-- 1 scemama scemama 0 Apr 2 23:20 mypipe $ ls > my_pipe & $ # Do some stuff... $ cat my_pipe prw-rw-r-- 1 scemama scemama 0 Apr 2 23:20 mypipe [1]+ Done ls > mypipe $ rm my_pipe The named pipe has to be removed when finished. 23
> input file > in an interactive shell > EOF This is my input file in an interactive shell Read input until a line containing only EOF is seen. All of the lines read up to that point are then used as the standard input for a command. 24
is substituted. Otherwise, the value of parameter is substituted. • ${parameter:=word} : Assign Default Values. • ${parameter:?word} : Display Error if Null or Unset. • ${parameter:+word} : Use Alternate Value. • ${parameter:offset} : Substring starting at the offset character • ${parameter:offset:length} : Substring starting at the offset character up to length characters $ B=${A:?Error message} bash: A: Error message $ B=${A:-First} ; echo $B First $ B=${A:=Second} ; echo $B Second 29
$ echo ${A#This is} my test string $ echo ${A%test string} This is my $ echo ${A/my/your} This is your test string $ echo ${A^^} THIS IS MY TEST STRING 31
the result of the expression. The let keyword evaluates arithmetic expressions: $ A=$((3+5)) ; echo $A 8 $ let A++ ; echo $A 9 $ A=$((1<<6)) ; echo $A # Bit shift 64 32
: displays the manual page of command. For all commands in this section, you are encouraged to look at the man pages (including man bash, or man uranus). 34
cat File1 content of first file $ cat File2 the second file $ cat File1 File2 content of first file the second file zcat is the same as cat for gzipped files. 36
date Wed Apr 3 00:32:29 CEST 2013 $ date --date="Next Monday" Mon Apr 8 00:00:00 CEST 2013 $ date -r mypipe Tue Apr 2 23:23:35 CEST 2013 date -r displays the last modification time of a file. 38
standard math library. $ bc -l bc 1.06.95 Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'. 2.+3. 5. $ echo 5./3. | bc -l 1.66666666666666666666 A trick to be able to use floating point operations in bash 48
3.8.CAS.out ----- FROZEN CORE ENERGY = -182.7238608120 STATE # 1 ENERGY = -198.806582658 STATE # 1 ENERGY = -198.806584871 ONE ELECTRON ENERGY = -301.0460998455 TWO ELECTRON ENERGY = 90.9596841354 $ grep -m 1 "ENERGY =" 3.8.CAS.out ----- FROZEN CORE ENERGY = -182.7238608120 $ grep "energy =" 3.8.CAS.out $ grep -m 1 -i "energy =" 3.8.CAS.out ----- FROZEN CORE ENERGY = -182.7238608120 53
; seq 10 14 > f2 $ paste f1 f2 1 10 2 11 3 12 4 13 14 $ paste -s f1 f2 1 2 3 4 10 11 12 13 14 $ paste -s -d 'x' f1 f2 1x2x3x4 10x11x12x13x14 If the delimiter is set to \n, zip the lines of the 2 input files. 55
23:59 warning: commands will be executed using (in order) a) $SHELL b) login shell c) /bin/sh at> /usr/bin/do_my_backup job 103 at Wed Apr 3 23:59:00 2013 $ atq 103 Wed Apr 3 23:59:00 2013 a scemama 56
100M 100M BigFile $ split -b 30M BigFile SmallFile. $ ls -sh total 200M 100M BigFile 30M SmallFile.aa 30M SmallFile.ab 30M SmallFile.ac 10M SmallFile.ad 61
f1 Adams A. 555-6235 Erwin G. 555-1234 Lewis B. 555-3237 Norwood M. 555-5341 Wright M. 555-1234 Xandy G. 555-5015 $ cat f2 Erwin Dept. 389 Nicholson Dept. 311 Norwood Dept. 454 Wright Dept. 520 Xandy Dept. 999 $ join f1 f2 68
f2 Dickerson B. 555-1842 [-Erwin-] G. {+Erwin+} 555-1234 Jackson J. 555-0256 [-Lewis B. 555-3237-] Norwood M. 555-5341 Smartt D. 555-1540 {+Scemama A. 555-3237+} Wright M. 555-1234 71
$ echo wrap each input line to fit in \ specified width | fold -w 12 wrap each in put line to fit in speci fied width $ echo wrap each input line to fit \ in specified width | fold -s -w 12 wrap each input line to fit in specified width 72
-d FILE : FILE is a directory • -p FILE : FILE is a named pipe • -r FILE : FILE has read permissions • -w FILE : FILE has write permissions • -x FILE : FILE has execute permissions • -s FILE : FILE has a size >0 • FILE1 -nt FILE2 : FILE1 is newer than FILE2 • FILE1 -ot FILE2 : FILE1 is older than FILE2 81
one. • @ Same as * but different when within double quotes • # Number of arguments • - Current option flags given to bash • 0 0 Expands to the name of the script • _ Absolute pathname used to invoke the script • 1,2,...,N Expands to the argument 90
shift the next ones one the left. #!/bin/bash echo $@ shift echo $@ shift 2 echo $@ $ test.sh one two three four five one two three four five two three four five four five 93
parameters ($0..$n). #!/bin/bash set one two three four five six until [[ -z $@ ]] do echo $1 $2 shift 2 done $ ./shift.sh one two three four five six 95
list • -l : Long options list • -n : Name reported when getopt returns errors • If an option is followed by :, it needs an argument Move commands on the left: $ getopt -o "1:23" -l "one:,two,three" -- test \ -1 three --one=two arg1 arg2 -2 --three -1 'three' --one 'two' -2 --three -- 'test' 'arg1' 'arg2' 96
COMMANDS return INTEGER } my_func The arguments of the function are positional arguments inside the functions. Return code is optional #!/bin/bash get_cpu_load() { local A A=$(uptime | cut -d: -f4) 99
and \ • -d : Set delimiter instead of newline • -n : Read n characters • -p : Prompt string • -s : Secure input (passwords) function pause() { local X read -s -r -n 1 -p \ "Press any key to continue..." X } 103
you have submitted hundreds of jobs by mistake. You want to kill all your jobs in the queue. On your cluster, the qstat command returns this output: $ qstat job-ID prior name user state submit/start at queue ----------------------------------------------------------------------------------------- 82851 2.50000 job_dummy scemama r 04/10/2013 13:45:51 [email protected] 82860 2.50000 job_dummy scemama r 04/10/2013 13:45:51 [email protected] 82868 2.50000 job_dummy scemama r 04/10/2013 13:45:51 [email protected] 82875 2.50000 job_dummy scemama r 04/10/2013 13:45:52 [email protected] [...] 82942 1.47958 job_dummy scemama qw 04/10/2013 13:45:55 82943 1.46969 job_dummy scemama qw 04/10/2013 13:45:55 82944 1.45999 job_dummy scemama qw 04/10/2013 13:45:55 82902 1.45048 job_dummy scemama qw 04/10/2013 13:45:53 • Read the output of qstat without the 2 first lines using qstat | tail --lines=+3 106
job ID qstat | tail --lines=+3 | cut -b-8 • Now, use this output as command-line arguments of the qdel command $ qstat | tail --lines=+3 | cut -b-8 | xargs qdel scemama has registered the job 82851 for deletion scemama has registered the job 82860 for deletion scemama has registered the job 82868 for deletion [...] scemama has deleted job 82943 scemama has deleted job 82944 scemama has deleted job 82902 $ qstat $ 107
that generates very large files. You want this files to be gzipped and gunzipped on the fly, and you don't have access to the source of the program. For the example, we use the following program: • If the -c option is present, it creates a 2000x2000 matrix filled with random numbers and the matrix is written in the matrix file • If the -c option is not present, it reads the matrix from the file • The program returns the max and min elements of the matrix $ /usr/bin/time ./minmax -c Creating Matrix Writing Matrix Min: 6.94080884877656956E-007 Max: 0.99999989401156275 5.74user 0.16system 0:06.09elapsed 96%CPU (0avgtext+0avgdata 108
in the background to gzip or gunzip your large file on the fly through a pipe. #!/bin/bash mkfifo matrix if [[ $1 == -c ]] then gzip < matrix > matrix.gz & else gunzip < matrix.gz > matrix & fi ./minmax $@ rm matrix $ /usr/bin/time ./minmax.sh -c Creating Matrix Writing Matrix Min: 6.94080884877656956E-007 Max: 0.99999989401156275 110
graphically your CPU load in real time. Step 1 Use the uptime command to get the CPU load, and save it to a data file every second: #!/bin/bash DATA_FILE=/tmp/data_file rm -f $DATA_FILE while true do uptime >> $DATA_FILE # 01:34:38 up 4:20, 2 users, load average: 0.31, 0.20, 0.16 sleep 1 done 112
with lines replot '$DATA_FILE' using :2 with lines replot '$DATA_FILE' using :3 with lines EOF Step 4 Create a pipe to control gnuplot #!/bin/bash DATA_FILE=/tmp/data_file GNUPLOT_PIPE=/tmp/gnuplot_pipe # If the pipe doesn't exist, create it [[ -e $GNUPLOT_PIPE ]] || mkfifo $GNUPLOT_PIPE # Push commands to the pipe in the background 114
using :1 with lines replot '$DATA_FILE' using :2 with lines replot '$DATA_FILE' using :3 with lines EOF # Start gnuplot and pull stdin from the pipe gnuplot --persist < $GNUPLOT_PIPE # Clean up pipe on exit rm $GNUPLOT_PIPE Alternative way: #!/bin/bash DATA_FILE=/tmp/data_file GNUPLOT_PIPE=/tmp/gnuplot_pipe 115
$GNUPLOT_PIPE ]] || mkfifo $GNUPLOT_PIPE # Start gnuplot and pull stdin from the pipe (background) gnuplot --persist < $GNUPLOT_PIPE & # Push commands to the pipe cat > $GNUPLOT_PIPE << EOF unset key plot '$DATA_FILE' using :1 with lines replot '$DATA_FILE' using :2 with lines replot '$DATA_FILE' using :3 with lines EOF # Clean up pipe on exit rm $GNUPLOT_PIPE 116
open #!/bin/bash DATA_FILE=/tmp/data_file GNUPLOT_PIPE=/tmp/gnuplot_pipe # If the pipe doesn't exist, create it [[ -e $GNUPLOT_PIPE ]] || mkfifo $GNUPLOT_PIPE # Start gnuplot and pull stdin from the pipe (background) tail -f $GNUPLOT_PIPE | gnuplot & # Push commands to the pipe cat > $GNUPLOT_PIPE << EOF unset key plot '$DATA_FILE' using :1 with lines replot '$DATA_FILE' using :2 with lines 117