Bien gérer les paramètres dans BASH

Sommaire

Index

Paramètres positionnels

Les fichiers pour les exercices sont à placer dans un dossier nommé bash-parametres (sauf mention contraire).

$1, $2, $3, …

Ce sont les paramètres positionnels.

Lors de l’appel d’un programme, ils prennent les valeurs données en argument.

Par exemple, créer un fichier nommé affichage-param et y ajouter la ligne ‘echo $1 $2 $3’ :

$ touch affichage-param
$ chmod +x affichage-param
$ echo 'echo $1 $2 $3' >> affichage-param

Le contenu du fichier est donc:

$ cat affichage-param
echo $1 $2 $3

L’appel en passant les paramètres a, b et cdef donne:

$ ./affichage-param a b cdef
a b cdef

… et ainsi de suite.

Voir le chapitre Positional Parameters dans le manuel de bash

Comment accéder aux paramètres après le 9ème

Illustration du problème:

echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10

shift
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10

shift
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10

shift
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10

shift
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10

shift
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10

shift
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10
$ ./affichage-param a b c d e f g h i j k l m n o p q
a b c d e f g h i a0
b c d e f g h i j b0
c d e f g h i j k c0
d e f g h i j k l d0
e f g h i j k l m e0
f g h i j k l m n f0
g h i j k l m n o g0

Modifions le

echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10

while (( $# ))
do

    echo "Nombre de param: $#"
    echo $1
    shift

done

Ce qui donne à l’exécution:

$ ./affichage-param a b c d e f g h i j k l m n o p q
a b c d e f g h i a0
Nombre de param: 16
b
Nombre de param: 15
c
Nombre de param: 14
d
Nombre de param: 13
e
Nombre de param: 12
f
Nombre de param: 11
g
Nombre de param: 10
h

Exercice 4

Écrire un programme nommé convertir_param qui prend en paramètre une liste de fichiers .md et qui les transforme en fichier .html (par exemple un fichier A.md permet de générer un fichier A.html)

Le programme devra respecter les contraintes suivantes:

Génération d’images

Exercice image 1

Écrire un programme nommé txt_simple qui permet de générer une image d’une lettre passée en paramètre.

Le programme doit utiliser la commande convert (ou magick dans les versions plus récentes) du logiciel imagemagick.

Les instructions sur les polices de caractères disponibles sont données ici: image manipulation - With ImageMagick, how can you see all available fonts? - Stack Overflow

Les instructions sur comment générer le texte sont données ici: Text Handling – ImageMagick Examples

Pour sélectionner aléatoirement une police de caractères:

convert -list font | grep Font: | cut -c 9- | xargs shuf -n1 -e

Exercice image 2

Écrire un programme genere_lettres qui génère dans le dossier caché ~/.lettres chacune des lettres de l’alphabet avec des polices de caractères différentes.

Utiliser la liste des polices données par la commande : convert -list font | grep Font: | less

Chaque lettre doit avoir une police différente.

Exercice image 3

Écrire un programme genere_texte qui génère une image d’un texte à partir des images de lettres placées dans le dossier caché ~/.lettres.

Le texte passé en paramètre doit être utilisé pour générer l’image, en parcourant chaque lettre du texte.

Utiliser la liste des polices données par la commande : convert -list font | grep Font: | less

Chaque lettre doit avoir une police différente.

Les instructions comment parcourir une chaîne de caractère en bash sont données ici: How to perform a for loop on each character in a string in Bash? - Stack Overflow

Les instructions pour assembler des images côte à côte sont données ici: imagemagick - Merge Images Side by Side (Horizontally) - Stack Overflow

Exercice image 4

Il arrive parfois qu’utiliser certaines polices de caractères génère des images vides.

Déterminer une méthode pour trouver comment identifier quelles sont les polices générant des images vides.

Créer un fichier nommé image_polices pour mettre en application cette méthode.

Utiliser l’option -v de la commande grep pour filtrer les polices à utiliser dans le programme de genere_lettres_ameliore (qui est une amélioration du programme genere_lettres dans lequel les polices générant du texte vide ne seront pas utilisées).

Traitement des paramètres avec un case

#!/bin/bash

mode_verbeux=false
while (( $# ))
do
    case "$1" in
        -d)
            shift
            dossier_a_traiter="$1"
            ;;
        -v)
            mode_verbeux=true
            ;;
        -h)
            echo "Afficher l'aide"
            ;;
    esac
    shift
done
echo "dossier_a_traiter=$dossier_a_traiter"
echo "mode_verbeux=$mode_verbeux"

Gestion d’un paramètre obligatoire

On ajoute ce code pour vérifier que le paramètre dossier_a_traiter a été renseigné:

if [[ -z $dossier_a_traiter ]]
then
    echo "ERREUR: vous devez préciser le dossier à traiter avec l'option -d" >&2
    exit 2
fi

Voici le programme traiter_dossier :

#!/bin/bash

mode_verbeux=false
while (( $# ))
do
    case "$1" in
        -d)
            shift
            dossier_a_traiter="$1"
            ;;
        -v)
            set -x
            mode_verbeux=true
            ;;
        -h)
            echo "Afficher l'aide"
            ;;
    esac
    shift
