Notes d’apprentissage§
Un script bash automatise une suite de commandes. L’enjeu à l’examen n’est pas d’écrire du code élégant, mais du code robuste : qui échoue proprement, vérifie ses entrées, et journalise.
Modèle mental : un script est un programme avec entrées, traitement, sorties.
arguments ($1, $2...) ──► vérifications ──► traitement ──► code de sortie + logs
(garde-fous) (exit 0/1)
Le squelette défensif à connaître par cœur :
#!/usr/bin/env bash
set -euo pipefail # la ligne qui sauve
set -e: s’arrêter à la première commande qui échoue.set -u: erreur si une variable non définie est utilisée (attrape les fautes de frappe).set -o pipefail: un pipe échoue si n’importe quel maillon échoue (sinon seul le dernier compte).
Sans ces options, un script continue malgré les erreurs et peut faire des dégâts silencieux. C’est la première chose qu’un correcteur regarde.
Vérifier les arguments (garde-fous + code de sortie) :
[[ $# -lt 1 ]] && { echo "Usage: $0 <dir>" >&2; exit 1; } # argument manquant
[[ ! -d "$1" ]] && exit 1 # pas un dossier
Le code de sortie est le contrat du script : 0 = succès, ≠0 = échec. C’est ce que cron, systemd ou un autre script testeront.
Le quoting, source n°1 de bugs. Toujours "$var" entre guillemets : un chemin avec espace non quoté est découpé en plusieurs arguments. Réflexe systématique.
Variables spéciales utiles : $# (nb d’arguments), $@ (tous, quotés), $? (dernier code de sortie), $$ (PID courant), $0 (nom du script).
Manipulations de chaînes (sans appeler de commande externe) :
"${file##*/}" # basename
"${file%/*}" # dirname
"${file%.txt}" # retirer un suffixe
"${var:-defaut}" # valeur par défaut si vide
Pièges :
- Oublier les guillemets autour des variables = bugs avec espaces et globbing.
[ ](POSIX) vs[[ ]](bash) : préférer[[ ]], plus sûr (pas de découpage de mots).- Un script doit renvoyer un code de sortie cohérent, surtout s’il est appelé par cron (voir q02-cronjobs) ou systemd.
trap '...' EXITgarantit le nettoyage (fichiers temporaires) même en cas d’erreur.
Énoncé§
Solve this question on: terminal
Write a bash script /opt/course/27/backup.sh that:
- Takes one argument: the directory to back up.
- Creates a
tar.gzarchive named<basename>-YYYYMMDD.tar.gzunder/var/backups/. - Exits with code
1if the argument is missing or the directory does not exist. - Logs the result (success / failure) to
/var/log/backup.logwith a timestamp.
Make the script executable. Schedule it to run daily at 02:30 via cron for /etc/.
Solution§
sudo tee /opt/course/27/backup.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
LOG=/var/log/backup.log
DEST=/var/backups
log() {
echo "$(date '+%F %T') $*" >> "$LOG"
}
if [[ $# -lt 1 ]]; then
log "ERROR missing argument"
echo "Usage: $0 <directory>" >&2
exit 1
fi
SRC=$1
if [[ ! -d "$SRC" ]]; then
log "ERROR $SRC is not a directory"
exit 1
fi
NAME=$(basename "$SRC")
DATE=$(date +%Y%m%d)
ARCHIVE="$DEST/${NAME}-${DATE}.tar.gz"
mkdir -p "$DEST"
if tar -czf "$ARCHIVE" -C "$(dirname "$SRC")" "$NAME"; then
log "OK $ARCHIVE"
else
log "FAIL $ARCHIVE"
exit 1
fi
EOF
sudo chmod +x /opt/course/27/backup.sh
Add the cron entry:
echo '30 2 * * * root /opt/course/27/backup.sh /etc' | sudo tee /etc/cron.d/backup-etc
Bash essentials worth recognising§
Shebang + safe flags
#!/usr/bin/env bash
set -e # exit on first error
set -u # error on unset variable
set -o pipefail # fail if any pipe stage fails
set -x # trace (debug)
Variables and quoting
name="alice"
echo "$name" # expand
echo '$name' # literal
echo "${name^^}" # uppercase
echo "${file%.txt}" # strip suffix
echo "${file##*/}" # basename
echo "${file%/*}" # dirname
echo "${var:-default}" # default if unset
echo "${#name}" # length
Conditionals
if [[ -f $file ]]; then ... fi # file exists
if [[ -d $dir ]]; then ... fi # directory
if [[ -z $var ]]; then ... fi # empty
if [[ $a == $b ]]; then ... fi
if [[ $n -gt 10 ]]; then ... fi
[[ -r $f && -w $f ]] && echo "rw"
Loops
for f in *.log; do echo "$f"; done
for i in {1..10}; do echo $i; done
while read -r line; do echo "$line"; done < file.txt
until [[ -e /tmp/ready ]]; do sleep 1; done
Functions and exit codes
greet() {
local name=$1
echo "Hello $name"
return 0
}
greet "Bob"
echo "last exit: $?"
Arguments
| Token | Meaning |
|---|---|
$0 | script name |
$1…$9 | positional args |
$# | number of args |
$@ | all args (each quoted) |
$* | all args (one string) |
$$ | current PID |
$? | last exit code |
$! | PID of last backgrounded job |
Argument parsing with getopts
while getopts "vf:" opt; do
case $opt in
v) verbose=1 ;;
f) file=$OPTARG ;;
*) echo "usage..." >&2; exit 1 ;;
esac
done
shift $((OPTIND-1))
Trap (cleanup on exit)
TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT
Commentaires