Jak ustalić, czy zmienna bash jest pusta?

765

Jaki jest najlepszy sposób ustalenia, czy zmienna w bash jest pusta („”)?

Słyszałem, że jest to zalecane if [ "x$variable" = "x" ]

Czy to właściwy sposób? (musi być coś prostszego)

Brent
źródło
Zobacz: Testowanie łańcucha o niezerowej długości w Bash na StackOverflow.
codeforester

Odpowiedzi:

1053

Zwróci wartość true, jeśli zmienna jest rozbrojona lub ustawiona na pusty ciąg („”).

if [ -z "$VAR" ];
duffbeer703
źródło
14
Czy to obejmuje, jeśli zmienną ustawiono na wartość „”?
Brent
11
Tak, robi… Test „-z” dla łańcucha o zerowej długości.
David Z
91
if [ ! -z "$VAR" ];
Aaron Copley,
263
odwrotnością -zjest-n if [ -n "$VAR" ];
Felipe Alvarez
19
Podwójne cudzysłowy zapewniają, że zmienna nie zostanie podzielona. Prosta $varw wierszu polecenia zostanie podzielona przez jego biały znak na listę parametrów, a "$var"zawsze będzie tylko jednym parametrem. Cytowanie zmiennych często jest dobrą praktyką i powstrzymuje cię przed potknięciem się o nazwy plików zawierające spacje (między innymi). Przykład: po zrobieniu a="x --help"spróbuj cat $a- wyświetli się strona pomocy dla cat. Następnie spróbuj cat "$a"- to (zwykle) powie cat: x --help: No such file or directory. Krótko mówiąc, cytuj wcześnie i cytuj często, a prawie nigdy nie będziesz żałować.
Score_Under
247

W Bash, gdy nie martwisz się o przenośność do powłok, które go nie obsługują, powinieneś zawsze używać składni podwójnego nawiasu:

Dowolny z poniższych:

if [[ -z $variable ]]
if [[ -z "$variable" ]]
if [[ ! $variable ]]
if [[ ! "$variable" ]]

W Bash, używając podwójnych nawiasów kwadratowych, cudzysłowy nie są konieczne. Możesz uprościć test dla zmiennej, która zawiera wartość:

if [[ $variable ]]

Ta składnia jest kompatybilna z ksh (przynajmniej ksh93, w każdym razie). Nie działa w czystym POSIX-ie lub starszych powłokach Bourne'a, takich jak sh lub dash.

Zobacz moją odpowiedź tutaj i BashFAQ / 031, aby uzyskać więcej informacji na temat różnic między nawiasami kwadratowymi podwójnymi i pojedynczymi.

Możesz sprawdzić, czy zmienna jest konkretnie nieuzbrojona (w odróżnieniu od pustego ciągu):

if [[ -z ${variable+x} ]]

gdzie „x” jest dowolne.

Jeśli chcesz wiedzieć, czy zmienna ma wartość NULL, ale nie jest rozbrojona:

if [[ -z $variable && ${variable+x} ]]
Dennis Williamson
źródło
1
To if [[ $variable ]]działało dobrze dla mnie i nawet nie potrzebowało tego, set -uco było wymagane przez jedno z innych proponowanych rozwiązań.
Teemu Leisti,
3
Myślę, że to lepsze niż zaakceptowana odpowiedź.
qed
2
Dlaczego polecasz funkcję nieprzenośną, jeśli nie przynosi to żadnych korzyści?
Alastair Irvine
19
@AlastairIrvine: Wspominam o przenośności w pierwszym zdaniu mojej odpowiedzi, tytuł pytania i treść zawierają słowo „Bash”, a pytanie jest oznaczone jako bash , a struktura podwójnego nawiasu zapewnia wyraźne zalety na wiele sposobów. I nie polecam mieszania stylów wsporników ze względu na spójność i łatwość konserwacji. Jeśli potrzebujesz maksymalnej, najniższej przenośności wspólnego mianownika, użyj shzamiast Bash. Jeśli potrzebujesz zwiększonych możliwości, które zapewnia, użyj Bash i wykorzystaj go w pełni.
Dennis Williamson
2
@BrunoBronosky: Cofnąłem edycję. Na ;koniec nie ma wymogu . thenMoże być w następnym wierszu bez średnikiem w ogóle.
Dennis Williamson
230

