Scripts amusants en bash

Retour à la page Systèmes

Motivation

Le shell bash est celui que Linux propose par défaut. Mais son manuel (voir man bash et info bash) peut intimider les novices qui voudraient écrire des scripts.

Cette page expose quelques usages du shell, sans critère de technicité mais qui, m’ayant plu ou amusé, peuvent rendre l’apprentissage ou les révisions plus ludiques.

Permissions

Pour comprendre pourquoi un utilisateur ne peut pas accéder à un fichier, il faut détailler les étapes de l’accès.

Pour cela, ou bien on connaît la commande namei (package util-linux sous Fedora), ou bien on écrit une fonction du shell qui fait quelque-chose de semblable.

#Détailler les permissions de chaque étape d’un chemin
function follow () {
  fichier=$( echo $1 | sed 's@//*@/@g'); # Cas des //
  (
    set -o physical
    [[ $fichier =~ ^/ ]] && fichier=${fichier/\//} && cd /; # Cas des chemins absolus
    IFS="/";            # Pour séparer selon les /
    for f in $fichier;  # ... sans être gêné par les noms avec espaces
    do
      ls --color=auto -ld "$f"; # afficher chaque étape
      [ -d "$f" ] && cd "$f";   # suivre les (liens vers) répertoires
    done
  )
}

Les points saillants:

  • $( ... ) : utilise la sortie standard d’une commande comme si on avait lu une variable;
  • set -o physical: pour que cd interprète .. selon le chemin physique, pas les liens symboliques;
  • "..." : permet au shell d’interpréter les variables en $ à l’intérieur;
  • ( ... ) : un sous-shell protège l’appelant des effets collatéraux des cd et autres affectations de variables;
  • sed : pour traiter des / la commande s est plus lisible avec un autre délimiteur (ici: @);
  • [[ ... ]] : permet l’opérateur =~ qui teste une expression régulière comme en Perl, contrairement à [ ... ];
  • IFS=... : changer le délimiteur de champs est plus fiable que remplacer / par des espaces (car les noms de fichiers peuvent en comporter);
  • [ -d ... ] : détecte aussi bien un répertoire qu’un lien symbolique vers un répertoire;

Chatroom

Ce script singe un service de conversation en ligne, en jouant sur les pipes et redirections;

Jouet acceptable au sein d’un groupe Unix donné, il n’y empêche aucune usurpation.

#Mini-programme de chat en bash, au sein d’un groupe Unix donné.
#
#  chatcreate %nom% [ %groupe% ] : nouvelle conversation %nom% réservée au groupe.
#  chatspeak  %nom% [ %pseudo% ] : rediriger l’entrée standard vers la conversation.
#  chatlisten %nom%              : affiche les nouvelles interventions
#  chatclose  %nom%              : efface le fichier de conversation

function chatcreate () {
local fichier=/tmp/$1.txt
local groupe=$(groups | cut -f 1 -d ' ')
[ $# -ge 2 ] && groupe="$2"
[ -r $fichier ] && echo "[ERR] Conversation '$1' déjà créée" && return
touch $fichier
[ -n "$groupe" ] && chgrp "$groupe" $fichier
chmod g+rw,o-rwx $fichier
echo "[OPEN] $(date) : $USER crée conversation '$1' pour le groupe $groupe" >> $fichier
}

function chatspeak () {
local fichier=/tmp/$1.txt
local pseudo=$USER
[ $# -ge 2 ] && pseudo="$2"
[ ! -r $fichier ] && echo "[ERR] Conversation '$1' inexistante" && return
echo "[SPEAK] $(date) : $USER en tant que $pseudo" >> $fichier
sed -u "s/^/$pseudo> /1" >> $fichier
echo "[SPEAK] $(date) : $pseudo part" >> $fichier
}

function chatlisten () {
local fichier=/tmp/$1.txt
[ ! -r $fichier ] && echo "[ERR] Conversation '$1' inexistante" && return
echo "[LISTEN] $(date) : $USER tend l’oreille" >> $fichier
tail -f $fichier | sed -u "s/^/\x07/1"
echo "[LISTEN] $(date) : $USER part" >> $fichier
}

function chatclose () {
local fichier=/tmp/$1.txt
[ ! -r $fichier ] && echo "[ERR] Conversation '$1' inexistante" && return
echo "[CLOSE] $(date) : $USER ferme '$1'" >> $fichier
sleep 2
rm -f $fichier
}

Utilisation:

    1. Créer une discussion: «chatcreate madiscussion»;
    1. Ecouter ce qui se dit dessus: «chatlisten madiscussion»;
    1. Parler dessus: «chatspeak madiscussion monpseudo»: chaque ligne écrite sera affichée dans (ii); Taper C-d pour sortir;
    1. Fermer la discussion: «chatclose madiscussion»;

Points intéressants:

  • groupe=$(groups | cut -f 1 -d ' '): capture la sortie de commande pour la mettre dans $groupe
  • sed -u "s/^/$pseudo> " : la petite ligne qui fait presque tout
    • sans fichier en argument, prend l’entrée standard;
    • Le -u écrit immédiatement le résultat dans le fichier d’échange
    • insérer le pseudo au début de chaque ligne ^ identifie les interlocuteurs.
  • tail -f : suit le moindre ajout au fichier de conversation.
  • A && B : éxécution conditionnelle concise: utilise le statut de A pour exécuter B en cas de succès.