Bash
Substitution dans les variables
- Chercher/remplacer :
${variable/search_pattern/replacement}
⇒ remplacer la première occurrence desearch_pattern
parreplacement
dans de contenu de la variablevariable
.- Pour chercher explicitement au début du contenu de la variable :
${parameter/#search_pattern/replacement}
- Pour chercher explicitement à la fin du contenu de la variable :
${parameter/%search_pattern/replacement}
- Pour remplacer toutes les occurences :
${parameter//search_pattern/replacement}
- Mise en majuscule (upper case) :
${variable^}
⇒ mise en majuscule du première caractère du contenu de la variablevariable
- Pour mettre tout en majuscule :
${variable^^}
- Mise en minuscule (lower case) :
${variable,}
⇒ mise en minuscule du première caractère du contenu de la variablevariable
- Pour mettre tout en minuscule :
${variable,,}
Array
- déclaration :
array=( 1 2 3 )
oudeclare -a array=( 1 2 3 )
- déclaration d'un tableau associatif :
declare -A array=( ['key1']='value1' ['key2']='value2' ['key3']='value3' )
- déclaration d'un tableau en lecture seule :
declare -Ar ro_array=( […] )
oudeclare -ar ro_array=( […] )
- ajouter un élément :
array+=( 4 )
- ajouter un élément à un tableau associatif :
array+=( ['key']='value' )
- lister tous les éléments d'un tableau (=valeur dans le cas d'un tableau associatif) :
${array[@]}
- lister toutes les clés d'un tableau associatif :
${!array[@]}
- récupérer le nombre d'élements d'un tableau :
echo ${#array[@]}
- construire un tableau à partir d'une chaîne de caractères : Cela dépend du séparateur utilisé :
- avec un retour à la ligne :
mapfile -t myarray <<< "$ALL"
- avec un espace (ou autre caractère unique et “simple) :
IFS=" " read -ra myarray <<< "$ALL"
- ajouter des valeurs à un tableau existant :
mapfile -t -O "${#myarray[@)}" myarray <<< "$ALL"
- ajouter depuis un fichier :
mapfile -t myarray < /path/to/file
- ajouter depuis la sortie d'une commande :
mapfile -t myarray < <( grep -vE '^#' /path/to/file | grep -vE '^\s*$' )
- Note : voir la fonction
explode
pour une version générique
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.