Como lidar com o fim das opções - em getopts

9

Eu uso getopts para analisar argumentos em scripts bash como

while getopts ":hd:" opt; do
  case $opt in
    d ) echo "directory = $OPTARG"; mydir="$OPTARG"; shift $((OPTIND-1)); OPTIND=1 ;;
    h ) helptext
      graceful_exit ;;
    * ) usage
      clean_up
      exit 1
  esac
done

exeparams="$*"

exeparamsmanterá quaisquer opções / argumentos não analisados. Como desejo usar exeparams para conter opções para que um comando seja executado dentro do script (que pode se sobrepor às próprias opções do script), eu quero usar - para finalizar as opções passadas para o script. Se eu passar por exemplo

myscript -d myscriptparam -- -d internalparam

exeparams vai segurar

-- -d internalparam

Agora eu quero remover o líder --para passar esses argumentos para o comando interno. Existe uma maneira elegante de fazer isso ou posso obter uma string que retenha apenas o restante sem --getopts?

highsciguy
fonte
Colocar shift; OPTIND=1dentro do getoptsloop provavelmente não é a melhor maneira de fazê-lo. Isso só funciona no seu caso, porque você tem apenas 2 opções e, nos demais, apenas sai do script. Caso contrário, você precisaria shift; OPTIND=1de todas as opções, o que significa código duplicado (prática recomendada). Basta fazer um shift $((OPTIND - 1))imediatamente após o final do loop - esta é a maneira mais convencional e provavelmente a mais eficiente também.
jw013

Respostas:

7

E se:

# ... getopts processing ...

[[ $1 = "--" ]] && shift
exeparams=("$@")

Observe que você deve usar uma matriz para armazenar os parâmetros. Isso manipulará adequadamente qualquer argumento que contenha espaço em branco. Desreferenciar a matriz com"${exeparams[@]}"

Glenn Jackman
fonte
1
Isso pressupõe que não há argumentos entre o final das opções e --, ou seja script foo -- bar, passaria foo -- barpara o programa externo. Minha resposta não faz essa suposição, pois não foi explicitamente declarada na pergunta.
Jw013
Exceto onde o OP diz "Agora eu quero remover o líder -"
glenn jackman 14/11/12
Isso foi por exemplo -- -d internalparams. De qualquer forma, minha resposta é geral o suficiente para lidar com ambos os casos.
jw013
14

Use o built-in shift. Primeiro, faça o normal getoptspara o seu script. Depois que esse loop for concluído,

shift "$((OPTIND - 1))"

irá mudar todas as opções já processadas.

A partir daí, você precisará concluir o processamento dos argumentos que não são de opção, se houver, para a primeira parte do script (antes de --). Depois de encontrar o --, desloque-o até que apenas a última parte permaneça (a -d internalparamparte que vem depois --). Uma maneira de fazer isso (usando a bashsintaxe):

while [[ $# -gt 0 ]]; do
    # process next argument
    case $1 in
    foo) # process foo
    ;;
    --) shift; break;; # found '--', discard it and exit loop
    *) # handle unrecognized argument
    ;;
    esac
    # not '--', so discard the argument and continue
    shift
done

Finalmente, apenas o segundo conjunto de opções / argumentos permanece, que você pode transmitir. Você não usar $*para passar os parâmetros restantes para outro comando. Use em "$@"vez disso, que preserva a divisão da palavra original.

external_command "$@"
jw013
fonte
Sim obrigado! Mas como exatamente eu encontro --?
highsciguy