bash & structured programming
InstallFest 2018

Lukáš Bařinka
Barinka does not simply be in time with his talk.
Length of spaghetti code is too damn high.
Clarity, Quality, Development Time? - Structured Programming.
Brace Yourself, Structured Programming is Comming.
Blocks of commands.
(list) # in sub-shell

{ list; } # in current shell
						
Redirection of block of commands.
{ read first; read second; } <file

tar cf - dir | (cd /tmp; tar xf -)
						
Shut up and create a function.
Different syntaxes for function creation.
List of function names / declarations.
Simple functions.
error() { echo "$0[error]: $@"; exit 1; } >&2
warn() { echo "$0[warn]: $@"; } >&2
info() { ((VERBOSE)) && echo "$0[info]: $@"; } >&2
Quoting function.
debug() {
    local var quoted
    for var do
        quoted=${!var//\'/\'\\\'\'}
        info "$var='$quoted'"
    done }
Function Termination.
  • return [n]
  • exit [n]
  • f() {
        trap date RETURN
        sleep 5
    }
What if I told you, for almost every purpose aliases are superseded by shell functions.
Functions are more powerful, but aliases are simplier to use.
alias ls='ls --color=auto'
alias ll='ls -l'


ls() { command ls --color=auto "$@"; }
ll() { ls -l "$@"; }
If you can redefine built-in command, that would be great.
cd() {
    echo "OLD: $OLDPWD"
    builtin cd "$@"
    E=$?
    echo "NEW: $PWD"
    return $E
}
You don't have to reset you variables if you run separate script.
I'll create my own context, with variables and parameters.
f() {
    local i
    for i
    do
        echo "$0:$i
    done
}
f "$@"
get_id() (
    set -o pipefail
    getent passwd "$1" \
    | cut -d: -f3
)
uid=$(get_id user)
found=$?
eval for call by reference, well that escalated quickly.
list() { eval '
    for i in "${!'$1'[@]}"
    do
        printf -- "[%s] -> %s\n" \
            "$i" "${'$1'["$i"]}"
    done'
}
declare -A a=([1st]=foo [2nd]=bar)
list a
Look at me, I'm your variable now.
array_cp() {
    declare -gA "$2=()"
    declare -n from=$1 to=$2
    local i
    for i in "${!from[@]}"
    do
        to[$i]=${from[$i]}
    done
}
declare -A a=([1st]=foo [2nd]=bar)
array_cp a b
Nameref (declare -n), You the real MVP.
inc() {
    declare -n var=$1 R=BASH_REMATCH
    [[ $var =~ (.*)_([0-9]+)\.(.*) ]] \
    && printf -- '%s_%03d.%s' \
        "${R[1]}" \
        $((10#${R[2]}+1)) \
        "${R[3]}"
}
file=/var/tmp/backup_data_021.tgz
inc file
I'll create my own context, with variables and parameters.
# DEBUG / RETURN
set -o functrace
declare -tf f_name

# ERR
set -o errtrace

FUNCNEST=0
export -f f_name
So, you're telling me you understand everything and have no questions?
Not upvoting this talk, that's paddlin'.