done

if [[ -z $dossier_a_traiter ]]
then
    echo "ERREUR: vous devez préciser le dossier à traiter avec l'option -d" >&2
    exit 2
fi

echo "dossier_a_traiter=$dossier_a_traiter"
echo "mode_verbeux=$mode_verbeux"

Traitement des paramètres

Le script suivant fonctionne même sans paramètre. On souhaite bloquer son exécution si aucun paramètre dossier_a_traiter n’est passé.

#!/bin/bash

mode_verbeux=false
while (( $# ))
do
    case "$1" in
        -d)
            shift
            dossier_a_traiter="$1"
            ;;
        -v)
            mode_verbeux=true
            ;;
        -h)
            echo "Afficher l'aide"
            ;;
    esac
    shift
done

if [[ -z $dossier_a_traiter ]]
then
    echo "ERREUR: vous devez préciser le dossier à traiter avec l'option -d" >&2
    exit 2
fi

echo "dossier_a_traiter=$dossier_a_traiter"
echo "mode_verbeux=$mode_verbeux"

ls -l $dossier_a_traiter| awk 'BEGIN{compteur = 0} $1 != "total" && $5 <= 0 {compteur++} END {print compteur}'

Ce qui donne:

#!/bin/bash

mode_verbeux=false
while (( $# ))
do
    case "$1" in
        -d)
            shift
            dossier_a_traiter="$1"
            ;;
        -v)
            mode_verbeux=true
            ;;
        -h)
            echo "Afficher l'aide"
            ;;
    esac
    shift
done

if [[ -z "$dossier_a_traiter" ]]
then
    echo "ERREUR: vous devez préciser le dossier à traiter avec l'option -d" >&2
    exit 2
fi

echo "dossier_a_traiter=$dossier_a_traiter"
echo "mode_verbeux=$mode_verbeux"

ls -l $dossier_a_traiter| awk 'BEGIN{compteur = 0} $1 != "total" && $5 <= 0 {compteur++} END {print compteur}'

Gestion de l’aide

#!/bin/bash

mode_verbeux=false
while (( $# ))
do
    case "$1" in
        -d)
            shift
            dossier_a_traiter="$1"
            ;;
        -v)
            mode_verbeux=true
            ;;
        -h)
            echo "Afficher l'aide"
        exit 0
            ;;
    esac
    shift
done
if [[ -z "$dossier_a_traiter" ]]
then
    echo "Veuillez passer en paramètre un dossier à traiter avec l'option -d" >&2
    exit 1
fi

echo "dossier_a_traiter=$dossier_a_traiter"
echo "mode_verbeux=$mode_verbeux"

ls -l $dossier_a_traiter| awk 'BEGIN{compteur = 0} $1 != "total" && $5 <= 0 {compteur++} END {print compteur}'

Les fichiers pour cette partie sont à placer dans le dossier bash-parametres/traitement

Le nom du fichier est traiter_dossier

(ajouter des commentaires dans le code pour indiquer la partie de la question que vous traitez)

$ ./traiter_dossier -h
Afficher l'aide
  1. Traiter le cas où le dossier à traiter n’existe pas et afficher une erreur puis provoquer la sortie du programme le cas échéant
  2. Faire en sorte de n’exécuter les commandes suivantes que si le mode verbeux est activé:
echo "dossier_a_traiter=$dossier_a_traiter"
echo "mode_verbeux=$mode_verbeux"

Ajout d’éléments fonctionnels

L’objectif est de modifier ce script pour qu’il supprime le dossier à traiter s’il ne contient que des fichiers vides.

  1. Décrire la condition (en langage naturel) qui permet de dire si on peut supprimer le contenu d’un dossier sans risque
  2. Déterminer dans quels cas la suppression peut être problématique par rapport à une certaine catégorie de fichiers
  3. Écrire une commande permettant de déterminer le nombre total de fichiers non vides dans le script.
  4. Écrire une commande permettant d’afficher tous les fichiers vides (astuce: awk peut resservir)
  5. Écrire une commande permettant d’utiliser cette liste de fichiers vides pour supprimer ces derniers (astuce: xargs ou for peuvent être utilisés)
  6. Donner au moins deux méthodes (ou commandes) de suppression permettant la suppression du dossier (l’une d’elle consistant à supprimer d’abord tous les fichiers vides contenu dans celui-ci)
  7. Choisir une méthode pour écrire une commande permettant de tester puis supprimer le dossier si nécessaire et l’implémenter en BASH

Exemple: traiter un fichier

Dans le programme suivant, un fichier doit être passé en paramètre.

Si l’option -t ou l’option -s est choisie, une action est réalisée.

#!/bin/bash

