bash function arguments comportamento estranho

2

Eu tenho a seguinte função bash:

lscf() {
  while getopts f:d: opt ; do
    case $opt in
      f) file="$OPTARG" ;;
      d) days="$OPTARG" ;;
    esac
  done

  echo file is $file
  echo days is $days
}

Rodar isto com argumentos não produz nenhum valor. Somente depois de executar a função sem argumentos, e novamente com argumentos, ela exibe os valores corretos:

-bash-4.1$ lscf -d 10 -f file.txt
file is
days is

-bash-4.1$ lscf
file is
days is

-bash-4.1$ lscf -d 10 -f file.txt
file is file.txt
days is 10

Estou sentindo falta de algo?

    
por Subbeh 09.04.2018 / 06:29

1 resposta

3

Embora eu não possa reproduzir a execução inicial da função que você tem em sua pergunta, você deve redefinir OPTIND para 1 em sua função para poder processar a linha de comando da função em invocações repetidas dela.

Do manual bash :

OPTIND is initialized to 1 each time the shell or a shell script is invoked. When an option requires an argument, getopts places that argument into the variable OPTARG. The shell does not reset OPTIND automatically; it must be manually reset between multiple calls to getopts within the same shell invocation if a new set of parameters is to be used.

De o padrão POSIX :

If the application sets OPTIND to the value 1, a new set of parameters can be used: either the current positional parameters or new arg values. Any other attempt to invoke getopts multiple times in a single shell execution environment with parameters (positional parameters or arg operands) that are not the same in all invocations, or with an OPTIND value modified to be a value other than 1, produces unspecified results.

A "invocação de shell" que o manual bash menciona é o mesmo que o "ambiente de execução única" mencionado pelo texto POSIX, e ambos se referem ao seu shell script ou shell interativo. No script ou no shell interativo, várias chamadas para seu lscf invocarão getopts no mesmo ambiente e OPTIND precisará ser redefinido para 1 antes de cada chamada.

Portanto:

lscf() {
  OPTIND=1

  while getopts f:d: opt ; do
    case $opt in
      f) file="$OPTARG" ;;
      d) days="$OPTARG" ;;
    esac
  done

  echo file is $file
  echo days is $days
}

Se as variáveis file e days não não estiverem definidas no ambiente do shell de chamada, elas devem ser variáveis locais. Além disso, cite expansões de variáveis e use printf para gerar dados variáveis:

lscf() {
  local file
  local days

  OPTIND=1

  while getopts f:d: opt ; do
    case $opt in
      f) file="$OPTARG" ;;
      d) days="$OPTARG" ;;
    esac
  done

  printf 'file is %s\n' "$file"
  printf 'days is %s\n' "$days"
}
    
por 09.04.2018 / 07:41