HowtoBash

Astuces Bash

function hello { echo Hello; echo $1; }

Raccourcis

Ctrl+d : quitte le terminal courant / supprime le caractère sous le curseur si la ligne n'est pas vide
Ctrl+h : supprime le caractère avant curseur
Ctrl+a : aller en début de ligne
Ctrl+e : aller en fin de ligne
Ctrl+k : effacer (coupe) du curseur à la fin de la ligne
Ctrl+u : effacer (coupe) du début de ligne au curseur
Ctrl+y : colle la partie précédemment coupée
Ctrl+r : rechercher parmi l'historique
Ctrl+s : freeze l'affichage de l'output
Ctrl+q : défreeze l'affichage de l'affichage de l'output
Ctrl+t : inverse les caractères autour du curseur
Ctrl+c : annule la ligne en cours et passer sur une nouvelle ligne vide
Ctrl+z : suspend la tâche en cours
ctrl+l : efface/rafraichit l'écran
ctrl+w : efface le mot qui précède le curseur jusqu'au séparateur de mot (une espace en général)
ctrl+x ctrl+e : édite la ligne dans $VISUAL ou $EDITOR si non définie
alt+effacer : Efface un mot à gauche du curseur
alt+d : Efface un mot à droite du curseur
alt+t : Inverse les deux mots à gauche du curseur

Traduire les espaces insécables en espaces

Dans le fichier ~/.inputrc :

# espace insécables et espaces fine insécables -> espace
"\xc2\xa0": " "
"\xe2\x80\xaf": " "

Rendre la complétion insensible à la casse

Dans le fichier ~/.inputrc :

set completion-ignore-case On

Script

Variables

Environnement

$PPID
$BASHPID
$!
$$
$PIPESTATUS

Initialiser

var=

En readonly <=> constante

readonly var="pas touche"

Protéger

[ -n "${var}" ] && true
#et surtout pas
[ -n $var ] && false
#car si var est vide, l'expression sera équivalente à [ -n ] => erreur de syntaxe
echo $var "joue au ${foo}t"
#et surtout pas
echo $var "joue au $foot"
#car sinon c'est bien le contenu de la variable $foot qui sera remplacé et non $foo

Conditions

true && echo "C'est toujours vrai"
false || echo "Ça sera toujours vraiment faux"
[ $? -eq 0 ] && { echo "tâche"; sleep 1; echo -e "\e'a plus!!" }

test

Boucles

«Liste» :

for i in "valeur1 valeur2prisavec1" valeur3 valeurn; do echo $i; done

«Incrémentale» :

for ((i=0; i<10; i++)); do echo $i; done
#ou
for i in $(seq 1 10); do echo $i; done
#ou
for i in {1..10}; do echo $i; done

«Conditionnelle» :

file=; while [ -f "$file" ]; do sleep 10; touch $file; done

Processus

( echo "Je suis un fils du script principal, mais on attend que je meurs."; exit 0; )
( echo "Libreee !"; yes "Libre je dis !"; ) &
sleep 10 && kill $! #on tue le dernier fils créé

Note: Dans une boucle, ne pas créer de process via ( ) sinon un usage comme break ou continue, … ne fonctionnera pas :

while :
do
   : && { echo "Stop !"; break; }
done
# ( echo "Stop !"; break; ) dans cet exemple ne quittera pas la boucle -car break dans un process fils-, et donc restera infini

Astuces [concrètes]

Récupérer tout stdin dans une variable

Complet

var="$(cat <&0)"
#ou
var="$(cat -)"

Ligne par ligne

var=
while read ligne
do
  var="${var}${ligne}"
done <&0

autrement :

cat <&0 | (while read ligne; do ... done)

Écrire un fichier temporaire [unique]

nomfichier=$(mktemp)
#pour un répertoire
nomrep=$(mktemp -d)

Note: La création d’un fichier temporaire n’implique pas côté système une suppression automatique de ce dernier.

Gérer arguments donné au script

Exemple :

#!/bin/bash

DELAY=
NUMARG=0
FICHIER=

