Table des matières

Bash

Substitution dans les variables

Array

Fonctions utiles

in_array

function in_array() {
    local needle=$1 el
    shift
    for el in "$@"; do
        [ "$el" = "$needle" ] && return 0
    done
    return 1
}

Utilisation :

array=(1 2 3)
in_array 1 ${array[@]} && echo IN
in_array 5 ${array[@]} && echo OUT

is_empty

function is_empty() {
        [ $# -gt 0 ] && return 1
        return 0
}

Utilisation :

array=(1 2 3)
is_empty $array && echo empty
! is_empty $array && echo not empty

array_filter

function array_filter() {
    local values=() x=0 v
    for v in "$@"; do
        if [[ "$v" == "--" ]]; then
            x=1
        elif [[ $x -eq 0 ]]; then
            values+=( "$v" )
        else
            mapfile -t values < <( printf '%s\n' "${values[@]}" | grep -Ev "^${v}$" )
        fi
    done
    printf '%s\n' "${values[@]}"
}

Utilisation :

a=(a b c d e)
array_filter ${a[@]} -- c e
# Output:
# a
# b
# d

array_intersect

function array_intersect() {
    local result_var=$1
    declare -ga "$result_var=()"
    shift
    local array1=()
    local array2=()
    local switch_to_array2=0
 
    for v in "$@"; do
        if [ "$v" == "--" ]; then
            switch_to_array2=1
        elif [ $switch_to_array2 -eq 0 ]; then
            array2+=( "$v" )
        else
            array1+=( "$v" )
        fi
    done
 
    for i in "${array1[@]}"; do
        for j in "${array2[@]}"; do
              if [[ $i == $j ]]; then
                    declare -ga "$result_var+=( \"$i\" )"
                    break
              fi
        done
    done
}

Utilisation :

a=(a b c d e)
b=(c d)
array_intersect c "${a[@]}" -- "${b[@]}"
echo "${c[@]}"
# Result:
c d

implode

function implode() {
  local d=${1-} f=${2-}
  if shift 2; then
    printf %s "$f" "${@/#/$d}"
  fi
}

Utilisation :

array=(1 2 3)
echo $( implode "," "${array[@]}" )
# Output: 1,2,3
echo -e "- $( implode "\n- " "${array[@]}" )"
# Output:
# - 1
# - 2
# - 3

explode

function explode() {
    local output_var=$1 seperator=$2
    declare -ga "$output_var=()"
    mapfile -t "$output_var" < <( tr "$seperator" '\n' <<< "${@:3}" | grep -v '^$' )
}

Utilisation :

explode myarray " " "1 2 3" "4 5"
declare -p myarray
# Output:
# declare -a myarray=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6")
 
explode myarray "\n" "
1
2
3"
declare -p myarray
# Output:
# declare -a myarray=([0]="1" [1]="2" [2]="3")

format_duration

function format_duration {
    local t=$1
    local d=$((t/60/60/24))
    local h=$((t/60/60%24))
    local m=$((t/60%60))
    local s=$((t%60))
    [[ $d -gt 0 ]] && printf '%d days and ' $d
    printf '%02d:%02d:%02d' $h $m $s
}

dump_ass_array

function dump_ass_array() {
        declare -n aarr="$1"
        echo "\"$1\" = {"
        for key in "${!aarr[@]}"; do
                printf '  "%s" => "%s"\n' "$key" "${aarr[$key]}"
        done
        echo "}"
}

Utilisation :

declare -A myarray
myarray[a]="b"
myarray[c]="d"
dump_ass_array myarray

var_dump

declare -p variableName

Gestion des paramètres

#!/bin/bash
 
DEBUG=0
BIN_PATH="/bin/binary"
EXTRA_ARGS=()
 
function usage() {
        error="$1"
        [ -n "$error" ] && echo "$error"
        cat << EOF
Usage : $(basename $0) [-d] [-b /path/to/binary]
        -b [path]               Binary path (default: $BIN_PATH)
        -d                      Debug mode
        -X                      Enable bash tracing (=set -x)
        -h                      Show this message
EOF
        [ -n "$error" ] && exit 1
        exit 0
}
 
function debug() {
        [ $DEBUG -eq 1 ] && >&2 echo -e "$( date '+%Y-%m-%d %H:%M:%S' ) - $@"
}
 
idx=1
while [ $idx -le $# ]
do
        OPT=${!idx}
        case $OPT in
                -d)
                        DEBUG=1
                ;;
                -h)
                        usage
                ;;
                -b)
                        ((idx++))
                        BIN_PATH=${!idx}
                        if [ ! -x "$BIN_PATH" ]
                        then
                                usage "Invalid binary path ($BIN_PATH)"
                        fi
                ;;
                -X)
                        set -x
                ;;
                *)
                        EXTRA_ARGS+=( $OPT )
                ;;
        esac
        ((idx++))
