Em um shell script, processe opções como -a -b mas deixe --abc sozinho?

1

Portanto, eu tenho um script de shell que faz alguma preparação e, em seguida, executa um utilitário.

A preparação pode ser afetada por opções como -a ou -n . O conteúdo da linha de comando que não são tais opções deve ser passado para o utilitário. Atualmente meu script tem:

while getopts ":an" opt; do
  case $opt in
    a)
      #something
      ;;
    n)
      #something else
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      ;;
  esac
done
shift $((OPTIND-1))
  #more prep
my_utility $@

No entanto, isso falha quando desejo passar opções de formato longo para o utilitário, como --abc , que é interpretado como a , b e c options por getopts.

Então, como posso processar opções de forma que -a ou -n seja processado, mas --abc permaneça intocado? A menos, é claro, eu despejo o Shell e o faço no Python - então as opções seriam muito fáceis, mas eu também preciso copiar arquivos e executar utilitários, e o Python torna isso estranho.

    
por Mikhail Ramendik 16.11.2018 / 01:04

2 respostas

2

Não vejo por que você não pode simular opções longas tratando - como uma opção curta que exige um argumento:

$ cat /tmp/foo
while getopts :aq:-: opt; do
        case $opt in
        a)      echo option -a;;
        q)      echo option -q with value "$OPTARG";;
        -)      case $OPTARG in
                abc)    echo option --abc;;
                def=*)  echo option --def with value "${OPTARG#*=}";;
                *)      echo >&2 "unknown option --$OPTARG";;
                esac;;
        :)      echo >&2 "-$OPTARG needs an argument";;
        *)      echo >&2 "unknown option -$OPTARG";;
        esac
done
shift "$((OPTIND - 1))"
echo argv: "$@"

$ sh /tmp/foo -a -qt --abc --def=ghi -- foo bar
option -a
option -q with value t
option --abc
option --def with value ghi
argv: foo bar

Se você quiser fazer uma lista de argumentos (por exemplo, para chamar outro comando com ele), e seu shell não suporta arrays (por exemplo, debian ou busybox /bin/sh ), você pode usar o seguinte truque, que passará para eval uma lista de índices de argumento em vez de cadeias reais; que evitará quaisquer aborrecimentos de divisão / globbing / espaço em branco do IFS:

$ cat /tmp/foo
# handle -a and -qval
# call another command with any long options and non-option arguments
av=
while getopts :aq:-: opt; do
        case $opt in
        a)      echo option -a;;
        q)      echo option -q with value "'$OPTARG'";;
        -)      av="$av \"\${$((OPTIND-1))}\"" ;; # pass long options unchanged
        :)      echo >&2 "-$OPTARG needs an argument";;
        *)      echo >&2 "unknown option -$OPTARG";;
        esac
done
i=$OPTIND; while [ "$i" -le "$#" ]; do av="$av \"\${$i}\"" i=$((i + 1)); done

print_its_args(){ for a; do printf ' {%s}' "$a"; done; echo; }
echo "print_its_args $av"
eval "print_its_args $av"

Então:

$ sh /tmp/foo -aqval --foo='a  **  b' --abc -- '(moo)'
option -a
option -q with value 'val'
print_its_args  "$2" "$3" "$5"
 {--foo=a  **  b} {--abc} {(moo)}

Esse truque pode ser usado em outras situações ; mas este é um caso em que uma solução mais simples como set -- "$@" arg para enviar argumentos para $@ não pode ser usada, porque modificar $@ dentro do loop getopts não pode ser feito de maneira portátil.

    
por 16.11.2018 / 02:42
1

Para interpretar comandos de traço duplo, você precisa do getopt do GNU em vez do getopts incorporado.

Existe uma maneira de usar o bash builtin getopts para imitar opções longas, mas você não deve fazer isso porque é um kludge e não vale a pena seu esforço pois getopts não pode reforçar o longo especificação.

Assim, seu script se torna:

#!/bin/bash

# Get options
OPTS='getopt --options an: --long a_something,n_something_else: -n 'parse-options' -- "$@"'

if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi

#echo parsed and cleaned up options
echo "$OPTS"
eval set -- "$OPTS"

bSomething=false
bSomethingElse=false

# "Endless" loop over the parsed options
while true; do
  case "$1" in
    -a | --a_something )       bSomething=true;     shift ;;
    -n | --n_something_else )  bSomethingElse=true; shift ;;
    -- )                                            shift; break ;;
    * )                                                    break ;;
  esac
done

Para mais informações: man getopt
Para obter ainda mais informações: man getopt(3)

    
por 16.11.2018 / 01:49

Tags