Passer des arguments nommés à des scripts shell

114

Existe-t-il un moyen simple de transmettre (recevoir) des paramètres nommés à un script shell?

Par exemple,

my_script -p_out '/some/path' -arg_1 '5'

Et à l'intérieur, my_script.shrecevez-les comme:

# I believe this notation does not work, but is there anything close to it?
p_out=$ARGUMENTS['p_out']
arg1=$ARGUMENTS['arg_1']

printf "The Argument p_out is %s" "$p_out"
printf "The Argument arg_1 is %s" "$arg1"

Est-ce possible dans Bash ou Zsh?

Amelio Vazquez-Reina
la source
2
jetez un oeil à docopt - cela aide avec les paramètres nommés et fait aussi la validation des entrées
Beat

Réponses:

34

La syntaxe probablement la plus proche est la suivante:

p_out='/some/path' arg_1='5' my_script
Hauke ​​Laging
la source
7
Dans le même ordre d'idées, si l' -koption est définie dans le shell appelant , my_script p_out='/some/path' arg_1='5'le même effet est obtenu. (Tous les arguments sous la forme d'une affectation sont ajoutés à l'environnement, pas seulement les affectations précédant la commande.)
chepner
13
J'avais l'habitude d'aimer cette syntaxe, mais il y a une grosse mise en garde: après l'exécution de la commande / fonction, ces variables seront toujours définies dans la portée actuelle! Ex: x=42 echo $x; echo $xce qui signifie que lors de la prochaine exécution de my_script, si p_outest omis, il restera à la valeur passée la dernière fois !! ( '/some/path')
Lucas Cimon
@ LucasCimon Ne pouvez-vous pas unset, après la première exécution, les réinitialiser avant la prochaine exécution?
Nikos Alexandris
2
@ LucasCimon Ce n'est pas correct. x=42 echo $xn'émet même rien si cela $xn'a pas été défini auparavant.
Hauke ​​Laging
Vous avez raison @HaukeLaging, merci d'avoir corrigé cela
Lucas Cimon le
148

Si cela ne vous dérange pas d’être limité aux noms d’argument à une lettre my_script -p '/some/path' -a5, c’est -à- dire que vous pouvez utiliser bash getopts, par exemple:

#!/bin/bash

while getopts ":a:p:" opt; do
  case $opt in
    a) arg_1="$OPTARG"
    ;;
    p) p_out="$OPTARG"
    ;;
    \?) echo "Invalid option -$OPTARG" >&2
    ;;
  esac
done

printf "Argument p_out is %s\n" "$p_out"
printf "Argument arg_1 is %s\n" "$arg_1"

Alors tu peux faire

$ ./my_script -p '/some/path' -a5
Argument p_out is /some/path
Argument arg_1 is 5

Il existe un didacticiel de petite taille sur getopts ou vous pouvez taper help getoptsà l'invite du shell.

Steeldriver
la source
25
Cela devrait être la réponse acceptée
Kaushik Ghose
3
Je sais que c'est un peu vieux, mais pourquoi seulement 1 lettre pour les arguments?
Kevin
1
J'ai implémenté ceci (mais avec iet d). Lorsque je l'exécute avec my_script -i asd -d asdune chaîne vide pour l' dargument. Lorsque je l'exécute, la my_script -d asd -i asdchaîne est vide pour les deux arguments.
Milkncookiez
3
@Milkncookiez - J'ai eu un problème similaire - je n'ai pas inclus de ':' après le dernier argument (un 'w' dans mon cas). Une fois que j'ai ajouté le ":", il a commencé à fonctionner comme prévu
Derek
37

J'ai volé ceci à drupal.org , mais vous pourriez faire quelque chose comme ceci:

while [ $# -gt 0 ]; do
  case "$1" in
    --p_out=*)
      p_out="${1#*=}"
      ;;
    --arg_1=*)
      arg_1="${1#*=}"
      ;;
    *)
      printf "***************************\n"
      printf "* Error: Invalid argument.*\n"
      printf "***************************\n"
      exit 1
  esac
  shift
done

Le seul inconvénient est que vous devez utiliser la syntaxe my_script --p_out=/some/path --arg_1=5.

