Bash Snippets

Hier werden nützliche Befehle und Code Snippets gesammelt.

Flags und Argumente in Bash Skripten

Ein Snippet um Flags wie -h oder --help und Argumente abzufragen. Alle Argumente mit einem Dash werden verarbeitet und aus dem Argumenten entfernt. Übrig bleiben alle anderen Argumente, wie z.B. Dateinamen, in der richtigen Reihenfolge.

# handle dashed arguments and remove them from $@
POSITIONAL_ARGS=()
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            usage
            exit 0;;
        -o|--output)
            if [ -z "$2" ]; then
                echo "Error: no output filename given" >&2 ; exit 2
            fi
            output_file="$2"
            shift;shift;; # shift once for the name and once for the value
        -*)
            echo "Unknown option $1"; exit 1;;
        *)
            POSITIONAL_ARGS+=("$1") # save positional arg
            shift ;;
    esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional arguments

Parameter Expansion

Die verschiedenen Möglichkeiten Variablen einzusetzen, zu verändern oder zu ersetzen. Mehr Infos im Bash Manual.

${parameter:−word} # return parameter or word if parameter is null
${parameter:=word} # set parameter to word of parameter is null (does not work with $1)
${parameter:?word} # write word to stderr if parameter is null (exits if noninteractive)

${parameter:offset:length} # return substring

${parameter#word}  # remove shortest match from beginning (remove prefix)
${parameter##word} # remove longest match from beginning (remove all slashes)
${parameter%word}  # remove shortest match from end (remove file extension)
${parameter%%word} # remove longest match from end (remove everything after first occurrence)

${parameter/pattern/string}  # replace first pattern with string
${parameter//pattern/string} # replace all patterns with string
${parameter/#pattern/string} # replace pattern at beginning
${parameter/%pattern/string} # replace pattern at end
${parameter/pattern}         # remove pattern

Timer ohne Drift

Ein einfacher Timer mit einem sleep 60 der jede Minute eine Funktion ausführen soll würde mit der Zeit abdriften, da die Anweisungen zwischen den sleep und auch der Prozessaufruf von sleep selbst auch Zeit benötigen.
Für ein Timer ohne Drift muss von der Systemzeit ausgegangen werden. Die Bash Variable EPOCHSECONDS enthält die Sekunden seit dem 1.1.1970. Mit 60 - ($EPOCHREALTIME % 60) können die Sekunden bis zum nächsten Aufruf berechnet werden. Zusammen mit EPOCHREALTIME , die die Epoch Zeit in Mikrosekunden angibt, kann so die genaue Zeit für einen sleep-Aufruf bestimmt werden.

timer_seconds=60
timer_micsecs=${timer_seconds}000000

# calculate start time
now_seconds=$(( ${EPOCHSECONDS} % $timer_seconds )) # in seconds
wait_seconds=$(( $timer_seconds - $now_seconds )) # in seconds
begin=$(( ${EPOCHSECONDS} + $wait_seconds ))000000 # in µs
echo "starting timer at $(date -d @${begin%000000})" >&2

count=0
while true; do
    next=$((begin+count*$timer_micsecs)) # in µs
    now=${EPOCHREALTIME/[.,]} # in µs
    diff=$((next-now)) # in µs
    diff_real=$(echo "$diff / 1000000" | bc -l) # in s
    if test $diff -ge 0; then
        sleep $diff_real
    fi

    if [[ $diff -lt -$timer_micsecs ]]; then
        # we missed at least two events - execute immediately, but only once
        # calculate new count to skip other events
        (( count = (now-begin) / timer_micsecs ))
    fi

    $@ # execute supplied command with all arguments
    (( count++ ))
done