Przetestuj ciąg znaków o niezerowej długości w Bash: [-n „$ var”] lub [„$ var”]

182

Widziałem skrypty Bash testujące ciąg znaków o niezerowej długości na dwa różne sposoby. Większość skryptów korzysta z -nopcji:

#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
  # Do something when var is non-zero length
fi

Ale opcja -n nie jest tak naprawdę potrzebna:

# Without the -n option
if [ "$var" ]; then
  # Do something when var is non-zero length
fi

Który jest lepszy sposób?

Podobnie, co jest lepszym sposobem testowania dla zerowej długości:

if [ -z "$var" ]; then
  # Do something when var is zero-length
fi

lub

if [ ! "$var" ]; then
  # Do something when var is zero-length
fi
AllenHalsey
źródło

Odpowiedzi:

394

Edycja: jest to bardziej kompletna wersja, która pokazuje więcej różnic między [(aka test) a [[.

Poniższa tabela pokazuje, że niezależnie od tego, czy zmienna jest cytowana, czy nie, czy używasz nawiasów pojedynczych czy podwójnych i czy zmienna zawiera tylko spację, wpływają na to, czy test z lub bez -n/-zjest odpowiedni do sprawdzania zmiennej.

     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b
     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"
-----+------------------------------------+------------------------------------
unset| false false true  false true  true | false false false false true  true
null | false false true  false true  true | false false false false true  true
space| false true  true  true  true  false| true  true  true  true  false false
zero | true  true  true  true  false false| true  true  true  true  false false
digit| true  true  true  true  false false| true  true  true  true  false false
char | true  true  true  true  false false| true  true  true  true  false false
hyphn| true  true  true  true  false false| true  true  true  true  false false
two  | -err- true  -err- true  -err- false| true  true  true  true  false false
part | -err- true  -err- true  -err- false| true  true  true  true  false false
Tstr | true  true  -err- true  -err- false| true  true  true  true  false false
Fsym | false true  -err- true  -err- false| true  true  true  true  false false
T=   | true  true  -err- true  -err- false| true  true  true  true  false false
F=   | false true  -err- true  -err- false| true  true  true  true  false false
T!=  | true  true  -err- true  -err- false| true  true  true  true  false false
F!=  | false true  -err- true  -err- false| true  true  true  true  false false
Teq  | true  true  -err- true  -err- false| true  true  true  true  false false
Feq  | false true  -err- true  -err- false| true  true  true  true  false false
Tne  | true  true  -err- true  -err- false| true  true  true  true  false false
Fne  | false true  -err- true  -err- false| true  true  true  true  false false

Jeśli chcesz wiedzieć, czy zmienna ma niezerową długość, wykonaj dowolną z następujących czynności:

  • podaj zmienną w pojedynczych nawiasach (kolumna 2a)
  • użyj -ni cytuj zmienną w pojedynczych nawiasach (kolumna 4a)
  • używaj nawiasów podwójnych z cytowaniem lub bez i z lub bez -n(kolumny 1b - 4b)

Zauważ w kolumnie 1a rozpoczynającej się od wiersza oznaczonego „dwa”, że wynik wskazuje, że [ocenia zawartość zmiennej tak, jakby były częścią wyrażenia warunkowego (wynik odpowiada twierdzeniu sugerowanemu przez „T” lub „F” w kolumna opisu). Gdy [[jest używany (kolumna 1b), zawartość zmiennej jest postrzegana jako ciąg i nie jest analizowana.

Błędy w kolumnach 3a i 5a są spowodowane faktem, że wartość zmiennej zawiera spację, a zmienna nie jest cytowana. Ponownie, jak pokazano w kolumnach 3b i 5b, [[ocenia zawartość zmiennej jako ciąg.

Odpowiednio, w przypadku testów ciągów o zerowej długości kolumny 6a, 5b i 6b pokazują prawidłowe sposoby wykonania tego. Zauważ również, że którykolwiek z tych testów może być zanegowany, jeśli negowanie wykazuje wyraźniejsze zamiary niż użycie odwrotnej operacji. Na przykład: if ! [[ -n $var ]].

Jeśli używasz [, kluczem do upewnienia się, że nie otrzymasz nieoczekiwanych wyników, jest cytowanie zmiennej. Używanie [[nie ma znaczenia.

Komunikaty o błędach, które są tłumione, to „oczekiwany operator jednoargumentowy” lub „oczekiwany operator binarny”.

To jest skrypt, który utworzył powyższą tabelę.

#!/bin/bash
# by Dennis Williamson
# 2010-10-06, revised 2010-11-10
# for http://stackoverflow.com/q/3869072
# designed to fit an 80 character terminal

dw=5    # description column width
w=6     # table column width

t () { printf '%-*s' "$w" " true"; }
f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }

o=/dev/null

echo '     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b'
echo '     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"'
echo '-----+------------------------------------+------------------------------------'

while read -r d t
do
    printf '%-*s|' "$dw" "$d"

    case $d in
        unset) unset t  ;;
        space) t=' '    ;;
    esac

    [ $t ]        2>$o  && t || f
    [ "$t" ]            && t || f
    [ -n $t ]     2>$o  && t || f
    [ -n "$t" ]         && t || f
    [ -z $t ]     2>$o  && t || f
    [ -z "$t" ]         && t || f
    echo -n "|"
    [[ $t ]]            && t || f
    [[ "$t" ]]          && t || f
    [[ -n $t ]]         && t || f
    [[ -n "$t" ]]       && t || f
    [[ -z $t ]]         && t || f
    [[ -z "$t" ]]       && t || f
    echo

done <<'EOF'
unset
null
space
zero    0
digit   1
char    c
hyphn   -z
two     a b
part    a -a
Tstr    -n a
Fsym    -h .
T=      1 = 1
F=      1 = 2
T!=     1 != 2
F!=     1 != 1
Teq     1 -eq 1
Feq     1 -eq 2
Tne     1 -ne 2
Fne     1 -ne 1
EOF
Wstrzymano do odwołania.
źródło
1
Dzięki! Zdecydowałem się przyjąć styl „cytowania zmiennej w pojedynczych nawiasach (kolumna 2a)” IMO, -n po prostu dodaje szum i zmniejsza czytelność. Podobnie do testowania zerowej długości lub rozbrojenia użyję [! „$ var”] zamiast [-z „$ var”].
AllenHalsey
2
Twój wykres dla ["vs [-n"(pierwsze pytanie PO) pokazuje, że są one całkowicie równoważne, prawda?
płyty grzejne
1
@ płyty grzewcze: Tak, a których użyć zależy od tego, co jest bardziej jasne. Zauważysz również, że ich ekwiwalent dotyczy formy podwójnego nawiasu, która jest preferowana podczas korzystania z Bash.
Wstrzymano do odwołania.
1
Podsumowując swój niesamowity stół, bardziej przejrzystym („lepszym”) sposobem testowania ciągów o wartości innej niż null jest[["
płyta
22

Lepiej jest użyć mocniejszego, [[ jeśli chodzi o Bash.

Zwykłe przypadki

if [[ $var ]]; then   # var is set and it is not empty
if [[ ! $var ]]; then # var is not set or it is set to an empty string

Powyższe dwie konstrukcje wyglądają na czyste i czytelne. W większości przypadków powinny wystarczyć.

Zauważ, że nie musimy cytować wewnętrznych rozszerzeń, [[ponieważ nie ma niebezpieczeństwa dzielenia i globowania słów .

Aby zapobiec delikatnym skargom Shellcheck na [[ $var ]]i [[ ! $var ]], moglibyśmy skorzystać z tej -nopcji.

Rzadkie przypadki

W rzadkim przypadku, gdy musimy wprowadzić rozróżnienie między „ustawieniem pustego ciągu” a „nie ustawieniem wcale”, moglibyśmy użyć tych:

if [[ ${var+x} ]]; then           # var is set but it could be empty
if [[ ! ${var+x} ]]; then         # var is not set
if [[ ${var+x} && ! $var ]]; then # var is set and is empty

Możemy również skorzystać z -vtestu:

if [[ -v var ]]; then             # var is set but it could be empty
if [[ ! -v var ]]; then           # var is not set
if [[ -v var && ! $var ]]; then   # var is set and is empty
if [[ -v var && -z $var ]]; then  # var is set and is empty

Powiązane posty i dokumentacja

Istnieje wiele postów związanych z tym tematem. Tu jest kilka:

codeforester
źródło
9

Oto kilka innych testów

Prawda, jeśli ciąg znaków nie jest pusty:

[ -n "$var" ]
[[ -n $var ]]
test -n "$var"
[ "$var" ]
[[ $var ]]
(( ${#var} ))
let ${#var}
test "$var"

Prawda, jeśli ciąg znaków jest pusty:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"
Steven Penny
źródło
To nie odpowiada na pytanie OP o lepszy sposób.
codeforester
0

Prawidłowa odpowiedź to:

if [[ -n $var ]] ; then
  blah
fi

Zwróć uwagę na użycie [[...]], które poprawnie obsługuje cytowanie zmiennych za Ciebie.

użytkownik2592126
źródło
2
Po co korzystać, -ngdy nie jest tak naprawdę potrzebne w Bash?
codeforester
0

Alternatywnym i być może bardziej przejrzystym sposobem oceny pustej zmiennej środowiskowej jest użycie ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi
użytkownik1310789
źródło
10
To bardzo stara szkoła z czasów muszli Bourne'a. Nie utrwalaj tego starego nawyku. bashjest ostrzejszym narzędziem niż jego poprzednicy.
tbc0
2
Nie potrzebujesz tego nawet z powłokami przedbashowymi, jeśli unikasz składni, że standard POSIX wyraźnie oznacza, że ​​jest przestarzały. [ "$ENV_VARIABLE" != "" ]będzie działał na każdej powłoce z testimplementacją zgodną z POSIX - nie tylko bash, ale ash / dash / ksh / etc.
Charles Duffy,
... to znaczy nie używaj -aani -onie łącz testów, zamiast tego używaj [ ... ] && [ ... ]lub [ ... ] || [ ... ]jedyne przypadki narożne, które mogą mieć zastosowanie do używania danych dowolnych zmiennych w teście, są jednoznacznie zamknięte.
Charles Duffy,
-2

Użyj case/esacdo przetestowania:

case "$var" in
  "") echo "zero length";;
esac
ghostdog74
źródło
12
Nie prosze To nie casejest przeznaczone.
tbc0
2
casedziała najlepiej, gdy istnieją więcej niż dwie alternatywy.
codeforester
casenie jest przeznaczony do tego rodzaju zastosowań.
Luis Lavaire