Como extrair argumentos desconhecidos dentro de um script de shell?

1

Eu tenho um script de shell que aceita uma variedade de opções, algumas com argumentos, outras sem, algumas curtas, outras longas. Ele deve lidar com algumas dessas opções em si e repassar o resto, ele não sabe como se preocupar com outro programa. Posso obter getopts ou algo em suas linhas para armazenar argumentos desconhecidos?

Por exemplo, digamos que meu script seja chamado de program e deva aceitar argumentos

  • -n com um único argumento,
  • --verbose sem nenhum argumento e
  • -s sem nenhum argumento.

Ele analisa e imprime todas as suas opções e seus argumentos e, em seguida, chama echo rest: com qualquer coisa que resta. A seguinte saída deve ser observada.

> program -sin42 --long --verbose
-s
-n with argument 42
--verbose
rest: -i --long 

> program -n --short
-n with argument --short

> program -n
error: -n without argument

Pode algo assim ser alcançado em um script de shell?

    
por XZS 28.04.2015 / 23:18

2 respostas

1

Eu não acho que há um método padrão (isto é, além de implementá-lo todo do zero) que estaria disponível, muito menos amplamente disponível em shells.

Embora ksh ofereça suporte a getopts embutido. Com base nisso (e em seus requisitos bastante exigentes) eu delineei uma possível solução baseada em ksh com o seguinte fragmento de código:

while getopts ":[-][[n]:][99:verbose][s]" opt
do  case $opt in
    (n) n_arg=$OPTARG ;;
    (99) verbose=1 ;;
    (s) s=1 ;;
    (*) arg_rest+=( "${@:OPTIND-1:1}" ) ;;
    esac
done
shift OPTIND-1

printf "main opt(%s)=%s\n" "-n" "$n_arg"
printf "main opt(%s)=%s\n" "--verbose" "$verbose"
printf "main opt(%s)=%s\n" "-s" "$s"

function delegate
{
    while getopts ":[-][i][98:long]" opt
    do  case $opt in
        (i) int=1 ;;
        (98) long=1 ;;
        esac
    done
    shift OPTIND-1

    printf "func opt(%s)=%s\n" "-i" "$int"
    printf "func opt(%s)=%s\n" "--long" "$long"
}

printf "Delegate: '%s'\n" "${arg_rest[@]}"
delegate "${arg_rest[@]}"

Primeiro, o programa analisa todas as opções, define as variáveis internas conforme necessário e armazena as opções desconhecidas em uma matriz. Então você vê alguns printf para controlar as configurações. Em seguida, uma definição de função para onde o restante das opções deve ser delegado; a função também pode ser substituída por um comando. Finalmente a chamada da função (ou outro comando) com o resto dos argumentos.

(Para obter uma descrição da chamada ksh ' getopts dos recursos de dentro de ksh session getopts --man .)

A execução desse programa produz esta saída:

$ ksh ./getopts_script -s -n 23 --verbose -i --long
main opt(-n)=23
main opt(--verbose)=1
main opt(-s)=1
Delegate: '-i'
Delegate: '--long'
func opt(-i)=1
func opt(--long)=1


Para uma implementação shell de uma função getopts que suporta opções longas, consulte link

    
por 29.04.2015 / 03:14
0

É tentador tentar as opções conhecidas getopt order na frente. Infelizmente, ele rejeita todos os argumentos desconhecidos e para de analisar imediatamente.

Mas, contanto que as opções longas não sejam excessivamente complexas, o getopts de bash pode ser enganado para fazer isso. Alguma inspiração foi tirada de mkaurball . As advertências seguem abaixo da fonte.

#!/bin/bash

while [ $OPTIND -le $# ]
do
  if getopts ":sn:-:" argument
  then
    case $argument in
      s) echo "-$argument" ;;
      n) echo "-$argument with argument $OPTARG" ;;
      \?) pass+=("-$OPTARG") ;;
      -) lastarg=$((OPTIND - 1))
        case "${!lastarg}" in
          --verbose) echo "--verbose" ;;
          --*) pass+=("--$OPTARG") ;;
          *) echo "invalid argument: -"
            exit 1 ;;
        esac ;;
      :) echo "$OPTARG without argument"
        exit 1 ;;
    esac
  else
    pass+=("${!OPTIND}")
    let OPTIND++
  fi
done

echo pass: "${pass[@]}"
  • Excepcionalmente, getopts s não é usado como a condição para while , o que faria com que o loop parasse assim que encontrasse qualquer parâmetro posicional, ou seja, qualquer coisa que não fosse uma opção nem um argumento para um. Em vez disso, esses casos são anexados à matriz, coletando parâmetros para pass on.
  • A especificação do parâmetro inclui :- , o que torna getopts em opções longas --long como uma opção curta - com o argumento -long . Usado sem cautela, misturar o traço no meio de compostos de opção curtos (como -s-n ) pode quebrar isso, então é melhor verificar se o parâmetro real realmente começa com dois traços. Assim, a abordagem falharia se o programa agrupado quiser ver opções como -short-with-dashes como uma opção.
  • Embora à natureza de getopts , os traços duplos também são engolidos. Isso pode ser desejado, mas também pode ser evitado inserindo [ "${!OPTIND}" != '--' ] && antes de getopts
  • A análise de argumentos longos pelo wrapper pode ser um pouco difícil, pois eles precisam ser manipulados no aninhado case .
por 29.04.2015 / 13:25