Se eu tivesse um script que define variáveis somente leitura para alguns valores ímpares e define errexit
por causa de outras operações inseguras:
#!/bin/bash
set -e
declare -r NOTIFY=$(case "$OS" in (macosx) echo macos_notify ;; (linux) echo linux_notify ;; (*) echo : ;; esac)
declare -r SAY=_say # _say is a function
declare -r VERSION=0.99
set +e
E eu a fonte para obter as definições, na segunda vez porque está em desenvolvimento:
$ . s.bash
$ . s.bash
bash: declare: NOTIFY: readonly variable
Exited
Normalmente declare -r EXISTING_VAR
, não pararia o script nem removeria a definição antiga e funcional de EXISTING_VAR
.
Mas com errexit
, atribuir a uma variável existente é compreensivelmente uma falha. As opções fáceis são remover -r
ou usar set +e
essa parte do script.
Exceto isso, é possível escrever uma função Bash para substituir, declare -r
mas não reatribuir, se o nome já existir ?
Eu tentei:
# arg #1: var name, #2: value
set_var_once () {
# test whether the variable with the
# name stored in $1 exists
if [[ -z "${!1}" ]]
then # if it doesn't, set it
declare -r $1=$2
fi
}
Eu também tentei coisas ao longo das linhas de eval "declare -r $1=$(eval $2)"
, parece que eval
é necessário em algum lugar aqui, mas não tenho certeza de onde.
Todas as versões de set_var_once
resultam em não definir a variável que deveriam.
declare -r
torna uma variável somente leitura, mas também a declara no escopo atual e, portanto, a torna local para a função atual. Em vez disso, você gostariareadonly
que apenas o primeiro:Para ser usado como:
Observe que, ao contrário de
readonly
, issoreadonly_once
não é uma palavra-chave (sim, tambémreadonly
é uma palavra-chave, embora mantenha esse fato oculto), que precisa ser citado para evitar split+glob, não é uma atribuição nesse ponto.bash
$(cmd)
$(cmd)
será expandido (e entãocmd
executado) mesmo que o valor acabe não sendo atribuídoVAR2
se já estiver definido.Essa função só funciona para variáveis escalares, não arrays nem arrays associativos.
Se o seu shell for bash, você pode usar o
-v
test
:Por exemplo
Ou use a
${param:=value}
expansão e o:
comandodemonstrando:
Eu encontrei o seguinte para ser útil
O AbortScript é outra função que uso. Ele apenas imprime a mensagem de erro e sai do script.