Emita um erro ao usar variáveis ​​de shell vazias

22

Às vezes eu uso, $PROJECT_HOME/*para excluir todos os arquivos do projeto. Quando a variável de ambiente PROJECT_HOMEnão está definida (como eu fiz sue o novo usuário não tem essa variável de ambiente definida), ela começa a excluir todos os arquivos da pasta raiz. Isso é apocalíptico.

Como posso configurar bashpara gerar erro quando uso uma variável de ambiente indefinida no shell?

Comunidade
fonte
5
set -ufará o que você quiser.
precisa saber é
você pode fazer isso como a resposta?
2
Claro que você não inicializaria seus vars em cadeias vazias. [ -z "$VAR" ]trabalha com um não inicializado VARtambém. A inicialização foi apenas para mostrar o comportamento indesejável - o que quero dizer é que, se seus vars forem inicializados em cadeias vazias, de qualquer maneira, e você continuar rm -r "$PROJECT_HOME"/*confiando erroneamente set -u, obterá o comportamento "apocalíptico". IHMO, é melhor prevenir do que remediar quando se trata de proteger todo o conteúdo do seu computador. set -unão é seguro.
PSkocik
3
"É muito longo"? Você não deve procurar uma maneira conveniente de executar manualmente operações perigosas. Em vez disso, você deve criar uma função, alias ou script para fazer o que quiser; nesse caso, criar um alias do comando sugerido do @ PSkocik será seguro e conveniente.
Kyle Strand
1
E se o usuário definir PROJECT_HOME=/etc? Apenas verificar se há um valor vazio não é suficiente para impedir o cataclismo. Você não deve usar variáveis ​​de usuários não confiáveis ​​ao executar como root.
Barmar

Respostas:

27

No shell POSIX, você pode usar set -u :

#!/bin/sh

set -u
: "${UNSET_VAR}"

ou usando a expansão de parâmetro :

: "${UNSET_VAR?Unset variable}"

No seu caso, você deve usar em :?vez de ?também falhar em variáveis ​​definidas, mas vazias:

rm -rf -- "${PROJECT_HOME:?PROJECT_HOME empty or unset}"/*
cuonglm
fonte
17
[ -z "$PROJECT_HOME" ] || rm -r "$PROJECT_HOME"/*

Isso também captura o caso em que PROJECT_HOME está definido, mas não contém nada.

Exemplo:

1) Isso excluirá praticamente tudo o que você pode excluir em seu sistema (exceto arquivos de ponto dentro /(normalmente não existem)):

set -u
PROJECT_HOME=
rm -r "$PROJECT_HOME"/*

2) Isso não fará nada:

PROJECT_HOME=
[ -z "$PROJECT_HOME" ] || rm -r "$PROJECT_HOME"/* 

Remover completamente a página inicial do projeto e recriá-la pode ser outra opção (se você também quiser se livrar dos dotfiles):

#no apocalyptic threats in this scenario
rm -r "$PROJECT_HOME"
mkdir "$_" 
PSkocik
fonte
1
-zestá ok, mas como $PROJECT_HOMEdeveria ser um diretório, talvez -dfosse melhor. [[ -d $PROJECT_HOME ]] && rm -r "$PROJECT_HOME".
kojiro
2
Se PROJECT_HOME estiver definido como um não-diretório, será um erro não catastrófico, possivelmente devido a um erro de digitação. A -dverificação ocultaria esse erro. Eu acho que é melhor se continuar rme rmreclamar sobre isso em voz alta.
PSKocik
2
Se PROJECT_HOME estiver definido, mas o nome não for um diretório, [[ -d $PROJECT_HOME ]] && rm -r "$PROJECT_HOME"nada fará silenciosamente. Mas [[ -z $PROJECT_HOME ]] || rm -r "$PROJECT_HOME"excluirá silenciosamente "$ PROJECT_HOME", mesmo que seja um arquivo que não seja um diretório. Receber uma mensagem de erro nunca é um problema:if [[ -d $PROJECT_HOME ]]; then rm -r "$PROJECT_HOME"; else printf '%s is not a directory\n' "$PROJECT_HOME" >&2; fi
kojiro
1
Agora "acidentalmente" set PROJECT_HOME="/."...
Hagen von Eitzen
1
@HagenvonEitzen Se PROJECT_HOME estiver definido como raiz, o procedimento que esvazia PROJECT_HOME esvaziará a raiz. Esse é o comportamento esperado e perfeitamente razoável. E você nem precisa desse ponto final.
PSKocik
0

Outra maneira de fazer isso:

rm -r "${somevar:-/tmp/or_this_if_somevar_is_empty}"/*

Existem muitas substituições de variáveis, a acima é quando "somevar" está vazio (e, nesse caso, ele tenta excluir /tmp/or_this_if_somevar_is_empty/*)

Olivier Dulac
fonte
1
Ou: rm -fr ${ENV_VAR:?suitably caustic message here}/*, mas ainda pode valer a pena verificar que o valor não mapeia para o diretório raiz, observando que há muitas maneiras de subverter testes simples: //, /.., /usr/who/../.., ...
Jonathan Leffler
Sim. Se você estiver indo para usar uma expansão de parâmetros, assim como você pode usar o que realmente gera um erro
Trauma Digital