web-dev-qa-db-fra.com

Validation des paramètres dans un script Bash

Je suis venu avec un base pour aider à automatiser le processus de suppression d'un certain nombre de dossiers lorsqu'ils deviennent inutiles.

#!/bin/bash
rm -rf ~/myfolder1/$1/anotherfolder
rm -rf ~/myfolder2/$1/yetanotherfolder
rm -rf ~/myfolder3/$1/thisisafolder

Ceci est évoqué comme suit:

./myscript.sh <{id-number}>

Le problème est que si vous oubliez de taper le id-number (comme je viens de le faire) , il pourrait potentiellement supprimer beaucoup de choses que vous ne voulez vraiment pas supprimer.

Y a-t-il un moyen d'ajouter n'importe quelle forme de validation aux paramètres de ligne de commande? Dans mon cas, il serait bon de vérifier qu'il y a un paramètre, b) qu'il est numérique et c ) ce dossier existe; avant de continuer avec le script.

88
nickf
#!/bin/sh
die () {
    echo >&2 "$@"
    exit 1
}

[ "$#" -eq 1 ] || die "1 argument required, $# provided"
echo $1 | grep -E -q '^[0-9]+$' || die "Numeric argument required, $1 provided"

while read dir 
do
    [ -d "$dir" ] || die "Directory $dir does not exist"
    rm -rf "$dir"
done <<EOF
~/myfolder1/$1/anotherfolder 
~/myfolder2/$1/yetanotherfolder 
~/myfolder3/$1/thisisafolder
EOF

edit: J'ai raté la partie concernant la vérification de l'existence des répertoires au début, alors je l'ai ajouté, complétant ainsi le script. Ont également abordé les problèmes soulevés dans les commentaires; fixe l'expression régulière, passe de == à eq.

Cela devrait être un script portable, conforme à POSIX, autant que je sache; il n'utilise pas de bashismes, ce qui est en fait important parce que /bin/sh sur Ubuntu est en fait dash de nos jours, pas bash.

150
Brian Campbell

La solution sh de Brian Campbell Bien que noble et bien exécuté, il a quelques problèmes, alors j’ai pensé apporter ma propre solution bash.

Les problèmes avec le sh one:

  • Le tilde dans ~/foo ne s'étend pas à votre répertoire personnel dans heredocs. Et ni quand il est lu par l'instruction read ou cité dans l'instruction rm. Ce qui signifie que vous aurez No such file or directory les erreurs.
  • Décaler grep et autres opérations de base est idiot. Surtout lorsque vous utilisez une coquille de merde pour éviter le poids "lourd" de la bash.
  • J'ai aussi remarqué quelques problèmes de citations, par exemple autour d'un paramètre de développement dans son echo.
  • Bien que rare, la solution ne peut pas gérer les noms de fichiers contenant des nouvelles lignes. (Presque aucune solution dans sh ne peut y faire face - c'est pourquoi je préfère presque toujours bash, il est beaucoup plus résistant et plus difficile à exploiter lorsqu'il est utilisé correctement).

Alors que, oui, en utilisant /bin/sh _ car votre hashbang signifie que vous devez éviter bashismes à tout prix, vous pouvez utiliser tous les bashismes que vous aimez, même sur Ubunutu ou autre chose quand vous êtes honnête et mettez #!/bin/bash au sommet.

Voici donc une solution bash qui est plus petite, plus propre, plus transparente, probablement "plus rapide" et plus efficace.

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. Remarquez les guillemets autour de $1 dans l'instruction rm!
  2. Le -d La vérification échouera également si $1 est vide, donc deux contrôles en un.
  3. J'ai évité les expressions régulières pour une raison. Si vous devez utiliser =~ dans bash, vous devriez mettre l'expression régulière dans une variable. Dans tous les cas, les globs comme le mien sont toujours préférables et supportés dans des versions beaucoup plus basses.
20
lhunath

Je voudrais utiliser bash [[:

if [[ ! ("$#" == 1 && $1 =~ ^[0-9]+$ && -d $1) ]]; then 
    echo 'Please pass a number that corresponds to a directory'
    exit 1
fi

J'ai trouvé cette FAQ être une bonne source d'informations.

14

Pas aussi à l'épreuve des balles que la réponse ci-dessus, mais toujours efficace:

#!/bin/bash
if [ "$1" = "" ]
then
  echo "Usage: $0 <id number to be cleaned up>"
  exit
fi

# rm commands go here
12
Boiler Bill

Utilisation set -u, ce qui fera échouer immédiatement le script dans toute référence d'argument non définie.

http://www.davidpashley.com/articles/writing-robust-Shell-scripts.html

10
shmichael

Utilisez '-z' pour tester les chaînes vides et '-d pour rechercher les répertoires.

if [[ -z "$@" ]]; then
    echo >&2 "You must supply an argument!"
    exit 1
Elif [[ ! -d "$@" ]]; then
    echo >&2 "$@ is not a valid directory!"
    exit 1
fi
8
guns

La page de manuel de test (man test) fournit tous les opérateurs disponibles que vous pouvez utiliser comme opérateurs booléens dans bash. Utilisez ces indicateurs au début de votre script (ou de vos fonctions) pour la validation des entrées, comme vous le feriez dans tout autre langage de programmation. Par exemple:

if [ -z $1 ] ; then
  echo "First parameter needed!" && exit 1;
fi

if [ -z $2 ] ; then
  echo "Second parameter needed!" && exit 2;
fi
7
whaley

Vous pouvez valider les points a et b de manière compacte en procédant comme suit:

#!/bin/sh
MYVAL=$(echo ${1} | awk '/^[0-9]+$/')
MYVAL=${MYVAL:?"Usage - testparms <number>"}
echo ${MYVAL}

Ce qui nous donne ...

$ ./testparams.sh 
Usage - testparms <number>

$ ./testparams.sh 1234
1234

$ ./testparams.sh abcd
Usage - testparms <number>

Cette méthode devrait bien fonctionner chez sh.

5
MattK

Ancien post mais je pensais que je pouvais contribuer quand même.

Un script n'est sans doute pas nécessaire et pourrait être exécuté à partir de la ligne de commande avec une certaine tolérance aux caractères génériques.

  1. correspondant n'importe où sauvage. Permet de supprimer toute occurrence de sous "dossier"

    $ rm -rf ~/*/folder/*
    
  2. Shell itéré. Permet de supprimer les dossiers spécifiques pré et post avec une seule ligne

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
    
  3. Shell itéré + var (testé par BASH).

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
    
1
Xarses