Fondamentaux des scripts shell

ShellBeginner
Pratiquer maintenant

Introduction

Un script shell stocke des commandes dans un fichier afin de pouvoir répéter une tâche de manière cohérente. Les scripts fiables pour débutants indiquent explicitement leur interpréteur, protègent les expansions de variables avec des guillemets, valident les données saisies avant d'agir et renvoient un statut de sortie que les autres commandes peuvent interpréter.

Dans ce laboratoire, vous allez créer progressivement de petits scripts Bash. Vous exécuterez des scripts avec Bash et en tant que fichiers exécutables, utiliserez des variables et des arguments positionnels, lirez une saisie interactive, effectuerez des branchements avec if et des tests de fichiers, choisirez des codes de sortie, itérerez avec for et combinerez ces notions dans un simple résumé de journaux.

Créer et exécuter votre premier script

Dans cette étape, vous allez créer un script Bash, l'exécuter avec un interpréteur, puis le rendre directement exécutable.

Accédez à l'espace de travail du laboratoire :

cd /home/labex/project/shell-lab

Créez hello.sh avec un document here. La première ligne est un shebang qui sélectionne Bash lorsque le fichier est exécuté directement :

cat > hello.sh <<'EOF'
#!/bin/bash

echo "Hello from a Bash script"
pwd
EOF

Examinez le fichier avant de l'exécuter :

cat hello.sh

Passez explicitement le fichier à Bash. Cette commande fonctionne même avant l'ajout de la permission d'exécution :

bash hello.sh

Ajoutez la permission d'exécution et examinez le mode du fichier :

chmod u+x hello.sh
ls -l hello.sh

Exécutez le fichier depuis le répertoire courant. ./ fournit un chemin ; par défaut, le shell ne recherche pas automatiquement les commandes dans le répertoire courant :

./hello.sh

Utiliser des variables et des guillemets sûrs

Dans cette étape, vous allez affecter des variables, les développer et voir pourquoi les guillemets doubles protègent les valeurs contenant des espaces.

Créez un script avec deux variables :

cat > variables.sh <<'EOF'
#!/bin/bash

course="Linux for Noobs"
workspace="/home/labex/project/shell-lab"

echo "Course: $course"
echo "Workspace: $workspace"
printf 'Quoted argument count: %s\n' "$#"
printf 'First argument: %s\n' "${1:-not provided}"
EOF

Dans une affectation shell, aucun espace ne doit entourer =. Les guillemets doubles permettent l'expansion de $course tout en conservant Linux for Noobs comme une seule valeur.

Exécutez le script avec un argument contenant des espaces :

bash variables.sh "beginner learner"

La variable spéciale $# contient le nombre d'arguments positionnels et $1 contient le premier argument. ${1:-not provided} fournit une valeur de remplacement lorsque le premier argument est absent.

Enregistrez la sortie pour l'examiner :

bash variables.sh "beginner learner" > variable-output.txt
cat variable-output.txt

Valider les arguments positionnels

Dans cette étape, vous allez rendre un argument obligatoire, afficher une indication d'utilisation et renvoyer des statuts de sortie différents en cas d'échec et de réussite.

Créez greet-argument.sh :

cat > greet-argument.sh <<'EOF'
#!/bin/bash

if [ "$#" -ne 1 ]; then
  echo "Usage: $0 NAME" >&2
  exit 2
fi

name=$1
echo "Hello, $name"
exit 0
EOF

La commande de test [ ... ] vérifie si le nombre d'arguments est différent de un. >&2 envoie le texte d'utilisation vers la sortie d'erreur standard et exit 2 signale une utilisation incorrecte.

Exécutez le script sans argument, puis affichez immédiatement $?, le statut de sortie de la commande précédente :

bash greet-argument.sh
echo "missing argument status: $?"

Le statut attendu est 2. Exécutez ensuite le script correctement :

bash greet-argument.sh "Ada Lovelace"
echo "valid argument status: $?"

Enregistrez le message d'accueil obtenu :

bash greet-argument.sh "Ada Lovelace" > argument-greeting.txt

Lire une saisie interactive

Dans cette étape, vous allez inviter un utilisateur à saisir une valeur, lire une ligne en toute sécurité et gérer une saisie vide.

Créez ask-name.sh :

cat > ask-name.sh <<'EOF'
#!/bin/bash

read -r -p "Enter your name: " name

if [ -z "$name" ]; then
  echo "A name is required" >&2
  exit 1
fi

echo "Welcome, $name"
EOF

read -r lit une ligne sans traiter les barres obliques inverses de manière particulière. -p affiche une invite et -z vérifie si la valeur placée entre guillemets est vide.

Exécutez le script :

bash ask-name.sh

À l'invite, saisissez :

Grace Hopper

