Por que verificar se existe uma pasta usando -d em uma string vazia retorna true?

8

Eu estava escrevendo alguns scripts e escrevi algo como

ARTIFACTS="/SOME/PATH"
[ -d $ARTIFCATS ] && rm -rf $ARTIFACTS/*

O que aconteceu é que, por estupidez, executei a segunda linha sem executar a primeira. Acontece que [-d ""] retorna verdadeiro e a expressão se torna

rm -rf /*

Felizmente, era apenas uma máquina de teste e eu não era um sudo, mas apesar de ter perdido alguns dados

Minha pergunta é: por que [-d ""] retorna true ?? a documentação afirma claramente que verifica se existe um caminho e se é uma pasta

Eu resolvi o problema usando

[ -e $ARTIFACTS ]
o que parece funcionar

Felicidades

Moataz Elmasry
fonte
5
Ou talvez você tenha executado as duas linhas. No exemplo de código acima, você nunca define ARTIFCATS.
Buhb
2
Eu apenas escreveria isso rm -rf $ARTIFACTSsem o /*. Isso também excluiria o $ARTIFACTSdiretório, o que é bom, porque se eu quiser ter certeza de que ele existe antes de colocar algo nele, executarei de mkdir -p $ARTIFACTSqualquer maneira. Ele também excluirá os arquivos ocultos $ARTIFACTS, o que também é bom, porque eu não escreveria rm -rf $ARTIFACTS/*se $ARTIFACTScontivesse algo que quisesse salvar.
Christoffer Hammarström
@ ChristofferHammarström very true
Moataz Elmasry

Respostas:

9

1. Esses dois testes retornam true :

# [ -d ] && echo true || echo false
true
# [ -d $SOME_UNSET_VAR ] && echo true || echo false
true

de acordo com o POSIX (conforme explicado pelo @Tim).

2. Mas isso retorna falso ( não é verdade, como indicado na pergunta)

# [ -d "" ] && echo true || echo false
false

porque testé chamado com dois argumentos (embora o segundo seja uma sequência vazia).

3. É por isso que é uma boa prática usar, em [[ … ]]vez de test( [ … ]), que a maioria das conchas atuais (todas?) Fornece. Essa construção verifica se você fornece argumentos suficientes (caso contrário, gera um erro e aborta)

# [[ -d ]] && echo true || echo false
bash: unexpected argument `]]' to conditional unary operator
bash: syntax error near `]]'

ou simplesmente se comporta como se poderia esperar:

# [[ -d $SOME_UNSET_VAR ]] && echo true || echo false
false

4. E, como apontado por @Gilles, ainda mais importante é a substituição de aspas duplas. Portanto, -d "$SOME_UNSET_VAR"expande -d ""e retorna falso, mesmo com test(igual ao caso 2). Portanto, isso também é compatível com o shell Bourne sh:

# [ -d "$SOME_UNSET_VAR" ] && echo true || echo false
false

testado com o bash 3.00.16 (1) e 4.1.5 (1)

mpy
fonte
11
Bash, ksh e zsh fornecem, [[ … ]]mas não são simples sh. A prática realmente importante é colocar aspas em torno substituições de comando : [ -d "$ARTIFACTS" ].
Gilles 'SO- stop being evil' em
6

Esta pergunta já foi respondida no StackOverflow. Ele diz que, de acordo com o padrão POSIX, testsempre deve retornar bem-sucedido se for chamado com exatamente um argumento não vazio (e nenhum outro argumento).

Esse também deve ser o caso test -e(e de fato está no meu sistema), portanto, tenha cuidado.

Em vez disso, use:

[ -d "$ARTIFACTS" ]

test será chamado com dois argumentos, mesmo que a variável esteja vazia e retorne false nesse caso.

Tim
fonte
"exatamente um argumento não vazio." - este é um resumo enganoso - a página que você vinculou menciona ser chamada com exatamente um argumento e esse argumento não estar vazio; nada sobre o caso de um argumento não vazio seguido por um argumento vazio. A solução correta deve ser colocar o nome da variável entre aspas.
precisa saber é o seguinte
@ Random832 Obrigado! Eu implementei as duas alterações sugeridas.
Tim
[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]não é melhor: apenas atende ao caso específico quando $ARTIFACTSestá vazio, mas falha quando $ARTIFACTSpode conter espaços em branco ou \[?*. [ -d "$ARTIFACTS" ]é a maneira correta de fazer isso (ou [[ -d $ARTIFACTS ]]em conchas que possuem [[ … ]]).
Gilles 'SO- stop be evil'
@Gilles Você está certo, eu o removi. Obrigado!
Tim
4

Resolvi o problema usando [-e $ ARTIFACTS], que parece funcionar

Você está errado . Funciona porque $ARTIFACTSagora está definido como algo.

Quando uma variável não está definida, então diz

[ -d $SOMEVAR ]

ou

[ -e $SOMEVAR ]

seria tanto avaliar a trueporque implica dizer

[ -d ]

e

[ -e ]

respectivamente. (Dizendo [ foobar ]sempre avaliaria como verdadeiro.)

Dizendo

set -u

é útil nessas situações. help setdiria a você:

  -u  Treat unset variables as an error when substituting.
devnull
fonte
[-e ""] && echo "IMPRIMIR ALGO" não imprime nada, então como isso pode estar errado?
Moataz Elmasry
2
ahh porcaria [-e $ ARTIFACTS] com rendimentos artefatos vazias [-e] não [-e ""], entendeu
Moataz Elmasry
4

Veja que você configurou a variável ARTIFACTS e estava verificando ARTIFCATS. Provavelmente digitando errado?

De qualquer forma, -d e -e produziriam os mesmos resultados em variáveis ​​não definidas.

Portanto, use aspas duplas e ele irá ajudá-lo.

ARTIFACTS="/SOME/PATH"
[ -d "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS/"*

NOTA: Se o seu "/ SOME / PATH" tiver alguma pasta com espaço, o script que você mencionou será interrompido com o erro "esperado pelo operador binário".

Exemplo:

ARTIFACTS="/home backup/"

1) [ -d $ARTIFACTS ] && rm -rf $ARTIFACTS/*
bash: [: /home: binary operator expected

2) [ -d "$ARTIFACTS" ] && rm -rf "$ARTIFACTS"/*

vai dar certo. Não se esqueça de colocar aspas na rminvocação também ( rm -rf $ARTIFACTSremoveria com alegria /homee reclamaria por backup/*não existir).

Além disso, incluir -Lcheck garantirá que seja um diretório e não apenas um link simbólico para um diretório.

Então, basicamente,

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*
thiruvenkadam
fonte