Por que [-n] não é falso como [-n “”]?

20

Minha pergunta é sobre os valores de retorno produzidos por este código:

if [ -n ]; then echo "true"; else echo "false"; fi

Isso imprime true.

Seu teste complementar usando [ -z ]também imprime true:

if [ -z ]; then echo "true"; else  echo "false"; fi

No código acima, por que o [ -n ]teste assume o valor da sequência que não é passado, como não nulo?

O código abaixo é impresso false. Isso é esperado, já que o valor da cadeia passada é nulo e tem tamanho zero.

if [ -n "" ]; then echo "true"; else  echo "false"; fi
Ravi Kumar
fonte

Respostas:

14

[x] é equivalente a [ -nx] mesmo que x comece com -desde que não haja operando.

$ [ -o ] ; echo $?
0
$ [ -eq ] ; echo $?
0
$ [ -n -o ] ; echo $?
0
$ [ -n -eq ] ; echo $?
0
Ignacio Vazquez-Abrams
fonte
4
Nota que, historicamente, [ -t ]foi testar se stdout foi um terminal (curto para [ -t 1 ]) e algumas conchas ainda estão fazendo isso (no caso de ksh93apenas quando esse -té literal), por isso é melhor usar [ -n "$var" ]do que [ "$var" ]. Embora isso ainda falhe em algumas testimplementações antigas para valores de $varlike =, nesse caso [ "" != "$var" ]ou [ "x$var" != x ]ou case $x in "")...pode ser melhor.
Stéphane Chazelas
3
Escolha interessante -odaqui. Em alguns shells bash, -oé um operador unário (teste se uma opção está configurada) e binário (ou), portanto, coisas como [ ! -o monitor ]são ambíguas. Está testando se a monitoropção não está definida ou se são !ou monitornão cadeias de caracteres vazias? As regras POSIX decidem: a última (que é diferente com [[ ! -o monitor ]]).
Stéphane Chazelas
30

[ -n ]não usa o -nteste.

O -nin [ -n ]não é um teste. Quando existe apenas um argumento entre [e ], esse argumento é uma sequência testada para verificar se está vazia. Mesmo quando essa sequência tem uma liderança -, ela ainda é interpretada como um operando, não como um teste. Como a cadeia -nnão está vazia - ela contém dois caracteres -e n, não zero, de caráteres [ -n ]avaliada como verdadeira.

Como diz Ignacio Vazquez-Abrams , onde stringhá um único argumento, o teste realizado stringem é o mesmo que o realizado por ele . Quando acontece , nada de especial acontece. O in e o segundo in são simplesmente cadeias sendo testadas quanto ao vazio.[ string ][ -n string ]string-n-n[ -n ]-n[ -n -n ]

Quando existe apenas um argumento entre [e ], esse argumento é sempre uma string a ser testada para verificar se não há vazio, mesmo que seja nomeado o mesmo que um teste. Da mesma forma, quando há dois argumentos entre [e ]o primeiro deles -n, o segundo é sempre uma sequência a ser testada para verificar se não há vazios, mesmo que seja nomeado o mesmo que um teste. Isso ocorre simplesmente porque a sintaxe para [insiste em que um único argumento entre [e ]depois-n é um operando de string.

Pelo mesmo motivo que [ -n ]não usa o -nteste, [ -z ]não usa o -zteste.


Você pode aprender mais sobre [a bashexaminando a ajuda para ele. Observe que é um shell embutido :

$ type [
[ is a shell builtin

Assim, você pode executar help [para obter ajuda:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

Para obter mais informações, incluindo quais testes são suportados e como eles funcionam, você precisará ver a ajuda test. Quando você executa o comando help test, você obtém uma lista detalhada. Em vez de reproduzir tudo, aqui está a parte sobre os operadores de string:

      -z STRING      True if string is empty.

      -n STRING
         STRING      True if string is not empty.

      STRING1 = STRING2
                     True if the strings are equal.
      STRING1 != STRING2
                     True if the strings are not equal.
      STRING1 < STRING2
                     True if STRING1 sorts before STRING2 lexicographically.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.

Observe isso -n STRINGe STRINGfaça o mesmo: eles testam se a string STRINGnão está vazia.

Eliah Kagan
fonte
2
Boa explicação #
Sergiy Kolodyazhnyy 27/1017
1
Talvez também mencione que isso tem implicações para variáveis ​​não citadas que estão desmarcadas, como esta: paste.ubuntu.com/25831047
Sergiy Kolodyazhnyy 27/10/17
1
@SergiyKolodyazhnyy Acho que pode ser melhor em uma resposta separada. Variáveis ​​não definidas, ou definidas, mas vazias, ou que contenham apenas IFSespaços em branco, farão isso; variáveis ​​que contêm várias palavras em vez de zero produzem outro efeito e podem fazer com que um teste completamente diferente seja executado. As strings que aparecem literalmente com a intenção de serem operandos somente funcionarão corretamente se não contiverem espaços ou tabulações ou se estiverem entre aspas. A resposta se tornaria muito mais longa e mais complicada se eu abordasse esse tópico de uma maneira acessível. Se você decidir postar uma resposta, sinta-se à vontade para @ me aqui sobre isso!
Eliah Kagan 27/10
@Eliah Kagan, sua resposta é mais completa e detalhada, embora a resposta de Ignacio Vazquez-Abrams tenha respondido à minha pergunta.
Ravi Kumar
1
Acho que você está perdendo o motivo e deve ser assim: caso contrário, [ "$var" ]nunca seria útil como teste, pois $varpode se expandir para -n.
R ..
12

[ -n ]é verdadeiro porque o [comando (também conhecido como testcomando) atua de acordo com o número de argumentos que é fornecido. Se ele receber apenas um único argumento, o resultado será "verdadeiro" se o argumento for uma sequência não vazia. "-n" é uma sequência com 2 caracteres, não está vazia, portanto, "verdadeira".

Glenn Jackman
fonte