guest@6admin.io:~$

Bash : Personnalisation du fichier .bashrc

Sur nos systèmes préférés, la configuration du shell bash en mode interactif, de son prompt et de sa coloration, se fait dans les fichiers:

  • /etc/profile
  • /etc/bash.bashrc
  • /root/.bashrc
  • /home/user/.bashrc

Ici nous ne toucherons qu’aux trois derniers. On commence par la couleur du nom d’utilisateur dans le prompt, vert pour les utilisateurs, rouge pour le compte root, parce qu’un prompt root qui ne se signale pas est un prompt root dans lequel on finit par taper une bêtise:

# Set color of username in prompt
if [ "`id -u`" -eq 0 ];then prompt_color='\[\033[01;31m\]';else prompt_color='\[\033[01;32m\]';fi

Voici la liste des codes couleurs: Noir 00;30, Rouge 00;31, Vert 00;32, Marron 00;33, Bleu 00;34, Violet 00;35, Cyan 00;36, Gris clair 00;37, Gris 01;30, Rouge rosé 01;31, Vert clair 01;32, Marron clair 01;33, Bleu clair 01;34, Violet clair 01;35, Cyan clair 01;36, Blanc 01;37.

Le prompt lui-même donne l’heure (\t) plutôt que le répertoire courant (\w), qui prenait énormément de place pour rien et il préfixe chaque ligne du code de retour de la commande précédente, ce qui est la petite touche dont je ne me passe plus.

Le .bashrc, section par section

# If not running interactively, don't do anything
[ -z "$PS1" ] && return 0;

export PATH=/sbin:/usr/sbin:$PATH;
export EDITOR=vi
export PAGER=most

# Gestion de l'historique, horodaté et en couleur
CYAN=$(echo -e '\e[0;36m')
NORMAL=$(echo -e '\e[0m')
export HISTCONTROL=ignoredups;                  # pas de doublons
export HISTTIMEFORMAT="${CYAN}[ %d/%m/%Y %H:%M:%S ]${NORMAL} "
export HISTFILESIZE=50000
export HISTSIZE=50000
PROMPT_COMMAND="history -a";                    # historique écrit après chaque commande

shopt -s checkwinsize;                          # recalcule LINES/COLUMNS après chaque commande
[ -x /usr/bin/lesspipe ] && eval "$(lesspipe)";
if [ "$TERM" != "dumb" ]; then eval "`dircolors -b`"; fi

Les lignes qui valent de l’or dans ce bloc: HISTTIMEFORMAT, l’historique horodaté, ici colorisé en cyan, la seule façon de savoir qui a tapé quoi et quand sur un serveur partagé; PROMPT_COMMAND="history -a", qui écrit l’historique après chaque commande et non à la fermeture, de sorte qu’un terminal qui crashe ne perd plus rien; et checkwinsize, pour que les éditeurs plein écran cessent de se dessiner en 80 colonnes dans un terminal qui en fait 200. Le export PAGER=most est un goût d’époque, most colorise les pages de man et garde plusieurs fichiers ouverts, là où less restait sobre.

Vient ensuite le prompt en couleur, le titre de fenêtre xterm et surtout le PROMPT_COMMAND qui affiche le code de retour quand il n’est pas nul:

# cd vers une variable, et correction des fautes de frappe sur cd
shopt -s cdable_vars;
shopt -s cdspell;

# affiche le code de retour de la commande précédente s'il n'est pas nul
export PROMPT_COMMAND='( x=$? ; let x!=0 && echo "sHell returned : $x" )';

Un aveu au passage: PROMPT_COMMAND est redéfini plusieurs fois dans ce fichier et c’est le dernier qui l’emporte. On empile l’historique, le titre xterm et le code de retour comme si les trois cohabitaient, alors qu’il faudrait les fondre dans une seule fonction. Je le laisse tel quel parce que c’est exactement la maladresse qu’on traîne pendant des années sans la voir.

Et pour finir, l’accueil, parce qu’un shell qui ne dit pas bonjour est un shell triste:

echo -e "Hello `whoami` !\tWe're in `date '+%Y:%m:%d'`\nI am Locutus of Borg. Resistance is futile."
who

Les alias, la section qui ne meurt jamais

D’abord les gardes-fous et la famille ls, qui ne changent pas d’une décennie à l’autre:

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias ls='ls --color=auto'
alias ll='ls -l'
alias la='ls -A'
alias l='ls -CF'
alias d='ls -d */'
alias grep='grep --color=auto'
alias vi='vim'

Ensuite le :q, le réflexe de celui qui vit dans vim et oublie qu’il est dans un shell. Détecter si le shell courant descend d’un vim, en 2009, se faisait sans variable dédiée, à coups de ps et d’awk:

[[ $(ps -ef | awk "\$2 == $(ps -ef | awk "\$2 == $$ {print \$3}") {print \$8}" | grep vi) ]] \
    && alias :q='exit' || alias :q='echo "Not in vi{m} !"';

Dans un :sh lancé depuis vim, :q sort; dans un shell normal, il répond poliment “Not in vi{m} !”. C’est tordu, ça lance deux ps, mais ça marche et à l’époque on trouvait ça très malin.

Enfin la collection dont je ne suis pas peu fier, les alias en namespace, une façon de se faire une pseudo-complétion par préfixe bien avant que la complétion bash ne soit partout:

alias dir.change='cd'
alias dir.new='mkdir'
alias dir.list='ls'
alias file.move='mv'
alias file.grep='grep'
alias file.stat='stat'
alias alias.edit='$EDITOR ~/.bash_aliases'
alias alias.reload='. ~/.bash_aliases'
alias apt.install='apt-get install'
alias apt.search='apt-cache search'
alias apt.moo='apt-get moo'

L’idée est qu’on tape dir. puis Tab et le shell propose tout ce qui touche aux répertoires, file. pour les fichiers, apt. pour la gestion de paquets. C’est de la programmation orientée objet du pauvre, appliquée à des alias et c’est resté l’une de mes marottes. On y trouve aussi quelques private jokes, un nan qui crie “I am not a number !” et le apt.moo qui n’a jamais servi à rien d’utile.