Há algum mal em usar variáveis ​​que não estão definidas?

Há algum mal em usar variáveis ​​que não estão definidas?

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 $FOOou (equivalentemente) ${FOO}, e não há mal nenhum em depender disso, exceto em um caso específico:

Se alguém tiver chamado set -uo 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 -uantes 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 $Nvariá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 $ERRpara stderr- tudo automaticamente.

Os $GREEN $RESETe $DIMserã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.she eu o chamasse assim:

GREEN="$(tput setaf 2)$(tput bold)" greenworld.sh

Então $GREENnã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 tputdessa 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 falseinvocação seguinte um ambiente autônomo. Mas assim que todos os parâmetros posicionais forem shifteliminados, ${1}não se expande dessa forma e falseé invocado, e o whileloop 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 unsetaka set -uestá 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 fooif it is set (mesmo que esteja vazio) e para barif foois 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 sete 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...

informação relacionada