mode_verbeux=false
option_stat=0
option_type=0
while (( $# ))
do
    case "$1" in
        -f)
            shift
            fichier_a_traiter="$1"
            ;;
        -v)
            set -x
            mode_verbeux=true
            ;;
        -t)
            option_type=1
            ;;
        -s)
            option_stat=1
            ;;
        -h)
            echo "Afficher l'aide"
            ;;
    esac
    shift
done

if [[ -z "$fichier_a_traiter" ]]
then
    echo "ERREUR: vous devez préciser le fichier à traiter avec l'option -f" >&2
    exit 2
fi

if [[ ! -e "$fichier_a_traiter" ]]
then
    echo "ERREUR: le fichier $fichier_a_traiter n'exite pas" >&2
    exit 2
fi

if [[ "$option_stat" = 1 ]]
then
    stat "$fichier_a_traiter"
fi
if [[ "$option_type" = 1 ]]
then
    if [[ -d "$fichier_a_traiter" ]]
    then
        echo "C'est un dossier"
    fi
    if [[ -f "$fichier_a_traiter" ]]
    then
        echo "C'est un fichier régulier"
    fi
fi

Lire et comprendre le programme précédent.

Exercice : vérification de l’existence du dossier

Ajouter au programme traiter_dossier la vérification de l’existence du dossier.

Si le dossier n’existe pas, le programme doit afficher un message et s’arrêter

Les commandes suivantes peuvent vous aider à trouver comment faire cette vérification:

help test
man test
man bash

Une fois cette vérification faite, le programme doit afficher le nombre de fichiers présents (fichiers ou dossiers) à l’intérieur de ce dossier ainsi que la taille occupée sur le disque.

Rappel: apropos est une commande permettant de chercher, par exemple, l’espace utilisé sur le disque (en lui passant un paramètre approprié comme par exemple disk ou space)

Action à réaliser

Ajouter au programme précédent les options suivantes:

Utilisation de getopt

Exécution de getopt

Placée au début d’un script, la commande suivante :

getopt -l "file:,verbose,type,stat,help" -o "f:vtsh" -- "$@"

Affichage en fonction des paramètres passés ($@):

Si on passe une option non acceptée, on aura un message d’erreur:

Explication

getopt prend en paramètres deux chaînes qui permettent de définir les options acceptées.

Les définitions d’option sont séparées par des virgules ,

Les options suivies d’un : indiquent à getopt que l’option reçoit un paramètre supplémentaire.

Redéfinition des arguments du shell courant

Grâce à la sortie de getopt, on peut redéfinir les arguments du Shell courant:

options=$(getopt -l "file:,verbose,type,stat,help" -o "f:vtsh" -- "$@")
eval set -- "$options"

Vérification du code de sortie

options=$(getopt -l "file:,verbose,type,stat,help" -o "f:vtsh" -- "$@")
if [[ $? != 0 ]]
then
  # On peut afficher ici un message d'erreur supplémentaire
  # voire l'aide de la commande
    exit 7
fi
eval set -- "$options"

Exemple

#!/bin/bash

options=$(getopt -l "file:,verbose,type,stat,help" -o "f:vtsh" -- "$@")
if [[ $? != 0 ]]
then
    exit 7
fi

echo $options
eval set -- "$options"
mode_verbeux=false
option_stat=0
option_type=0
while :
do
    case "$1" in
        -f|--file)
            shift
            fichier_a_traiter="$1"
            ;;
        -v | --verbose)
            set -x
            mode_verbeux=true
            ;;
        -t)
            option_type=1
            ;;
        -s)
            option_stat=1
            ;;
        -h)
            echo "Afficher l'aide"
            ;;
        --)
            shift
            break
            ;;
    esac
    shift
done

if [[ -z "$fichier_a_traiter" ]]
then
    echo "ERREUR: vous devez préciser le dossier \
    à traiter avec l'option -d" >&2
    exit 2
fi

if [[ ! -e "$fichier_a_traiter" ]]
then
    echo "ERREUR: le fichier $fichier_a_traiter n'exite pas" >&2
    exit 2
fi


## Dernier étage

if [[ "$option_stat" = 1 ]]
then
    stat "$fichier_a_traiter"
fi
if [[ "$option_type" = 1 ]]
then
    if [[ -d "$fichier_a_traiter" ]]
    then
        echo "C'est un dossier"
    fi
    if [[ -f "$fichier_a_traiter" ]]
    then
        echo "C'est un fichier régulier"
    fi

fi

echo "---------------------------------"

ls -d $@

Exercice

Créer un programme nommé execdossier qui permettra d’exécuter un ensemble de fichiers placés dans un dossier.

Le dossier sera le paramètre obligatoire.

Si ce dossier existe, le programme doit:

  1. récupérer la liste des fichiers du dossier
  2. déterminer si chaque fichier est exécutable
  3. s’il est exécutable, exécuter le fichier
  4. récupérer le code de sortie de chaque exécution
  5. en fonction de la présence de l’option -s, deux possibilités.