done
 
debug "Extra args: ${EXTRA_ARGS[@]}"

Barre de progression

declare -A PBARS
declare PBID
 
# Create a progress bar
# Arguments:
# - the progress bar title (default: Progress)
# - total count (default: 100)
# - bar size (default: use all the width of the terminal with a minimum of 5 caracters)
# - the name of the variable use to store the progress bar ID (default: PBID)
function pbar_create() {
    # Define the name of the variable that will store the progress bar ID
    local pbar_id_var=${4:-}; [[ -z "$pbar_id_var" ]] && pbar_id_var=PBID
    local -n id="$pbar_id_var"
    # Generate the progress bar ID
    id="$( tr -dc A-Za-z0-9 </dev/urandom | head -c 3 )"
    # Initialize progress bar information
    PBARS["${id}_START_TIME"]="$( date +%s )"
    [ -n "$1" ] && PBARS["${id}_TITLE"]="$1" || PBARS["${id}_TITLE"]="Progress"
    [ -n "$2" ] && PBARS["${id}_TOTAL"]="$2" || PBARS["${id}_TOTAL"]=100
    [ -n "$3" ] && PBARS["${id}_SIZE"]="$3" || PBARS["${id}_SIZE"]=0
    PBARS["${id}_CURRENT"]=0
    PBARS["${id}_LAST_UPDATE"]=0
    PBARS["${id}_END_TIME"]=0
    # Draw the progress bar for a first time
    pbar_draw "$id"
}
 
# Finish a progress bar
# Arguments:
# - the ID of the progress bar (default: $PBID)
function pbar_finish() {
    local id=${1:-}; [[ -z "$id" ]] && id=$PBID
 
    # Force a last update of the progess bar
    PBARS["${id}_END_TIME"]="$( date +%s )"
    pbar_draw "$@"
 
    # Unset progress bar info
    unset 'PBARS[${id}_START_TIME]'
    unset 'PBARS[${id}_TITLE]'
    unset 'PBARS[${id}_TOTAL]'
    unset 'PBARS[${id}_CURRENT]'
    unset 'PBARS[${id}_LAST_UPDATE]'
    unset 'PBARS[${id}_END_TIME]'
    echo
}
 
