web-dev-qa-db-fra.com

Meilleur moyen d'analyser les arguments de ligne de commande dans Bash?

Après plusieurs jours de recherche , je n'arrive toujours pas à trouver la meilleure méthode pour analyser les arguments cmdline dans un script .sh. Selon mes références, la cmd getopts est la voie à suivre car elle " extrait et vérifie les commutateurs sans perturber les variables de paramètre de position. Les commutateurs inattendus, ou les commutateurs qui manquent d'arguments, sont reconnus et signalés comme des erreurs. = "

Les paramètres positionnels (Ex. 2 - $ @, $ #, etc.) ne fonctionnent apparemment pas bien lorsque des espaces sont impliqués, mais peuvent reconnaître les paramètres réguliers et longs (-p et --longparam). J'ai remarqué que les deux méthodes échouent lors du passage de paramètres avec des guillemets imbriqués ("ceci est un Ex. De" "quotes" "."). Lequel de ces trois exemples de code illustre le mieux la manière de traiter les arguments cmdline? La fonction getopt n'est pas recommandée par les gourous, donc j'essaye de l'éviter!

Exemple 1:

#!/bin/bash
for i in "$@"
do
case $i in
    -p=*|--prefix=*)
    PREFIX=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
    ;;
    -l=*|--lib=*)
    DIR=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
exit 0

Exemple 2:

#!/bin/bash
echo ‘number of arguments’
echo "\$#: $#"
echo ”

echo ‘using $num’
echo "\$0: $0"
if [ $# -ge 1 ];then echo "\$1: $1"; fi
if [ $# -ge 2 ];then echo "\$2: $2"; fi
if [ $# -ge 3 ];then echo "\$3: $3"; fi
if [ $# -ge 4 ];then echo "\$4: $4"; fi
if [ $# -ge 5 ];then echo "\$5: $5"; fi
echo ”

echo ‘using $@’
let i=1
for x in $@; do
echo "$i: $x"
let i=$i+1
done
echo ”

echo ‘using $*’
let i=1
for x in $*; do
echo "$i: $x"
let i=$i+1
done
echo ”

let i=1
echo ‘using shift’
while [ $# -gt 0 ]
do
echo "$i: $1"
let i=$i+1
shift
done

[/bash]

output:

bash> commandLineArguments.bash
number of arguments
$#: 0

using $num
$0: ./commandLineArguments.bash

using $@

using $*

using shift
#bash> commandLineArguments.bash  "abc def" g h i j*

Exemple 3:

#!/bin/bash

while getopts ":a:" opt; do
  case $opt in
    a)
      echo "-a was triggered, Parameter: $OPTARG" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

exit 0
11
LogicalConfusion

Je trouve que l'utilisation de getopt est la plus simple. Il fournit une gestion correcte des arguments, ce qui est autrement délicat. Par exemple, getopt saura comment gérer les arguments d'une option longue spécifiée sur la ligne de commande comme --arg=option ou --arg option.

Ce qui est utile pour analyser toute entrée passée à un script Shell est l'utilisation du "$@" variables. Reportez-vous à la page de manuel de bash pour savoir en quoi cela diffère de $@. Cela garantit que vous pouvez traiter les arguments qui incluent des espaces.

Voici un exemple de la façon dont je pourrais écrire un script pour analyser quelques arguments de ligne de commande simples:

#!/bin/bash

args=$(getopt -l "searchpath:" -o "s:h" -- "$@")

eval set -- "$args"

while [ $# -ge 1 ]; do
        case "$1" in
                --)
                    # No more options left.
                    shift
                    break
                   ;;
                -s|--searchpath)
                        searchpath="$2"
                        shift
                        ;;
                -h)
                        echo "Display some help"
                        exit 0
                        ;;
        esac

        shift
done

echo "searchpath: $searchpath"
echo "remaining args: $*"

Et utilisé comme ceci pour montrer que les espaces et les guillemets sont préservés:

user@machine:~/bin$ ./getopt_test --searchpath "File with spaces and \"quotes\"."
searchpath: File with spaces and "quotes".
remaining args: other args

Quelques informations de base sur l'utilisation de getopt peuvent être trouvées ici

19
Austin Phillips

Si vous voulez éviter d'utiliser getopt, vous pouvez utiliser cette approche rapide de Nice:

  • Définition de l'aide pour toutes les options sous la forme ## commentaires (personnaliser à votre guise).
  • Définissez pour chaque option une fonction avec le même nom.
  • Copiez les cinq dernières lignes de ce script dans votre script (la magie).

Exemple de script: log.sh

#!/bin/sh
## $PROG 1.0 - Print logs [2017-10-01]
## Compatible with bash and dash/POSIX
## 
## Usage: $PROG [OPTION...] [COMMAND]...
## Options:
##   -i, --log-info         Set log level to info (default)
##   -q, --log-quiet        Set log level to quiet
##   -l, --log MESSAGE      Log a message
## Commands:
##   -h, --help             Displays this help and exists
##   -v, --version          Displays output version and exists
## Examples:
##   $PROG -i myscrip-simple.sh > myscript-full.sh
##   $PROG -r myscrip-full.sh   > myscript-simple.sh
PROG=${0##*/}
LOG=info
die() { echo $@ >&2; exit 2; }

log_info() {
  LOG=info
}
log_quiet() {
  LOG=quiet
}
log() {
  [ $LOG = info ] && echo "$1"; return 1 ## number of args used
}
help() {
  grep "^##" "$0" | sed -e "s/^...//" -e "s/\$PROG/$PROG/g"; exit 0
}
version() {
  help | head -1
}

[ $# = 0 ] && help
while [ $# -gt 0 ]; do
  CMD=$(grep -m 1 -Po "^## *$1, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" log.sh | sed -e "s/-/_/g")
  if [ -z "$CMD" ]; then echo "ERROR: Command '$1' not supported"; exit 1; fi
  shift; eval "$CMD" $@ || shift $? 2> /dev/null
done

Essai

Exécution de cette commande:

./log.sh --log yep --log-quiet -l nop -i -l yes

Produit cette sortie:

yep
yes

Au fait: C'est compatible avec posix !

4
Tet