Vous devriez voir Welcome, Grace Hopper. Pour obtenir un résultat enregistré et reproductible, transmettez une ligne de saisie au même script :

printf 'Grace Hopper\n' | bash ask-name.sh > interactive-greeting.txt
cat interactive-greeting.txt

Effectuer des branchements avec des tests de fichiers

Dans cette étape, vous allez utiliser if, elif et else avec des tests de fichiers pour déterminer la nature d'un chemin.

Créez classify-path.sh :

cat > classify-path.sh <<'EOF'
#!/bin/bash

path=${1:-}

if [ -z "$path" ]; then
  echo "Usage: $0 PATH" >&2
  exit 2
elif [ -d "$path" ]; then
  echo "directory: $path"
elif [ -f "$path" ]; then
  echo "file: $path"
else
  echo "missing: $path" >&2
  exit 1
fi

exit 0
EOF

-d vérifie qu'il s'agit d'un répertoire et -f vérifie qu'il s'agit d'un fichier ordinaire. L'ordre est important : l'utilisation incorrecte est traitée avant le type du chemin.

Testez les trois résultats possibles pour le chemin :

bash classify-path.sh logs
bash classify-path.sh notes.txt
bash classify-path.sh missing.txt
echo "missing path status: $?"

Enregistrez ensemble les résultats obtenus avec succès. && n'exécute la deuxième commande que si la première réussit :

bash classify-path.sh logs > path-results.txt && bash classify-path.sh notes.txt >> path-results.txt
cat path-results.txt

Traiter plusieurs fichiers avec une boucle for

Dans cette étape, vous allez parcourir des fichiers journaux et compter les enregistrements correspondants dans chacun d'eux.

Créez count-errors.sh :

cat > count-errors.sh <<'EOF'
#!/bin/bash

log_dir=${1:-logs}

if [ ! -d "$log_dir" ]; then
  echo "Log directory not found: $log_dir" >&2
  exit 1
fi

for log_file in "$log_dir"/*.log; do
  error_count=$(grep -c '^ERROR ' "$log_file" || true)
  printf '%s errors=%s\n' "$(basename "$log_file")" "$error_count"
done
EOF

À chaque itération, la boucle affecte à log_file un chemin correspondant. Les guillemets autour de "$log_file" protègent les espaces, tandis que basename supprime le préfixe du répertoire. grep -c compte les lignes correspondantes ; || true empêche l'absence de correspondance d'interrompre un flux de travail strict plus vaste.

Exécutez la boucle et enregistrez sa sortie :

bash count-errors.sh logs | tee error-counts.txt

Vous devriez voir un résultat pour chaque fichier .log.

Combiner validation, boucles et statuts de sortie

Dans cette étape, vous allez créer un script réutilisable qui valide un répertoire, résume ses journaux et signale clairement la réussite ou l'échec.

Créez summarize-logs.sh :

cat > summarize-logs.sh <<'EOF'
#!/bin/bash

input_dir=${1:-}
output_file=${2:-}

if [ -z "$input_dir" ] || [ -z "$output_file" ]; then
  echo "Usage: $0 INPUT_DIR OUTPUT_FILE" >&2
  exit 2
fi

if [ ! -d "$input_dir" ]; then
  echo "Input directory not found: $input_dir" >&2
  exit 1
fi

: > "$output_file"

for log_file in "$input_dir"/*.log; do
  lines=$(wc -l < "$log_file")
  errors=$(grep -c '^ERROR ' "$log_file" || true)
  printf '%s lines=%s errors=%s\n' "$(basename "$log_file")" "$lines" "$errors" >> "$output_file"
done

echo "Wrote summary to $output_file"
exit 0
EOF

La commande : > "$output_file" crée la destination ou la vide de manière sûre avant la boucle. Exécutez le script avec les deux arguments obligatoires :

chmod u+x summarize-logs.sh
./summarize-logs.sh logs log-summary.txt
echo "summary status: $?"
cat log-summary.txt

Testez le chemin d'échec avec un répertoire inexistant :

./summarize-logs.sh missing log-summary.txt
echo "missing directory status: $?"

Le statut de réussite doit être 0, tandis que le statut associé au répertoire inexistant doit être 1.

Résumé

Vous avez créé et exécuté des scripts Bash, utilisé des shebangs et des permissions, développé des variables protégées par des guillemets, lu des saisies positionnelles et interactives et renvoyé des statuts de sortie pertinents. Vous avez également effectué des branchements avec if/elif/else, testé des fichiers et des répertoires, parcouru des journaux avec for et combiné ces notions dans un outil réutilisable de création de résumés.

Ces notions fondamentales rendent les automatisations ultérieures plus faciles à lire, plus sûres à réexécuter et plus simples à évaluer pour d'autres commandes ou des tâches planifiées.