Digamos que eu tenha o seguinte código:
# Check if the color prompt is enabled and supported on this system
if [ -n "$force_color_prompt" ] && [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
GREEN="\033[1;32m"
DIM="\033[2m"
RESET="\033[00m"
fi
echo -e "Oh, ${GREEN}green${RESET} world, ${DIM}don't desert me now...${RESET}"
Se o suporte a cores estiver ativado, ele ecoará uma linha bonita e colorida. Se o suporte a cores não estiver habilitado, valores como ${GREEN}
não serão definidos e o texto será impresso no branco usual com fundo preto.
O código se baseia no fato de que variáveis que não estão definidas serão simplesmente avaliadas como uma string vazia (o que acontece em meus testes). Isso causará bugs ou problemas em alguns sistemas, ou todas as variáveis inexistentes serãosempreavaliar como uma string vazia? Existe alguma razão para eu não confiar neste mecânico?
Responder1
Variáveis inexistentes sempre serão avaliadas como uma string vazia quando expandidas como $FOO
ou (equivalentemente) ${FOO}
, e não há mal nenhum em depender disso, exceto em um caso específico:
Se alguém tiver chamado set -u
o shell atual antes de você tentar usar essa variável, ele ativou esta configuração:
-u Trate variáveis não definidas como um erro ao executar parâmetros expansão contínua. Se a expansão for tentada em um local não definido variável, o shell imprime uma mensagem de erro e, se não interativo, sai com status diferente de zero.
Isso significa que se você estiver escrevendo uma função projetada para ser originada em um script controlado por outra pessoa, talvez seja necessário ficar paranóico ao usar variáveis não definidas - caso contrário, se elas forem usadas set -u
antes de chamar sua função, o script delas sairia com uma mensagem de erro na primeira vez que você tentasse expandir uma variável não definida.
Se você estiver escrevendo seu próprio script, não há mal nenhum em contar com variáveis não definidas expandindo para a string vazia.
EDITAR- Além disso, apenas uma ideia - já que você está condicionando tudo ao fato de os recursos de cores do terminfo estarem disponíveis para o seu terminal, por que não usar o terminfo para gerar as sequências, em vez de codificar os valores vt100? Algo como:
if [ -n "$force_color_prompt" ] && type tput &>/dev/null; then
GREEN="$(tput setaf 2)$(tput bold)"
DIM="$(tput dim)"
RESET="$(tput sgr0)"
fi
Isso pode lhe proporcionar alguma portabilidade entre outros terminais (embora, reconhecidamente, o número de terminais que não usam os códigos que você mostrou seja pequeno e cada vez menor). Também pode perder alguma portabilidade, pois alguns recursos podem não existir em algumas plataformas, dependendo de quão corretas são as definições do terminfo. YMMV.
Responder2
Um dos recursos mais exclusivos da linguagem de script de shell compatível com POSIX éexpansão de parâmetros. Ele pode ser usado de diversas maneiras para realizar tarefas que não são comumente associadas a valores de variáveis. No shell, uma variável tem potencial para ser mais do que apenas um valor - ela pode ser um item acionável. Ele tem potencial para se testar. E isso vem explicitamente - sem qualquer necessidade de definir opções de shell.
Por exemplo, seu código poderia ser parecido com:
N= ERR='error encountered - exiting'
: ${force_color_prompt?"$ERR"}
/usr/bin/tput setaf >/dev/null 2>&1 || ${N:?"$ERR"}
: "${GREEN:=\033[1;32m}" "${DIM:=\033[2m}" "${RESET:=\033[00m}"
printf %b\\n \
"Oh, ${GREEN}green${RESET} world, ${DIM}don't desert me now...${RESET}"
A $N
variável é explicitamente definida como a string nula e, portanto, quando é avaliada com a ${N:?}
forma de expansão de parâmetro, seu shell pai é encerrado automaticamente e a instrução a seguir ?
também é avaliada para expansão, cujos resultados são gerados em stderr
. O mesmo vale para $force_color_prompt
- se não estiver definido, o script sai com um erro e sai $ERR
para stderr
- tudo automaticamente.
Os $GREEN
$RESET
e $DIM
serão definidos com os valores que você definiu se não estiverem definidos no momento ou se estiverem definidos como a ''
sequência nula. Isso permite que você passe seus valores para o script como variáveis de ambiente. Por exemplo, se o trecho acima estivesse em um script chamado greenworld.sh
e eu o chamasse assim:
GREEN="$(tput setaf 2)$(tput bold)" greenworld.sh
Então $GREEN
não seria redefinido no conteúdo do script e, em vez disso, herdaria o valor explícito que defini para ele. Isso torna os scripts de shell flexíveis.
E usar tput
dessa forma, como godlygeek recomendou, é uma recomendação que eu, por exemplo, em segundo lugar.
No shell, uma variável não definida às vezes pode ser tão útil quanto uma variável definida. Aqui está um exemplo diferente:
set -- *
while ${1+:} false ; do
#do stuff until all positionals are shifted away
shift ; done
Nesse exemplo, desde que o primeiro parâmetro seja definido, ele será expandido para o :
nulo interno do shell, o que consequentemente tornará a false
invocação seguinte um ambiente autônomo. Mas assim que todos os parâmetros posicionais forem shift
eliminados, ${1}
não se expande dessa forma e false
é invocado, e o while
loop termina. Você pode fazer inúmeras variações disso.
Responder3
No uso comum, ou seja, quando você está apenas expandindo variáveis, uma variável não definida é tratada como vazia em todos os shells estilo Bourne/POSIX. A exceção é quando set -o unset
aka set -u
está em vigor, caso em que o shell gerará um erro se você tentar acessar o valor de uma variável não definida (vejaresposta do godlygeek).
Existem maneiras de testar se uma variável não está definida ou está vazia; por exemplo, a construção ${foo-bar}
se expande para o valor de foo
if it is set (mesmo que esteja vazio) e para bar
if foo
is unset;${foo:-bar}
(com dois pontos extras) trata uma variável vazia como se ela não estivesse definida. Outras construções de expansão semelhantes ${foo+bar}
se ${foo?bar}
comportam ${foo=bar}
de maneira semelhante. Uma variável vazia também aparecerá na saída de set
e export
(se exportado), entre outras diferenças. Você só encontrará essas diferenças se quiser.
No entanto, há uma razão diferente parasempre inicialize variáveis: como você sabe que a variável está realmente não definida? O chamador pode ter definido uma variável semelhante para seus próprios propósitos. Se o chamador for alguma outra parte do mesmo script, então seu uso em uma função substituirá o do chamador, a menos que você declare a variável como local (o que só é possível em ksh/bash/zsh), portanto, conflitos de nomes de variáveis são um problema de qualquer forma. Mas também pode acontecer que a variável esteja presente no ambiente, porque outra pessoa escolheu o mesmo nome de variável que você. Existe uma espécie de convenção para usar nomes em letras minúsculas para variáveis de shell e nomes em letras maiúsculas para variáveis de ambiente, mas isso não resolve todos os conflitos e não é seguido universalmente.
$ exportar DIM=1 SOMA=42 $ festa Oh, mundo verde, não me abandone agora...