Dlaczego [-n] nie jest fałszywy jak [-n „”]?


20

Moje pytanie dotyczy wartości zwracanych przez ten kod:

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

To drukuje true.

Jego uzupełniający test wykorzystujący [ -z ]również odbitki true:

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

Dlaczego w powyższym kodzie [ -n ]test zakłada, że ​​wartość ciągu nie jest wcale przekazywana, ponieważ nie jest zerowa?

Poniższy kod zostanie wydrukowany false. Jest to oczekiwane, ponieważ przekazana wartość ciągu jest zerowa i ma zerową długość.

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

Odpowiedzi:


14

[x] jest równoważne [ -nx,] nawet jeśli x zaczyna się od -pod warunkiem, że nie ma argumentu.

$ [ -o ] ; echo $?
0
$ [ -eq ] ; echo $?
0
$ [ -n -o ] ; echo $?
0
$ [ -n -eq ] ; echo $?
0

4
Zauważ, że historycznie [ -t ]testowałem, czy stdout był terminalem (skrót od [ -t 1 ]), a niektóre powłoki wciąż to robią (w przypadku, ksh93gdy -tjest to dosłowne), więc lepiej jest użyć [ -n "$var" ]niż [ "$var" ]. Chociaż nadal nie udałoby się to w niektórych starych testimplementacjach dla wartości $varpodobnych =, w którym to przypadku [ "" != "$var" ]lub [ "x$var" != x ]lub case $x in "")...może być lepsza.
Stéphane Chazelas

3
Ciekawy wybór -otutaj. W niektórych powłokach, takich jak bash, -ojest zarówno jednoargumentowy (sprawdź, czy ustawiono opcję), jak i binarny (lub), więc rzeczy takie [ ! -o monitor ]są niejednoznaczne. Jest to badanie, że monitoropcja ta nie jest ustawiona, albo, że albo !czy monitorsą niepuste ciągi? Decydują reguły POSIX: te ostatnie (z czym to się różni [[ ! -o monitor ]]).
Stéphane Chazelas

30

[ -n ]nie korzysta z -ntestu.

-nW [ -n ]nie jest test w ogóle. Gdy między [i jest tylko jeden argument ], argument ten jest testowanym ciągiem, aby sprawdzić, czy jest pusty. Nawet jeśli ten ciąg ma wiodące -, nadal jest interpretowany jako operand, a nie test. Ponieważ łańcuch -nnie jest pusty - zawiera dwa znaki, -i n, nie zerowy characters-- [ -n ]wartość true.

Jak mówi Ignacio Vazquez-Abrams , gdzie stringjest pojedynczy argument, test przeprowadzony na stringin jest taki sam jak test przeprowadzony na nim . Kiedy tak się dzieje , nic specjalnego się nie dzieje. W a drugi w to po prostu ciągi testowane na pustkę.[ string ][ -n string ]string-n-n[ -n ]-n[ -n -n ]

Gdy pomiędzy [i jest tylko jeden argument ], argument ten jest zawsze ciągiem do przetestowania pod kątem niełaskawości, nawet jeśli przypadkiem nazywa się go tak samo jak test. Podobnie, gdy istnieją dwa argumenty pomiędzy [i ]i pierwszy z nich jest -n, drugi jest zawsze ciągiem do przetestowania pod kątem nieuprzejmości, nawet jeśli zdarza się, że nazywa się tak samo jak test. Jest tak po prostu dlatego, że składnia for [nalega, aby pojedynczy argument pomiędzy [i ]po -nbył operandem łańcuchowym.

Z tego samego powodu, [ -n ]który nie korzysta z -ntestu, [ -z ]nie korzysta z -ztestu.


Możesz dowiedzieć się więcej na temat [w bashbadając pomoc dla niego. Zauważ, że jest to wbudowana powłoka :

$ type [
[ is a shell builtin

Możesz więc uruchomić, help [aby uzyskać pomoc:

$ 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 `['.

Aby uzyskać więcej informacji, w tym o tym, jakie testy są obsługiwane i jak one działają, będziesz musiał zobaczyć pomoc na temat test. Po uruchomieniu polecenia help testotrzymasz szczegółową listę. Zamiast reprodukować wszystko, oto część o operatorach łańcuchów:

      -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.

Zauważ to -n STRINGi po prostu STRINGzrób to samo: sprawdzają, czy łańcuch STRINGnie jest pusty.


2
Dobre wyjaśnienie
Sergiy Kolodyazhnyy

1
Być może wspomnę również, że ma to wpływ na nieustawione zmienne niewymienione, takie jak: paste.ubuntu.com/25831047
Sergiy Kolodyazhnyy

1
@SergiyKolodyazhnyy Myślę, że może być lepiej w osobnej odpowiedzi. Zrobią to zmienne, które nie są ustawione, ustawione, ale puste lub zawierają tylko IFSbiałe znaki; zmienne zawierające wiele słów zamiast zera dają inny efekt i mogą spowodować uruchomienie zupełnie innego testu. Ciągi, które pojawiają się dosłownie z zamiarem bycia operandami, będą działać poprawnie tylko wtedy, gdy nie będą zawierały spacji ani tabulatorów, lub jeśli będą cytowane. Odpowiedź stałaby się znacznie dłuższa i bardziej skomplikowana, gdybym omawiał ten temat w przystępny sposób. Jeśli zdecydujesz się opublikować odpowiedź, nie krępuj się tutaj @ mnie!
Eliah Kagan

@Eliah Kagan, twoja odpowiedź jest bardziej kompletna i szczegółowa, chociaż odpowiedź Ignacio Vazquez-Abramsa odpowiedziała na moje pytanie.
Ravi Kumar

1
Wydaje mi się, że nie rozumiesz, dlaczego tak jest i musi być w ten sposób: jeśli nie, [ "$var" ]nigdy nie byłby użyteczny jako test, ponieważ $varmógłby się rozwinąć -n.
R ..

12

[ -n ]jest prawdą, ponieważ [polecenie (inaczej testpolecenie) działa na podstawie podanej liczby argumentów. Jeśli podano tylko jeden argument, wynikiem jest „prawda”, jeśli argument jest niepustym łańcuchem. „-n” jest łańcuchem zawierającym 2 znaki, niepustym, dlatego „prawda”.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.