2011-01-03 20 views
18

Ya sé sobre getopts, y esto está bien, pero es molesto que tengas que tener una bandera incluso para argumentos obligatorios.¿Cómo crear un script bash que tome argumentos?

Idealmente, me gustaría ser capaz de tener un script que recibe argumentos en esta forma:

script.sh [optional arguments] [anything required] 

por ejemplo

script.sh -rvx output_file.txt 

donde el guión dice que hay que tener una salida archivo. ¿Hay alguna manera fácil de hacer esto?

Por lo que sé, con getopts debería tener el siguiente aspecto: script.sh -rvx -f output_file.txt, y eso simplemente no es muy limpio.

También puedo usar Python si es necesario, pero solo tengo 2.4 disponibles, que es un poco anticuado.

Respuesta

29

No utilice getopts integrados, use getopt (1) en su lugar. Son (sutilmente) diferentes y hacen cosas diferentes bien. Para que el escenario que podría hacer esto:

#!/bin/bash 

eval set -- $(getopt -n $0 -o "-rvxl:" -- "[email protected]") 

declare r v x l 
declare -a files 
while [ $# -gt 0 ] ; do 
     case "$1" in 
       -r) r=1 ; shift ;; 
       -v) v=1 ; shift ;; 
       -x) x=1 ; shift ;; 
       -l) shift ; l="$1" ; shift ;; 
       --) shift ;; 
       -*) echo "bad option '$1'" ; exit 1 ;; 
       *) files=("${files[@]}" "$1") ; shift ;; 
     esac 
done 

if [ ${#files} -eq 0 ] ; then 
     echo output file required 
     exit 1 
fi 

[ ! -z "$r" ] && echo "r on" 
[ ! -z "$v" ] && echo "v on" 
[ ! -z "$x" ] && echo "x on" 

[ ! -z "$l" ] && echo "l == $l" 

echo "output file(s): ${files[@]}" 

EDIT: para lo completo que he proporcionado un ejemplo de manejo de una opción que requiere un argumento.

+0

Parece que hace exactamente lo que yo quiero. Una pregunta con esto, sin embargo: si quiero permitir un indicador que toma un argumento (por ejemplo, -l archivo.txt), ¿cuál sería la sintaxis? Gracias de nuevo. –

+0

Consulte la página getopt man para eso. Brevemente: agregue dos puntos a la opción (por ejemplo, '" -rv: x "', que requiere v para tomar un argumento). También se admiten opciones largas, que es una de las formas en que triunfa sobre los getopts integrados. Tenga en cuenta que tendrá que manejar el argumento adicional no opcional usted mismo. – Sorpigal

+0

Muchas gracias. Una excelente respuesta. –

11

Si está utilizando getops, simplemente cambie por $OPTIND-1 después de su declaración de caso. Entonces lo que queda en $* será todo lo demás, que es probablemente lo que quiere.

shift $((${OPTIND} - 1)); echo "${*}" 
+0

Sí. Simplemente marque $ # para asegurarse de que se pasó la cantidad correcta de argumentos requeridos, luego acceda a ellos como $ 1, $ 2, etc., como de costumbre. –

4

Estás sufriendo de ilusiones; utilizando getopts no requiere argumentos obligatorios con el prefijo de una letra de bandera. Traté de encontrar un ejemplo adecuado de mi corpus de scripts; esta es una aproximación semi-decente. Se llama rcsunco y se utiliza para cancelar una salida de RCS. No lo he modificado desde hace un tiempo, ya veo; Lo uso con bastante frecuencia (porque aún no he migrado completamente de RCS).

#!/bin/sh 
# 
# "@(#)$Id: rcsunco.sh,v 2.1 2002/08/03 07:41:00 jleffler Exp $" 
# 
# Cancel RCS checkout 

# -V print version number 
# -n do not remove or rename checked out file (like SCCS unget) (default) 
# -r remove checked out file (default) 
# -k keep checked out file as $file.keep 
# -g checkout (unlocked) file after clean-up 
# -q quiet checkout 

: ${RCS:=rcs} 
: ${CO:=co} 

remove=yes 
keep=no 
get=no 
quiet= 

while getopts gknqrV opt 
do 
    case $opt in 
    V) echo "`basename $0 .sh`: RCSUNCO Version $Revision: 2.1 $ ($Date: 2002/08/03 07:41:00 $)" | 
     rcsmunger 
     exit 0;; 
    g) get=yes;; 
    k) keep=yes;; 
    n) remove=no;; 
    q) quiet=-q;; 
    r) remove=yes;; 
    *) echo "Usage: `basename $0 .sh` [-{n|g}][-{r|k}] file [...]" 1>&2 
     exit 1;; 
    esac 
done 

shift $(($OPTIND-1)) 

for file in $* 
do 
    rfile=$(rfile $file) 
    xfile=$(xfile $rfile) 
    if $RCS -u $rfile 
    then 
     if [ $keep = yes ] 
     then 
      if [ -f $xfile ] 
      then 
       mv $xfile $xfile.keep 
       echo "$xfile saved in $xfile.keep" 
      fi 
     elif [ $remove = yes ] 
     then rm -f $xfile 
     fi 
     if [ $get = yes ] && [ $remove = yes -o $keep = yes ] 
     then $CO $quiet $rfile 
     fi 
    fi 
done 

Es solo una aproximación semi-decente; el script silenciosamente no hace nada si no proporciona ningún nombre de archivo después de los argumentos opcionales. Sin embargo, si lo necesita, puede verificar que los argumentos obligatorios estén presentes después del 'cambio'. Otro script mío tiene argumentos obligatorios. Contiene:

... 
shift $(($OPTIND - 1)) 

case $# in 
2) case $1 in 
    install) MODE=Installation;; 
    uninstall) MODE=Uninstallation;; 
    *)   usage;; 
    esac;; 
*) usage;; 
esac 

Por lo tanto, esta orden (jlss) puede tomar argumentos opcionales, tales como -d $HOME, pero requiere ya sea install o uninstall seguido por el nombre de algo de instalar. El modo básico de uso es:

jlss install program 

Pero el modo opcional es:

jlss -d $HOME -u me -g mine -x -p install program 

I, no mostró todos jlss ya que tiene cerca de 12 opciones - no es tan compacto como rcsunco .


Si se tratara de argumentos obligatorios antes de argumentos de opciones, entonces usted tendría que hacer un poco más de trabajo:

  • Se quedaría con los argumentos obligatorios, ellos desplazando fuera de la camino.
  • Luego procesa los argumentos opcionales con los indicadores.
  • Finalmente, si corresponde, maneja los argumentos adicionales de 'nombre de archivo'.

Si se trata de argumentos obligatorios intercalados con argumentos de opción (tanto antes como después de los obligatorios), entonces todavía tiene más trabajo por hacer. Esto es utilizado por muchos sistemas VCS; CVS y GIT tanto tienen la facilidad:

git -global option command [-sub option] [...] 

Aquí, se corre un getopts bucle para obtener las opciones globales; recoger los argumentos obligatorios; y ejecute un segundo bucle getopts para obtener las subopciones (y quizás ejecute un bucle final sobre los argumentos de 'nombre de archivo').

¿No es divertido la vida?

2

Y oí una cosa completamente opuesto, que no se debe utilizar getopt, pero el getopts incorporado.

Cross-platform getopt for a shell script

Nunca use getopt (1). getopt no puede manejar cadenas de argumentos vacíos, o argumentos con espacios en blanco incrustados. Por favor, olvide que alguna vez existió.

El shell POSIX (y otros) ofrecen getopts que es seguro de usar en su lugar.

Cuestiones relacionadas