Bash Script Template Fr

25 Jan 2022 - nicolas

Après avoir un peu ramer à écrire de petits scripts bash pour me faciliter la vie, voici un template que j’utilise souvent :

#! /bin/bash

readonly SCRIPT_NAME=$(basename $0)

styleNormal="\033[0m"
styleStrong="\033[1m"
styleWarning="\033[33m"
styleSuccess="\033[32m"

# Public function template:
functionName() {
    if [ $# -lt 1 ]; then
        echo -e "${SCRIPT_NAME} ${FUNCNAME[0]} ${styleWarning}parameterName${styleNormal}"
        exit 1
    fi
}

# Display the source code of this file
howItWorks() {
    cat $0
}

# List all functions that do not begin with an underscore _
_listAvailableFunctions() {
    cat $0 | grep -E '^[a-z]+[a-zA-Z0-9_]*\(\) \{$' | sed 's#() {$##' | sort
}

if [ $# -eq 0 ]; then
    _listAvailableFunctions
    exit
fi

"$@"

L’idée est de mettre les actions que vous voulez réaliser dans des fonctions écrites en camelCase ou en snake_case, puis appeler le script suivi du nom de la fonction à exécuter.

Ce comportement est possible grâce à l’utilisation de $@ à la fin du code du script qui contient tous les paramètres passés au script. Si vous ne voulez pas que bash interprète les *, mettez $@ entre double-guillemets "$@".

Les fonctions commençant par _ ne seront pas listées du fait de [a-zA-Z0-9_]*.

La première ligne #! /bin/bash s’appelle le shebang, elle permet de choisir l’interpréteur à utiliser, ici bash.

#! /usr/bin/env bash fonctionne aussi.

Voici en pratique ce que ça donne :

gif montrant l'utilisation

et le code correspondant écrit à partir du template :

#! /usr/bin/env bash

readonly SCRIPT_NAME=$(basename $0)

styleNormal="\033[0m"
styleStrong="\033[1m"
styleWarning="\033[33m"

filterBranches() {
    if [ $# -eq 0 ]; then
        echo -e "${SCRIPT_NAME} ${FUNCNAME[0]} ${styleWarning}criteriaHere${styleNormal}"
        exit
    fi

    listBranchNames | grep "$1"
}

filterAllBranches() {
    git --no-pager branch --all | grep "$1" | sed 's#^* ##' | sed 's#  ##' | sed 's#remotes/origin/##' | sed 's/HEAD ->//g' | sort | uniq
}

switchBranch() {
    echo -e "\n${styleStrong}Switching to $1...${styleNormal}"
    git switch "$1"
}

selectBranch() {
    local hint="$*"

    echo -e "
${styleStrong}Active branch: ${styleNormal}$(showCurrentBranch)

${styleStrong}Available branches :${styleNormal}"

    local availableBranches=$(filterAllBranches "$hint")
    PS3="Which number? "

    select branchToSwitch in $availableBranches
    do
        switchBranch "$branchToSwitch"
        return
    done
}

showCurrentBranch() {
    git branch --show-current
}

showOtherBranches() {
    git branch | grep --invert-match \* | sed 's#  ##'
}

listBranchNames() {
    git branch --format='%(refname:short)'
}

# Display the source code of this file
howItWorks() {
    less $0
}

# List all functions that do not begin with an underscore _
_listAvailableFunctions() {
    cat $0 | grep -E '^[a-z]+[a-zA-Z0-9_]*\(\) \{$' | sed 's#() {$##' | sort
}

if [ $# -eq 0 ]; then
    _listAvailableFunctions
    exit
fi

"$@"

Pour permettre l’autocompletion que l’on voit dans le gif (en appuyant sur tab), j’ajoute ce genre de code dans mon fichier ~/.bashrc :

listeOptionsAutoCompletion=$(nomCommandePourObtenirLesOptions)
complete -W "${listeOptionsAutoCompletion}" -o bashdefault -o default 'nomCommande'
unset listeOptionsAutoCompletion

unset permet de supprimer la variable listeOptionsAutoCompletion afin de libérer un peu de mémoire, utile si comme moi vous avez beaucoup de scripts à auto-compléter.

Ce qui nous donne dans le cas de mon script please_git :

please_git_options=$(please_git)
complete -W "${please_git_options}" -o bashdefault -o default 'please_git'
unset please_git_options

Enjoy!