# Draw the progress bar
# Arguments:
# - the ID of the progress bar (default: $PBID)
# - extra message to display in the progress bar (before the ETA, optional)
# - all extra arguments will be use to compute the extra message using printf
function pbar_draw() {
    local id=${1:-}; [[ -z "$id" ]] && id=$PBID
 
    # Compute extra message
    local extra=${2:-}
    # shellcheck disable=SC2059
    [[ -n "$extra" ]] && [[ $# -gt 2 ]] && extra=$( printf "$extra" "${@:3}" )
 
    # Only update progress bar one time by second
    local now; now=$(date +%s)
    [[ "${PBARS[${id}_END_TIME]}" -eq 0 ]] && [[ $now -eq ${PBARS[${id}_LAST_UPDATE]} ]] && return
 
    # Compute progress percentage
    local perc
    (( perc=${PBARS[${id}_CURRENT]}*100/${PBARS[${id}_TOTAL]} ))
 
    # Compute line without the progress bar
    local line line_items line_pad size line_length term_height term_width bar_done bar_pad
    line_items=(
        "${PBARS[${id}_TITLE]}"
        "[]"
        "${PBARS[${id}_CURRENT]}/${PBARS[${id}_TOTAL]} (${perc}%)"
    )
    [ -n "$extra" ] && line_items+=( "- $extra" )
 
    # Add ETA (or total duration if finish)
    if [[ "${PBARS[${id}_END_TIME]}" -eq 0 ]]; then
        # Compute duration, total duration, ETA & speed
        local duration total_duration speed eta
        (( duration=now-${PBARS[${id}_START_TIME]} ))
        if [[ "${PBARS[${id}_CURRENT]}" -gt 0 ]]; then
            (( total_duration=duration*${PBARS[${id}_TOTAL]}/${PBARS[${id}_CURRENT]} ))
            speed=$( bc <<< "scale=1; ${PBARS[${id}_CURRENT]}/$duration" )
        else
            total_duration=0
            speed="?"
        fi
        (( eta=total_duration-duration ))
 
        line_items+=(
            "- ETA: $(format_duration $eta)"
            "- $( printf "(%s / %s, %s/s)" "$(format_duration $duration)" "$(format_duration $total_duration)" "$speed" )"
        )
    else
        local total_duration
        (( total_duration=${PBARS[${id}_END_TIME]}-${PBARS[${id}_START_TIME]} ))
        line_items+=( "- Total duration: $(format_duration $total_duration)" )
    fi
 
    # Compute progress bar length (if not configured)
    # shellcheck disable=SC2034
    read -r term_height term_width < <(stty size)
    size=${PBARS[${id}_SIZE]}
    if [[ "$size" -eq 0 ]]; then
        line_length=$( wc -c <<< "${line_items[*]}" )
        (( size=term_width-line_length ))
        [[ $size -lt 5 ]] && size=5
    fi
 
    # Set progress bar text
    (( bar_done=perc*size/100 ))
    (( bar_pad=size-bar_done ))
    line_items[1]="[$(printf "%${bar_done}s"|tr ' ' '#')$(printf "%${bar_pad}s"|tr ' ' '-')]"
 
    # Add line padding (if need)
    (( line_pad=term_width-${#line_items} ))
    [[ $line_pad -gt 0 ]] && line_items+=( "$(printf "%${line_pad}s")" )
 
    # Compute & display line (strip the terminal width)
    line="${line_items[*]}"
    echo -en "\r${line:0:$term_width}"
 
    # Update last progress bar update time
    PBARS[${id}_LAST_UPDATE]=$now
}
 
# Increment the progress bar
# Arguments:
# - the ID of the progress bar (default: $PBID)
# - extra message to display in the progress bar (before the ETA, optional)
# - all extra arguments will be use to compute the extra message using printf
function pbar_increment() {
    local id=${1:-}; [[ -z "$id" ]] && id=$PBID
    # Increment the progress bar state
    ((PBARS[${id}_CURRENT]++))
    # Draw the progress bar
    pbar_draw "$@"
}

Utilisation :

pbar_create "Test" 20
for i in $( seq 1 20 ); do
    pbar_increment
    sleep 0.1
done
pbar_finish

Ajout d'info avant l'ETA :

pbar_create "Test" 20
for i in $( seq 1 20 ); do
    pbar_increment "" "%d iteration(s) - %d found(s)" $i $(( i/2 ))
    sleep 0.1
done
pbar_finish "" "%d iteration(s) - %d found(s)" $i $(( i/2 ))
La fonction format_duration est nécessaire.