getopts não obtém argumentos quando a função é chamada dentro do loop while

Sep 18 2020

Estou tentando chamar uma função em um loop while passando alguns argumentos. No entanto, getoptssó pode obter os argumentos para a primeira chamada.

Aqui está um exemplo mínimo:

function add_all_external_services() {
  env | sed -n "s/^EXTERNAL_SERVICE_OPTIONS_\(.*\)$/\1/p" > options while read -r line do key="${line%%=*}"
    opt="${line#*=}" if [[ -n "$key" && -n "$opt" ]]; then echo "Adding external service \"$key\" with options: \"$opt\"" add_external_service $opt
    else
      echo "Missing one or more variables:
  - Key: \"$key\" - Options: \"$opt\"
"
    fi
  done < options

  rm options
}

function add_external_service() {
  local local_service_name=""
  local external_service_name=""
  local external_service_namespace=""
  local service_url=""

  echo "    Options: $@" while getopts l:s:n:-: OPT; do if [[ "$OPT" = "-" ]]; then   # long option: reformulate OPT and OPTARG
      OPT="${OPTARG%%=*}" # extract long option name OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty) OPTARG="${OPTARG#=}"        # if long option argument, remove assigning `=`
    fi
    case "$OPT" in l | local-service-name) needs_arg; local_service_name="$OPTARG" ;;
      s | external-service-name)        needs_arg; external_service_name="$OPTARG" ;; n | external-service-namespace) needs_arg; external_service_namespace="$OPTARG" ;;
      external-name)                    needs_arg; service_url="$OPTARG" ;; ??* ) die "Illegal option --$OPT" ;;  # bad long option
      \? )                              exit 2 ;;  # bad short option (error reported via getopts)
    esac
  done

  echo "      - local $local_service_name" echo " - name $external_service_name"
  echo "      - namespace $external_service_namespace" echo " - url $service_url"
}

Então, ao ligar:

export EXTERNAL_SERVICE_OPTIONS_A="-l local_a -s rasa -n botinstance-12424526-review-feature-ce-swdjtf"
export EXTERNAL_SERVICE_OPTIONS_B="-l local_b -s review-feature-ce-swdjtf -n botinstance-12424526-review-feature-ce-swdjtf"

ventury-deploy add_all_external_services

Eu entendi isso:

Adding external service "B" with options: "-l local_b -s name_b -n namespace_b"
    Options: -l local_b -s name_b -n namespace_b
      - local local_b
      - name name_b
      - namespace namespace_b
      - url 
Adding external service "A" with options: "-l local_a -s name_a -n namespace_a"
    Options: -l local_a -s name_a -n namespace_a
      - local 
      - name 
      - namespace 
      - url 

Peguei a getoptsparte aqui e funciona bem sempre que chamo funções fora dos loops.

Depois de ler esta pergunta , tentei adicionar um &depois de chamar a função dentro do loop, e funciona ... todos os argumentos são lidos getopts. Não consigo entender por que executar os comandos em segundo plano faria com que funcionasse.

Se eu echo $@antes de getopts, posso ver que todos os argumentos são passados ​​corretamente, se eu chamar a segunda função manualmente, uma vez para cada variável env, ela também funciona corretamente.

Então, como a execução desses comandos em segundo plano é diferente? Quer dizer, para que é diferente getopts? Além disso, por que echo $@pode ver os argumentos enquanto getoptsnão pode?

Respostas

4 PhilipCouling Sep 18 2020 at 21:00

Isso ocorre porque você não está reiniciando OPTIND. De acordo com o manual :

Cada vez que é invocado, getopts coloca a próxima opção no nome da variável shell, inicializando o nome se ele não existir, e o índice do próximo argumento a ser processado na variável OPTIND. OPTIND é inicializado em 1 cada vez que o shell ou um script de shell é chamado.

Portanto, OPTIND é usado para controlar o próximo argumento a ser processado e é definido automaticamente para 1quando seu script começa, mas NÃO é redefinido quando sua função termina.

Para corrigir seu script, basta adicionar OPTIND=1ao início de sua função:

function add_external_service() {
  OPTIND=1
  local local_service_name=""
  local external_service_name=""
  local external_service_namespace=""
  local service_url=""

  echo "    Options: $@" while getopts l:s:n:-: OPT; do if [[ "$OPT" = "-" ]]; then   # long option: reformulate OPT and OPTARG
      OPT="${OPTARG%%=*}" # extract long option name OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty) OPTARG="${OPTARG#=}"        # if long option argument, remove assigning `=`
    fi
    case "$OPT" in l | local-service-name) needs_arg; local_service_name="$OPTARG" ;;
      s | external-service-name)        needs_arg; external_service_name="$OPTARG" ;; n | external-service-namespace) needs_arg; external_service_namespace="$OPTARG" ;;
      external-name)                    needs_arg; service_url="$OPTARG" ;; ??* ) die "Illegal option --$OPT" ;;  # bad long option
      \? )                              exit 2 ;;  # bad short option (error reported via getopts)
    esac
  done

  echo "      - local $local_service_name" echo " - name $external_service_name"
  echo "      - namespace $external_service_namespace" echo " - url $service_url"
}