cdmo
la source
7
La mise en garde n'est pas nécessaire. :) Vous pouvez avoir les conditions suivantes:-c|--condition)
Milkncookiez
28

J'utilise ce script et fonctionne comme un charme:

for ARGUMENT in "$@"
do

    KEY=$(echo $ARGUMENT | cut -f1 -d=)
    VALUE=$(echo $ARGUMENT | cut -f2 -d=)   

    case "$KEY" in
            STEPS)              STEPS=${VALUE} ;;
            REPOSITORY_NAME)    REPOSITORY_NAME=${VALUE} ;;     
            *)   
    esac    


done

echo "STEPS = $STEPS"
echo "REPOSITORY_NAME = $REPOSITORY_NAME"

Usage

bash my_scripts.sh  STEPS="ABC" REPOSITORY_NAME="stackexchange"

Résultat de la console:

STEPS = ABC
REPOSITORY_NAME = stackexchange

STEPS et REPOSITORY_NAME sont prêts à être utilisés dans le script.

Peu importe l'ordre des arguments.

JRichardsz
la source
4
C'est soigné et devrait être la réponse acceptée.
miguelmorin le
15

Avec zsh, vous utiliseriez zparseopts:

#! /bin/zsh -
zmodload zsh/zutil
zparseopts -A ARGUMENTS -p_out: -arg_1:

p_out=$ARGUMENTS[--p_out]
arg1=$ARGUMENTS[--arg_1]

printf 'Argument p_out is "%s"\n' "$p_out"
printf 'Argument arg_1 is "%s"\n' "$arg_1"

Mais vous appelez le script avec myscript --p_out foo.

Notez que zparseoptscela ne prend pas en charge l’abréviation d’options longues ni la --p_out=foosyntaxe comme GNU getopt(3).

Stéphane Chazelas
la source
Savez-vous pourquoi zparseopts utilise un seul tiret pour les arguments alors que dans le cas []il y a 2 tirets? N'a pas de sens!
Timo
@Timo, voir les info zsh zparseoptsdétails
Stéphane Chazelas
9

Je viens de venir avec ce script

while [ $# -gt 0 ]; do

   if [[ $1 == *"--"* ]]; then
        v="${1/--/}"
        declare $v="$2"
   fi

  shift
done

transmettez-le comme my_script --p_out /some/path --arg_1 5puis dans le script, vous pouvez utiliser $arg_1et $p_out.

Shahzad Malik
la source
J'aime cette solution dans KSH88 je devais v=``echo ${1} | awk '{print substr($1,3)}'`` typeset $v="$2"(Supprimer une backtick de chaque côté)
hol
-2

Si une fonction ou une application a plus que zéro arguments, elle a toujours un dernier argument.

Si vous voulez lire les paires option drapeau et valeur, comme dans: $ ./t.sh -o output -i input -l last

Et vous voulez accepter un nombre variable de paires option / valeur,

Et je ne veux pas un énorme arbre "si .. alors .. sinon .. fi",

Puis, après avoir vérifié le nombre d'arguments non nul et égal,

Ecrivez une boucle while avec ces quatre instructions eval comme corps, suivie par une instruction case utilisant les deux valeurs déterminées à chaque passage dans la boucle.

La partie délicate du script est illustrée ici:

#!/bin/sh    

# For each pair - this chunk is hard coded for the last pair.
eval TMP="'$'$#"
eval "PICK=$TMP"
eval TMP="'$'$(($#-1))"
eval "OPT=$TMP"

# process as required - usually a case statement on $OPT
echo "$OPT \n $PICK"

# Then decrement the indices (as in third eval statement) 

:<< EoF_test
$ ./t.sh -o output -i input -l last
-l 
last
$ ./t.sh -o output -l last
-l 
last
$ ./t.sh  -l last
-l 
last
EoF_test
knc1
la source
-3
mitsos@redhat24$ my_script "a=1;b=mitsos;c=karamitsos"
#!/bin/sh
eval "$1"

vous venez d'injecter des paramètres de ligne de commande dans la portée du script!

thettalos
la source
3
Cela ne fonctionne pas avec la syntaxe spécifiée par l'OP. ils veulent-a 1 -b mitsos -c karamitsos
Michael Mrozek