Zmienna w bash (i dowolnej powłoce kompatybilnej z POSIX) może znajdować się w jednym z trzech stanów:

  • nieoprawny
  • ustawiony na pusty ciąg
  • ustawiony na niepusty ciąg

Przez większość czasu musisz tylko wiedzieć, czy zmienna jest ustawiona na niepusty ciąg, ale czasami ważne jest, aby odróżnić unset od ustawionego na pusty ciąg.

Oto przykłady tego, jak możesz przetestować różne możliwości i działa on w bash lub dowolnej powłoce kompatybilnej z POSIX:

if [ -z "${VAR}" ]; then
    echo "VAR is unset or set to the empty string"
fi
if [ -z "${VAR+set}" ]; then
    echo "VAR is unset"
fi
if [ -z "${VAR-unset}" ]; then
    echo "VAR is set to the empty string"
fi
if [ -n "${VAR}" ]; then
    echo "VAR is set to a non-empty string"
fi
if [ -n "${VAR+set}" ]; then
    echo "VAR is set, possibly to the empty string"
fi
if [ -n "${VAR-unset}" ]; then
    echo "VAR is either unset or set to a non-empty string"
fi

Oto to samo, ale w poręcznej formie tabeli:

                        +-------+-------+-----------+
                VAR is: | unset | empty | non-empty |
+-----------------------+-------+-------+-----------+
| [ -z "${VAR}" ]       | true  | true  | false     |
| [ -z "${VAR+set}" ]   | true  | false | false     |
| [ -z "${VAR-unset}" ] | false | true  | false     |
| [ -n "${VAR}" ]       | false | false | true      |
| [ -n "${VAR+set}" ]   | false | true  | true      |
| [ -n "${VAR-unset}" ] | true  | false | true      |
+-----------------------+-------+-------+-----------+

${VAR+foo}Konstrukt rozwija do pustego ciągu znaków, jeśli VARjest wyłączony lub foojeśli VARjest ustawiony na cokolwiek (w tym ciąg pusty).

${VAR-foo}Konstrukcja zwiększa się do wartości VAR, jeśli zestaw (obejmującą łańcuch pusty), a foojeśli ustawiony. Jest to przydatne do zapewnienia domyślnych ustawień nadpisywanych przez użytkownika (np. ${COLOR-red}Mówi, aby używać, redchyba że zmienna COLORzostała ustawiona na coś).

