Skip to content

Commit df515d1

Browse files
committed
Updated scripting part.
1 parent 2007592 commit df515d1

File tree

2 files changed

+135
-57
lines changed

2 files changed

+135
-57
lines changed

presentation/linux_bash_metacentrum_course.tex

Lines changed: 133 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3331,10 +3331,10 @@ \subsection{Functions}
33313331
33323332
\subsection{BASH variables}
33333333
3334-
\begin{frame}{Special variables available in the script (selection)}
3334+
\begin{frame}{Special BASH internal variables available in the script (selection)}
33353335
\begin{itemize}
33363336
\item These variables can be used within script e.g. to parse arguments provided by the user
3337-
\item \alert{\texttt{\$1}},~\ldots~(number from \texttt{1} up to number of parameters) --- individual positional parameters (see further for example)
3337+
\item \alert{\texttt{\$1}},~\ldots~(number from \texttt{1} up to number of parameters) --- individual positional parameters (see further examples)
33383338
\item \alert{\texttt{\$0}} --- path of the starting script
33393339
\item \alert{\texttt{\$\#}} --- number of command-line arguments
33403340
\item \alert{\texttt{\$*}} --- all of the positional parameters, seen as a~single word, must be quoted (i.e. \texttt{"\$*"})
@@ -3361,6 +3361,7 @@ \subsection{Reading variables}
33613361
\item Script can behave unexpectedly, returning very weird results, damage data
33623362
\item Internal functions/commands can return error messages, which are hard to understand
33633363
\item Attacker can e.g. modify web content (\href{https://en.wikipedia.org/wiki/Cross-site_scripting}{XSS},~\ldots), obtain private data, root privileges,~\ldots
3364+
\item Applies also to scientific data --- wrong input use to have unexpected outcomes\ldots
33643365
\end{itemize}
33653366
\item Programmer should always check if user input is correct, filter it
33663367
\end{itemize}
@@ -3462,16 +3463,33 @@ \subsection{Reading variables}
34623463
echo "Try again (the number):" # Ask user for new input value
34633464
fi # End of the conditional evaluation
34643465
done # End of the while cycles
3465-
} # Read variable is in $INPUT
3466+
} # Read variable is in INPUT
34663467
\end{bashcode}
34673468
\end{frame}
34683469
34693470
\begin{frame}[fragile]{Ensuring user interactively provides correct input (function) II}
34703471
\begin{bashcode}
34713472
# Replace line 4 of interactive2.sh by (similarly for line 6)
3472-
checkinput
3473+
...
3474+
checkinput # Use function declared on previous slide
34733475
V1=$INPUT
3476+
...
3477+
checkinput # Recycle the function to read next variable
3478+
V2=$INPUT
3479+
...
34743480
\end{bashcode}
3481+
\vfill
3482+
\hrule
3483+
\vfill
3484+
\begin{itemize}
3485+
\item User can provide as arguments\ldots
3486+
\begin{itemize}
3487+
\item Input/output data names
3488+
\item Parameters for running whatever analysis --- can be passed to some scientific software within script or so
3489+
\item Which branch of the code to run
3490+
\item \ldots
3491+
\end{itemize}
3492+
\end{itemize}
34753493
\end{frame}
34763494
34773495
\begin{frame}[fragile]{Provide named parameters}
@@ -3491,13 +3509,13 @@ \subsection{Reading variables}
34913509
*) # Any other input
34923510
echo "Wrong option! "
34933511
echo "Usage: 'd' or 'disk' for available disk space or 'u' or"
3512+
echo " 'uptime' for computer uptime"
34943513
# Ends on next slide...
34953514
\end{bashcode}
34963515
\end{frame}
34973516
34983517
\begin{frame}[fragile]{Notes to previous script}
34993518
\begin{bashcode}
3500-
echo " 'uptime' for computer uptime"
35013519
exit 1;; # In this case, exit with error code 1
35023520
esac
35033521
exit
@@ -3516,7 +3534,7 @@ \subsection{Reading variables}
35163534
#!/bin/bash
35173535
# From the beginning (^) to the end ($) at least one (+) number ([0-9])
35183536
NUMBER='^[0-9]+$'
3519-
function usagehelp { # Function to print help - we will use it twice
3537+
function usagehelp { # Function to print help - we will use it four times
35203538
echo "Usage: number1 plus/minus/product/quotient number2"
35213539
echo "Use plus for sum, minus for difference, product"
35223540
echo " for multiplication or quotient for quotient."
@@ -3561,6 +3579,15 @@ \subsection{Reading variables}
35613579
./interactive4.sh 7 plus 5 # For example...
35623580
\end{bashcode}
35633581
\vfill
3582+
\begin{itemize}
3583+
\item Note that we can evaluate input parameters in any order
3584+
\item Compare syntax styling of \texttt{case} on previous slide and in script file \texttt{interactive4.sh}
3585+
\begin{itemize}
3586+
\item Each option can be on single line, or on multiple lines
3587+
\item For each \texttt{case} option there can be any number of commands
3588+
\item There can be any number of \texttt{case} options --- convenient way how to evaluate multiple options in single step
3589+
\end{itemize}
3590+
\end{itemize}
35643591
\end{frame}
35653592
35663593
\begin{frame}[fragile]{Multiple switches in classical UNIX form (no positional) I}
@@ -3573,7 +3600,7 @@ \subsection{Reading variables}
35733600
\begin{bashcode}
35743601
#!/bin/bash
35753602
# All provided values are evaluated in while cycles...
3576-
while getopts "hvi:o:a:" INITARGS; do
3603+
while getopts "hvi:o:a:" INITARGS; do # Switches are -h -v -i -o -a
35773604
case "$INITARGS" in # $INITARGS contains the parameters to evaluate
35783605
h|v) # Accept parameters "-h" or "-v" for help
35793606
echo "Usage options..."
@@ -3626,14 +3653,14 @@ \subsection{Reading variables}
36263653
echo "See \"$0 -h\" for help usage..."
36273654
exit 1
36283655
fi
3629-
if [ -z "$VALUE" ]; then
36303656
# ...ends on following slide...
36313657
\end{bashcode}
36323658
\end{frame}
36333659
36343660
\begin{frame}[fragile]{Multiple switches in classical UNIX form (no positional) IV}
36353661
\begin{bashcode}
36363662
# ...continuing from previous slide...
3663+
if [ -z "$VALUE" ]; then
36373664
echo "Warning! Value for \"-a\" was not provided! Using default (10)."
36383665
VALUE=10
36393666
fi
@@ -3643,20 +3670,27 @@ \subsection{Reading variables}
36433670
# the beginning of the line to overwrite the number in next step
36443671
sleep 1s # Wait 1 second - just for fun ;-)
36453672
# Do the task - append input to the output - note usage of variables
3646-
cat "$INPUTFILE" >> "$OUTPUTFILE" # Do the task - append inp to output
3673+
cat "$INPUTFILE" >> "$OUTPUTFILE" # Do the task - append into to output
36473674
done
36483675
echo -ne "\n" # Reset cursor to new line
36493676
echo "Done!"
36503677
exit
36513678
\end{bashcode}
3652-
\vfill
3653-
\begin{itemize}
3654-
\item Script \texttt{interactive5.sh} contains complete example
3655-
\end{itemize}
36563679
\end{frame}
36573680
36583681
\begin{frame}[fragile]{Multiple switches in classical UNIX form (no positional) V}
3682+
\begin{itemize}
3683+
\item Script \texttt{interactive5.sh} contains complete example
3684+
\item This is the classical way how to use UNIX switches used in most of commands
3685+
\item \texttt{while} loop encapsulating \texttt{case} ensures we evaluate all provided parameters regardless their number or order
3686+
\begin{itemize}
3687+
\item Be prepared that user can use the arguments in any combination and orded (e.g. calling \texttt{-h} together with any other switch) --- avoid in code doing several things at once
3688+
\end{itemize}
3689+
\end{itemize}
3690+
\vfill
36593691
\begin{bashcode}
3692+
# Make it executable
3693+
chmod +x interactive5.sh
36603694
# Start with displaying help
36613695
./interactive5.sh -h # Or ./interactive5.sh -v
36623696
# Try it as common command line tool
@@ -3683,21 +3717,29 @@ \subsection{Reading variables}
36833717
echo "Size of the file is $(du -sh "$1" | cut -f 1)."
36843718
echo "The file has $(wc -l "$1" | cut -d ' ' -f 1) lines."
36853719
echo "Making backup of the file $1..."
3686-
cp "$1" "$1".bak || { echo "Error! Making backup of $1 failed!"; exit 1; }
36873720
# Ends on next slide...
36883721
\end{bashcode}
36893722
\end{frame}
36903723
36913724
\begin{frame}[fragile]{Simple providing of input file II}
36923725
\begin{bashcode}
3693-
# The end from previous slide
3726+
# ...the end from previous slide
3727+
# Copy file to backup (*.bak). If it succeeds, report it, if it fails
3728+
# exit with error (still handling single variable $1)
3729+
{ cp "$1" "$1".bak && echo "Backup saved as $1".bak; } || \
3730+
{ echo "Error! Making backup of $1 failed!"; exit 1; }
36943731
echo "Done!"
36953732
exit
36963733
\end{bashcode}
36973734
\vfill
3698-
\hrule
3735+
\begin{itemize}
3736+
\item Common way for simple scripts --- do something with single input file
3737+
\item Note lines 4 and 5 above --- common way to report success as well as handle failure
3738+
\end{itemize}
36993739
\vfill
37003740
\begin{bashcode}
3741+
# Make it executable
3742+
chmod +x interactive6.sh
37013743
# Use the script with some text file...
37023744
./interactive6.sh long_text.txt
37033745
\end{bashcode}
@@ -3729,14 +3771,17 @@ \subsection{Branching the code}
37293771
37303772
\begin{frame}[allowframebreaks]{Evaluation of conditions}
37313773
\begin{itemize}
3732-
\item \enquote{\texttt{[} \ldots~\texttt{]}} (always keep space around it --- inside) is function to evaluate expressions (alternatively use command \texttt{test})
3774+
\item Basic method to branch code --- do something according to certain condition
3775+
\item Very versatile, usually there are more options how to write desired conditioning
3776+
\item Avoid long chaining of conditions using \texttt{elif} statement (previous slide) --- susceptible to mistakes, hard to debug
3777+
\item \enquote{\texttt{[~\ldots~]}} (always keep space around it --- inside) is function to evaluate expressions (alternatively use command \texttt{test})
37333778
\begin{itemize}
37343779
\item \texttt{if [ "\$VAR" -eq 25 ]} or \texttt{test \$VAR -eq 25}
37353780
\item \texttt{if [ "\$VAR" == "value" ]; \ldots}
37363781
\begin{itemize}
3737-
\item Escaping variables and values by double quotes (\texttt{"}\ldots\texttt{"}) is recommended (to be sure), but not strictly required all the time
3782+
\item Escaping variables and values by double quotes (\texttt{"\ldots"}) is recommended (to be sure), but not strictly required all the time
37383783
\end{itemize}
3739-
\item \texttt{if [ ! -f regularfile ];} \ldots --- reverted condition (\texttt{!})
3784+
\item \texttt{if [ ! -f regularfile ];} \ldots --- \texttt{!} reverts condition
37403785
\item Single-bracket conditions --- file, string, or arithmetic conditions
37413786
\item Double-bracket syntax --- enhanced
37423787
\begin{itemize}
@@ -3746,28 +3791,29 @@ \subsection{Branching the code}
37463791
\item Allows more detailed test, e.g. \texttt{if [[ \$num -eq 3 \&\& "\$STRINGVAR" == XXX ]] \ldots}
37473792
\end{itemize}
37483793
\end{itemize}
3749-
\item -\texttt{eq} --- Equal to
3750-
\item -\texttt{lt} --- Less than
3751-
\item -\texttt{gt} --- Greater than
3752-
\item -\texttt{ge} --- Greater than or equal to
3753-
\item -\texttt{le} --- Less than or equal to
3754-
\item -\texttt{f \$FILE} --- True if \texttt{\$FILE} exists and is a~regular file
3755-
\item -\texttt{r \$FILE} --- True if \texttt{\$FILE} exists and is readable
3756-
\item -\texttt{w \$FILE} --- True if \texttt{\$FILE} exists and is writable
3757-
\item -\texttt{x \$FILE} --- True if \texttt{\$FILE} exists and is executable
3758-
\item -\texttt{d \$FILE} --- True if \texttt{\$FILE} exists and is a~directory
3759-
\item -\texttt{s \$FILE} --- True if \texttt{\$FILE} exists and has a~size greater than zero
3760-
\item -\texttt{n \$STR} --- True if string \texttt{\$STR} is not a~null (empty) string
3761-
\item -\texttt{z \$STR} --- True if string \texttt{\$STR} is a~null string
3794+
\item \texttt{-eq} --- Equal to (like \texttt{==})
3795+
\item \texttt{-lt} --- Less than
3796+
\item \texttt{-gt} --- Greater than
3797+
\item \texttt{-ge} --- Greater than or equal to
3798+
\item \texttt{-le} --- Less than or equal to
3799+
\item \texttt{-f \$FILE} --- True if \texttt{\$FILE} exists and is a~regular file (not link or so)
3800+
\item \texttt{-r \$FILE} --- True if \texttt{\$FILE} exists and is readable
3801+
\item \texttt{-w \$FILE} --- True if \texttt{\$FILE} exists and is writable
3802+
\item \texttt{-x \$FILE} --- True if \texttt{\$FILE} exists and is executable
3803+
\item \texttt{-d \$FILE} --- True if \texttt{\$FILE} exists and is a~directory
3804+
\item \texttt{-s \$FILE} --- True if \texttt{\$FILE} exists and has a~size greater than zero
3805+
\item \texttt{-n \$STR} --- True if string \texttt{\$STR} is not a~null (empty) string
3806+
\item \texttt{-z \$STR} --- True if string \texttt{\$STR} is a~null string
37623807
\item \texttt{\$STR1 == \$STR2} --- True if both strings are equal
37633808
\item \texttt{\$STR} --- True if string \texttt{\$STR} is assigned a~value and is not null
37643809
\item \texttt{\$STR1 != \$STR2} --- True if both strings are unequal
3765-
\item -\texttt{a} --- Performs the \texttt{AND} function (\texttt{[ \ldots -a \ldots~]} or \texttt{[ \ldots~] \&\& [ \ldots~]})
3766-
\item -\texttt{o} --- Performs the \texttt{OR} function (\texttt{[ \ldots -o \ldots~]} or \texttt{[ \ldots~] || [ \ldots~]})
3767-
\item Do not confuse globing patterns and regular expressions when using \texttt{[[ \ldots~]]}
3810+
\item \texttt{-a} --- Performs the \texttt{AND} function (\texttt{[~\ldots~-a~\ldots~]} or \texttt{[~\ldots~] \&\& [~\ldots~]})
3811+
\item \texttt{-o} --- Performs the \texttt{OR} function (\texttt{[~\ldots~-o~\ldots~]} or \texttt{[~\ldots~] || [~\ldots~]})
3812+
\item Do not confuse globing patterns and regular expressions when using \texttt{[[~\ldots~]]}
37683813
\begin{itemize}
3769-
\item Shell globing: \texttt{if [[ "\$STRINGVAR" \textbf{==} ?[sS]tring* ]]; then} --- \texttt{?} represents single character \texttt{[]} any character inside and \texttt{*} zero or more characters
3770-
\item Regular expressions: \texttt{if [[ "\$STRINGVAR" \textbf{=$\sim$} .[sS]tring.* ]]; then} --- \texttt{.} represents single character (\texttt{?} would be zero or one occurrence of preceding expression), \texttt{[]} any character inside and \texttt{.*} zero or more occurrences of any single characters
3814+
\item \textbf{Shell globing:} \texttt{if [[ "\$STRINGVAR" \alert{==} ?[sS]tring* ]]; then} --- \texttt{?} represents single character \texttt{[]} any character inside and \texttt{*} zero or more characters
3815+
\item \textbf{Regular expressions:} \texttt{if [[ "\$STRINGVAR" \alert{=$\sim$} .[sS]tring.* ]]; then} --- \texttt{.} represents single character (\texttt{?} would be zero or one occurrence of preceding expression), \texttt{[]} any character inside and \texttt{.*} zero or more occurrences of any single characters
3816+
\item Same expression is interpreted in different ways
37713817
\end{itemize}
37723818
\end{itemize}
37733819
\end{frame}
@@ -3819,26 +3865,42 @@ \subsection{Branching the code}
38193865
38203866
\subsection{Loops}
38213867
3822-
\begin{frame}[fragile]{For cycles I}
3868+
\begin{frame}[fragile]{For loops I}
3869+
\begin{itemize}
3870+
\item \texttt{for} loops are available in practically every programming language
3871+
\item BASH allows plenty of variants how to declare repetitions
3872+
\item In \texttt{for} loop we know in advance number of repeats --- numerical sequence, list of files,~\ldots
3873+
\item Common way how to do same operation with multiple files
3874+
\end{itemize}
38233875
\begin{bashcode}
38243876
# Ways how to declare number of repetitions
3877+
# Variable $I contains in every repeat number from 1 to 10 - number of
3878+
# respective repeat
38253879
for I in $(seq 1 10); do echo $I; done # "seq" is outdated
38263880
for I in 1 2 3 4 5 6 7 8 9 10; do echo $I; done
38273881
for I in {1..10}; do echo $I; done
38283882
for (( I=1; I<=10; I++ )); do echo $I; done
3829-
# One line for cycle for resizing of images (another option, as above)
3883+
# One line for cycle for resizing of multiple images
3884+
# Variable $JPGF contains in every repeat one-by-one name of input file
3885+
# processed in the respective turn
38303886
for JPGF in *.jpg; do convert $JPGF -resize 100x100 thumbs-$JPGF; done
3831-
# More commands in a block
3832-
for JPGF in $(ls -1 *.jpg); do
3833-
echo "Processing JPG $JPGF"
3834-
convert $JPGF -resize 100x100 thumbs-$JPGF
3835-
echo "File thumbs-$file created"
3836-
done
38373887
\end{bashcode}
38383888
\end{frame}
38393889
3840-
\begin{frame}[fragile]{For cycles II}
3890+
\begin{frame}[fragile]{For loops II}
3891+
\begin{itemize}
3892+
\item Basic way to process more files
3893+
\end{itemize}
38413894
\begin{bashcode}
3895+
# More commands in a block
3896+
for JPGF in *.jpg; do
3897+
echo "Processing JPG $JPGF"
3898+
file $JPGF # Get information about currently processed file
3899+
convert $JPGF -resize 100x100 thumbs-$JPGF
3900+
echo "File thumbs-$JPGF created"
3901+
done
3902+
# Passing through each loop can be influenced using conditions and
3903+
# subsequent skipping of rest of the loop
38423904
for ...; do # Start cycles as you need
38433905
command1 # command1 will be executed in any case
38443906
if (condition); then # Set some condition to skip command2
@@ -3848,7 +3910,7 @@ \subsection{Loops}
38483910
\end{bashcode}
38493911
\end{frame}
38503912
3851-
\begin{frame}[fragile]{While and until cycles I}
3913+
\begin{frame}[fragile]{While and until loops I}
38523914
\begin{bashcode}
38533915
# while cycle is evaluating condition and if it is equal to 0 (TRUE)
38543916
# the cycle body is launched, repeatedly while the condition is met
@@ -3859,20 +3921,34 @@ \subsection{Loops}
38593921
until condition; do
38603922
commands
38613923
done
3924+
# Compare differences between while (more common) and until loops
3925+
I=0 # Assign initial value
3926+
# Repeat while I is lower or equal 10; in every step increment I by 1
3927+
while [ $I -le 10 ]; do echo "Value: $I"; I=$(($I + 1)); done
3928+
I=20 # Assign initial value
3929+
# Repeat until I is lower then 10; in every step decrement I by 1
3930+
until [ $I -lt 10 ]; do echo "Value: $I"; I=$(($I - 1)); done
3931+
\end{bashcode}
3932+
\end{frame}
3933+
3934+
\begin{frame}[fragile]{While and until loops II}
3935+
\begin{bashcode}
3936+
# 'continue' skips to another look turn, 'break' terminates running
3937+
# of whole loop (no further turns) and skips to following commands
38623938
while ...; do # Start cycles as you need
38633939
commands...
38643940
if [condition]; then # If something happens
3865-
break; fi # End up the cycles and continue by following commands
3866-
while read TEXTLINE; do # Run cycles on text file
3941+
break # End up the cycles and continue by following commands
3942+
fi
3943+
# While loops are popular to process every line of input file
3944+
# The text file use to contain e.g. list of files to process (if for
3945+
# whatever reason 'for' loop construction is impractical)
3946+
while read TEXTLINE; do # Run cycles on every line of text file
38673947
commands... # TEXTLINE contains in each cycle one line of the file
38683948
done < text_file_to_process.txt
3869-
\end{bashcode}
3870-
\end{frame}
3871-
3872-
\begin{frame}[fragile]{While and until cycles II}
3873-
\begin{bashcode}
3874-
while :; do echo "Press CTRL+C to exit..."; done # Infinite loop
3875-
for (( ; ; )) ; do echo "Press CTRL+C to exit..."; done # Infinite loop
3949+
# Infinite loops - common when waiting for some condition to proceed
3950+
while :; do echo "Press CTRL+C to exit..."; done
3951+
for (( ; ; )) ; do echo "Press CTRL+C to exit..."; done
38763952
\end{bashcode}
38773953
\end{frame}
38783954
@@ -4870,6 +4946,7 @@ \subsection{Resources}
48704946
\item Linux tutorial \url{https://ryanstutorials.net/linuxtutorial/}
48714947
\item Getting Started with BASH \url{https://www.hypexr.org/bash_tutorial.php}
48724948
\item Bash Guide \url{https://mywiki.wooledge.org/BashGuide}
4949+
\item TutorialKart \url{https://www.tutorialkart.com/bash-shell-scripting}
48734950
\item Česky
48744951
\begin{itemize}
48754952
\item Učebnice Linuxu \url{https://www.abclinuxu.cz/ucebnice}

scripts_data/interactive6.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ echo "Size of the file is $(du -sh "$1" | cut -f 1)."
1717
echo "The file has $(wc -l "$1" | cut -d ' ' -f 1) lines."
1818

1919
echo "Making backup of the file $1..."
20-
cp "$1" "$1".bak || { echo "Error! Making backup of $1 failed!"; exit 1; }
20+
# Copy file to backup (*.bak). If it succeeds, report it, if it fails exit with error
21+
{ cp "$1" "$1".bak && echo "Backup saved as $1".bak; } || { echo "Error! Making backup of $1 failed!"; exit 1; }
2122

2223
echo "Done!"
2324

0 commit comments

Comments
 (0)