while :
do

  [ $# == 0 ] && break

  OPTIND=1
  while getopts ":d:n" opt "$@"
  do
        case "$opt" in
          d) DELAY=$OPTARG; (( NUMARG+=1 )) ;;
          n) NOMAIL=true;   (( NUMARG+=1 )) ;;
          :) >&2 echo "Manque un argument avec cette option." ;;
          *) >&2 echo "$0 [-d DELAY] [-n] [FILE]"; exit 1 ;;
        esac
  done

  shift $((OPTIND-1))

  #Savoir si après toutes les options, une chaîne de caractère présente pour FILE
  [ -z "$1" ] && break

  #FICHIER=$1 si FICHIER non initialisé
  [ -z "$FICHIER" ] && FICHIER=$1 || { >&2 echo "Fichier en trop."; break; }
  shift

done

echo
echo "==RESULTAT=="

echo "ARG " $NUMARG
echo "FICHIER=" $FICHIER
echo "DELAY=" $DELAY
echo "NOMAIL=" $NOMAIL

exit 0

Permettra par exemple :

./cmd -d 2 -n monfichier
./cmd -d2 monfichier
./cmd monfichier -d 2 -n
./cmd -n monfichier -d2

Passer arguments de facon optionelle

${var:+..} peut etre utile

$ in_uid=1009 in_login=app_user adduser \
	--gecos "User $in_login" \
	--disabled-password "$in_login" \
	${in_uid:+'--uid' "$in_uid"}

Lire les commandes intégrés dans BASH

Lister fonctions

$ typeset -F
$ typeset -f #pour lister contenu de la fonction avec

Voir contenu

$ type FUNCTION 

Pour ceux «built-in», pas possible :

$ type getopts
getopts is a shell builtin

Utiliser résultat xargs en milieu de commande

$ echo coucou | xargs -i echo {} c\'est moi!

Ajouter la complétion à un script

Pré-requis :

  • La paquet bash-completion est installé.

Emplacement du script de complétion :

  • Pour tout le système : le mettre dans un fichier dédié dans le répertoire /etc/bash_completion.d/ avec la permission 644.
  • Pour un utilisateur particulier : le mettre dans le .bashrc, .profile ou autre fichier chargé à l’ouverture de la session de l’utilisateur. Si on a plusieurs fonctions de complétion, créer un .bash_completion dédié et le sourcer dans le .bashrc.

Dans le script de complétion, on appelle la commande Bash complete en lui indiquant une fonction avec l’option -F a appeler quand l’utilisateur appuie sur tab :

complete -F _MY_COMPLETION_FUNCTION PROG_NAME

Avant, on définit donc une fonction _MY_COMPLETION_FUNCTION(), qui doit remplir un array Bash COMPREPLY=() contenant les suggestions à présenter à l’utilisateur.

On peut utiliser l’array COMP_WORDS qui contient les mots déjà écrits par l’utilisateur, et le nombre de mots déjà écrits COMP_CWORD.

Quand un mot est déjà partiellement écrit, on utilise la commande Bash compgen, qui va filter les mots à présenter en fonction de celui déjà écrit.

Voici un exemple simple, qui répond action1 action2 --option après le nom du programme, option1 option2 après -o|--option et sinon la liste des fichiers dans le répertoire courant.

function _MY_COMPLETION_FUNCTION() {
    local cur_word=${COMP_WORDS[COMP_CWORD]};
    case "${prev_word}" in
        PROG_NAME)
            words="action1 action2 --option […]"
        -o|--option)
            words="option1 option2 […]"
            ;;
        *)
            words=$(ls -1)
            ;;
    esac

    local prev_word=${COMP_WORDS[COMP_CWORD-1]};
    COMPREPLY=($(compgen -W "${words}" -- "${cur_word}"))
}

complete -F _MY_COMPLETION_FUNCTION PROG_NAME

Si on ne veut pas que les options déjà présentes soient re-suggérées, on peut remplacer COMPREPLY=($(compgen -W "${words}" -- "${cur_word}")) par :

    opts=();
    for i in ${words}; do
        for j in "${COMP_WORDS[@]}"; do
            if [[ "$i" == "$j" ]]; then
                continue 2
            fi
        done
        opts+=("$i")
    done

    COMPREPLY=($(compgen -W "${opts[*]}" -- "${cur_word}"))

La complétion échappe les deux points

Lors qu’on complète un nom de fichier contenant le caractère deux points « : », Bash échappe les deux points par défaut. Exemple :

$ touch a:b
$ ls a<tab>
$ ls a\:b

Pour empêcher ce comportement : COMP_WORDBREAKS=${COMP_WORDBREAKS//:}.

Références :