Powodem, dla którego [ x"${VAR}" = x ]często zaleca się testowanie, czy zmienna jest nieuzbrojona, czy ustawiona na pusty ciąg, jest to, że niektóre implementacje [polecenia (znane również jako test) są błędne. Jeśli VARjest ustawiony na coś podobnego -n, wówczas niektóre implementacje zrobią coś złego, gdy podano, [ "${VAR}" = "" ]ponieważ pierwszy argument do [jest błędnie interpretowany jako -noperator, a nie ciąg.

Richard Hansen
źródło
3
Testowanie zmiennej ustawionej na pusty ciąg można również wykonać za pomocą [ -z "${VAR-set}" ].
nwellnhof
@nwellnhof: Dzięki! Zaktualizowałem swoją odpowiedź, aby użyć składni briefer.
Richard Hansen
Jaka jest różnica między pierwszym a ostatnim konstruktem? Oba odpowiadają „VAR jest albo rozbrojony, albo ustawiony na niepusty ciąg”.
Faheem Mitha,
1
@FaheemMitha: To nie twoja wina - moja odpowiedź była trudna do odczytania. Dodałem tabelę, aby, mam nadzieję, wyjaśnić odpowiedź.
Richard Hansen
2
Nieuzbrojone kontrole nie są wiarygodne. Jeśli użytkownik zadzwoni set -ulub uruchomi set -o nounsetbash, wówczas test spowoduje błąd „bash: VAR: zmienna niezwiązana”. Zobacz stackoverflow.com/a/13864829, aby uzyskać bardziej niezawodne sprawdzanie rozbrojenia. Moje przejście do sprawdzenia, czy zmienna ma wartość NULL czy nie jest ustawione [ -z "${VAR:-}" ]. Sprawdzam, czy zmienna nie jest pusta [ "${VAR:-}" ].
Kevin Jin,
38

-z to najlepszy sposób.

Inną opcją, której użyłem, jest ustawienie zmiennej, ale można ją zastąpić inną zmienną, np

export PORT=${MY_PORT:-5432}

Jeśli $MY_PORTzmienna jest pusta, PORTzostaje ustawiona na 5432, w przeciwnym razie PORT jest ustawiony na wartość MY_PORT. Uwaga: składnia obejmuje dwukropek i myślnik.

Rory
źródło
Przypadkowo znalazłem to dzisiaj i właśnie tego chciałem. Dzięki! Muszę tolerować set -o nounsetniektóre skrypty.
opello
24

Jeśli chcesz rozróżnić przypadki ustawiania pustego w porównaniu ze statusem nieustawionym, spójrz na opcję -u dla bash:

$ set -u
$ echo $BAR
bash: BAR: unbound variable
$ [ -z "$BAR" ] && echo true
bash: BAR: unbound variable
$ BAR=""
$ echo $BAR

$ [ -z "$BAR" ] && echo true
true
MikeyB
źródło
8

Alternatywą, którą widziałem, [ -z "$foo" ]jest następująca, jednak nie jestem pewien, dlaczego ludzie używają tej metody, ktoś wie?

[ "x${foo}" = "x" ]

W każdym razie, jeśli nie zezwalasz na nieuzbrojone zmienne (przez set -ulub set -o nounset), wtedy będziesz mieć problemy z obiema tymi metodami. Jest na to prosta poprawka:

[ -z "${foo:-}" ]

Uwaga: pozostawi zmienną niezdefiniowaną.

errant.info
źródło
3
Jest to komentarz na temat alternatywy dla -zw pubs.opengroup.org/onlinepubs/009695399/utilities/test.html . Zasadniczo nie jest to alternatywa dla -z. Raczej obsługuje przypadki, w których $foomoże rozwinąć się do czegoś zaczynającego się od metaznaku, który mógłby [lub testbyłby w stanie pomylić. Umieszczenie na początku dowolnego metaznaku eliminuje tę możliwość.
James Sneeringer,
6

Pytanie pyta , jak sprawdzić, czy zmienna jest pusty ciąg znaków i najlepsze odpowiedzi są już podane do tego.
Ale wylądowałem tutaj po pewnym czasie programowania w php, a tym, czego tak naprawdę szukałem, było sprawdzenie, jak pusta funkcja w php działająca w powłoce bash.
Po przeczytaniu odpowiedzi zdałem sobie sprawę, że nie myślę poprawnie w bash, ale w tym momencie funkcja pusta w php byłaby bardzo przydatna w moim kodzie bash.
Ponieważ myślę, że może się to zdarzyć innym, postanowiłem przekonwertować funkcję pustego php w bash

Zgodnie z instrukcją php :
zmienna jest uważana za pustą, jeśli nie istnieje lub jeśli jej wartość jest jedną z następujących:

  • „” (pusty ciąg)
  • 0 (0 jako liczba całkowita)
  • 0,0 (0 jako liczba zmiennoprzecinkowa)
  • „0” (0 jako ciąg)
  • pusta tablica
  • zmienna zadeklarowana, ale bez wartości

Oczywiście przypadków zerowych i fałszywych nie można przekonwertować na bash, więc są one pomijane.

function empty
{
    local var="$1"

    # Return true if:
    # 1.    var is a null string ("" as empty string)
    # 2.    a non set variable is passed
    # 3.    a declared variable or array but without a value is passed
    # 4.    an empty array is passed
    if test -z "$var"
    then
        [[ $( echo "1" ) ]]
        return

    # Return true if var is zero (0 as an integer or "0" as a string)
    elif [ "$var" == 0 2> /dev/null ]
    then
        [[ $( echo "1" ) ]]
        return

    # Return true if var is 0.0 (0 as a float)
    elif [ "$var" == 0.0 2> /dev/null ]
    then
        [[ $( echo "1" ) ]]
        return
    fi

    [[ $( echo "" ) ]]
}



Przykład użycia:

if empty "${var}"
    then
        echo "empty"
    else
        echo "not empty"
fi



Demo:
następujący fragment:

#!/bin/bash

vars=(
    ""
    0
    0.0
    "0"
    1
    "string"
    " "
)

for (( i=0; i<${#vars[@]}; i++ ))
do
    var="${vars[$i]}"

    if empty "${var}"
        then
            what="empty"
        else
            what="not empty"
    fi
    echo "VAR \"$var\" is $what"
done

exit

wyjścia:

VAR "" is empty
VAR "0" is empty
VAR "0.0" is empty
VAR "0" is empty
VAR "1" is not empty
VAR "string" is not empty
VAR " " is not empty

Powiedziawszy, że w logice bash kontrole zerowe w tej funkcji mogą powodować problemy uboczne, imho, każdy korzystający z tej funkcji powinien ocenić to ryzyko i być może zdecydować się odciąć te kontrole, pozostawiając tylko pierwszą.

Luca Borrione
źródło
Wewnątrz funkcji empty- dlaczego napisałeś [[ $( echo "1" ) ]] ; returnzamiast po prostu return 1?
Dor
5

całe if-then i -z są niepotrzebne.

[„$ foo”] && echo „foo nie jest puste”
[„$ foo”] || echo „foo jest rzeczywiście pusty”

źródło
3
To się nie powiedzie, jeśli foo zawiera tylko spacje
Brian
2
Nie powiedzie się również w niektórych powłokach, jeśli foo zaczyna się od myślnika, ponieważ jest interpretowane jako opcja. Np. Na solarisach ksh , zsh i bash nie stanowią problemu, sh i / bin / test się nie
udadzą
4

Dzieje się tak dokładnie wtedy, gdy $ FOO jest ustawione i puste:

[ "${FOO+x}" = x ] && [ -z "$FOO" ]

źródło
4

Osobiście wolę bardziej przejrzysty sposób sprawdzenia:

if [ "${VARIABLE}" == "" ]; then
  echo VARIABLE is empty
else
  echo VARIABLE is not empty
fi
Fedir RYKHTIK
źródło
1
Niektóre pociski nie akceptują podwójnego znaku równości.
Dennis Williamson,
1
Pytanie dotyczyło bashu. Używam bash. Działa dla mnie dobrze. O czym dokładnie mówisz?
Fedir RYKHTIK
2
Jeśli używasz Bash, powinieneś użyć podwójnych nawiasów kwadratowych. Mój poprzedni komentarz był prostym stwierdzeniem dla tych, którzy mogą przeczytać twoją odpowiedź i używać innej powłoki.
Dennis Williamson,
pojedyncze nawiasy kwadratowe są w tym przypadku w porządku, zobacz serverfault.com/questions/52034/…
Luca Borrione
4

Moje 5 centów: istnieje również krótsza składnia niż if ...ta:

VALUE="${1?"Usage: $0 value"}"

Ten wiersz ustawi WARTOŚĆ, jeśli podany zostanie argument, i wyświetli komunikat o błędzie poprzedzony numerem wiersza skryptu w przypadku błędu (i zakończy wykonywanie skryptu).

Kolejny przykład można znaleźć w przewodniku abs (wyszukaj „Przykład 10-7”).

gluk47
źródło
0

Nie jest to dokładna odpowiedź, ale wpadłem na tę sztuczkę. Jeśli ciąg, którego szukasz, pochodzi od „polecenia”, możesz faktycznie zapisać polecenie w env. zmienną, a następnie wykonuj ją za każdym razem dla instrukcji if, wtedy nawiasy nie są wymagane!

Na przykład to polecenie, które określa, czy korzystasz z Debiana:

grep debian /proc/version

pełny przykład:

IS_DEBIAN="grep -i debian /proc/version"

if $IS_DEBIAN; then
  echo 'yes debian'
else
  echo 'non debian'
fi

Jest to więc sposób pośredni (ponowne uruchamianie go za każdym razem) w celu sprawdzenia pustego ciągu (zdarza się, że sprawdza odpowiedź na błąd z polecenia, ale zdarza się również, że zwraca pusty ciąg).

rogerdpack
źródło