Eu escrevi uma função POSIX semelhante, mas isso não arrisca a execução de código arbitrário:
unexport()
while case ${1##[0-9]*} in ### rule out leading numerics
(*[!_[:alnum:]]*|"") ### filter out bad|empty names
set "" ${1+"bad name: '$1'"} ### prep bad name error
return ${2+${1:?"$2"}} ### fail w/ above err or return
esac
do eval set '"$'"{$1+$1}"'" "$'"$1"'" "$'@\" ### $1 = ( $1+ ? $1 : "" )
eval "${1:+unset $1;$1=\;} shift 3" ### $$1 = ( $1:+ ? $2 : -- )
done
Ele também lidará com tantos argumentos quanto quiser fornecer. Se um argumento for um nome válido que não esteja definido de outra maneira, ele será silenciosamente ignorado. Se um argumento é um nome ruim, ele grava em stderr e pára conforme apropriado, embora qualquer nome válido anterior a um inválido em sua linha de comando ainda seja processado.
Eu pensei em outro jeito. Eu gosto muito disso.
unexport()
while unset OPTARG; OPTIND=1 ### always work w/ $1
case ${1##[0-9]*} in ### same old same old
(*[!_[:alnum:]]*|"") ### goodname && $# > 0 || break
${1+"getopts"} : "$1" ### $# ? getopts : ":"
return ### getopts errored or ":" didnt
esac
do eval getopts :s: '"$1" -"${'"$1+s}-\$$1\""
eval unset "$1; ${OPTARG+$1=\${OPTARG}#-}"
shift
done
Bem, ambos usam muitas das mesmas técnicas. Basicamente, se uma var de shell não estiver definida, uma referência a ela não será expandida com uma expansão de parâmetro +
. Mas se for definido - independentemente do seu valor - uma expansão de parâmetro como: ${parameter+word}
será expandida para word
- e não para o valor da variável. E assim, as variáveis da shell fazem o autoteste e auto-substituem no sucesso.
Eles também podem auto-reprovar . Na função principal, se um nome inválido for encontrado, movo $1
para $2
e deixo $1
null porque a próxima coisa que faço é return
success se todos os args tiverem sido processados e o loop tiver terminado ou, se o argumento for inválido, o shell expandirá o $2
para $1:?
, o que eliminará um shell com script e retornará uma interrupção para um interativo ao gravar word
em stderr.
No segundo, getopts
faz as atribuições. E não irá atribuir um nome ruim - em vez disso, escreva para gravar uma mensagem de erro padrão para stderr. Além disso, ele salva o valor do argumento em $OPTARG
se o argumento era o nome de uma variável set em primeiro lugar. Então, depois de fazer getopts
, tudo que é necessário é eval
a expansão de OPTARG
na atribuição apropriada.