Używanie nieustawionego vs. ustawienie zmiennej na pustą

108

Obecnie piszę framework testowy bash, w którym w funkcji testowej [[można używać zarówno standardowych testów bash ( ), jak i predefiniowanych dopasowań. Dopasowujące są opakowaniami dla '[[' i oprócz zwracania kodu powrotu, ustawiają sensowną wiadomość mówiącą o oczekiwaniach.

Przykład:

string_equals() {
    if [[ ! $1 = $2 ]]; then
            error_message="Expected '$1' to be '$2'."

            return 1
    fi
}

Tak więc, gdy zostanie użyty element dopasowujący i nie powiedzie się, tylko wtedy ustawiany jest komunikat o błędzie.

Teraz, w pewnym momencie później, sprawdzam, czy testy się powiodły. Jeśli się udało, drukuję oczekiwanie na zielono, jeśli zawiodło na czerwono.

Ponadto może być ustawiony komunikat error_message, więc sprawdzam, czy komunikat istnieje, drukuję go, a następnie kasuję (ponieważ poniższy test może nie ustawić error_message):

if [[ $error_message ]]; then
    printf '%s\n' "$error_message"

    unset -v error_message
fi

Teraz moje pytanie brzmi, czy lepiej jest cofnąć ustawienie zmiennej, czy po prostu ustawić ją na „”, na przykład

error_message=''

Który jest lepszy? Czy to rzeczywiście robi różnicę? A może powinienem mieć dodatkową flagę wskazującą, że komunikat został ustawiony?

helpermethod
źródło
1
Jeśli nigdy nie porównujesz error_messagez niczym innym, powiedziałbym, że to nie ma znaczenia. Jednak myślę, że chcesz [[ $error_message ]], w przeciwnym razie testujesz, czy istnieje literał „komunikat_błędu”.
chepner
@chepner Tak, była literówką. Naprawione.
helpermethod

Odpowiedzi:

143

Przeważnie nie widzisz różnicy, chyba że używasz set -u:

/home/user1> var=""
/home/user1> echo $var

/home/user1> set -u
/home/user1> echo $var

/home/user1> unset var
/home/user1> echo $var
-bash: var: unbound variable

Tak naprawdę to zależy od tego, jak zamierzasz przetestować zmienną.

Dodam, że preferowany przeze mnie sposób testowania jeśli jest ustawiony to:

[[ -n $var ]]  # True if the length of $var is non-zero

lub

[[ -z $var ]]  # True if zero length
cdarke
źródło
43
var=nie jest „nie ustawiono”. To po prostu niecytowany pusty ciąg. Oprócz tego set -u, różne formy rozwijania parametrów przez bash mogą również rozróżniać między wartościami nieustawionymi i zerowymi: gdy foo jest nieustawione lub zerowe, ${foo:bar}rozwija się do „bar”, gdy nie foojest ustawione, ale „”, gdy foojest null, a ${foo:-bar}do „bar”.
chepner
1
[[ -n $var ]]ma wartość false, jeśli varjest ustawiona na pusty ciąg.
chepner
1
jeśli użyjesz 'deklaracji -p' do sprawdzenia zmiennej 'istnienie', wtedy var = pokaże 'zadeklaruj - var = ""' musisz użyć unset, aby się jej pozbyć, oczywiście jeśli jest to zmienna tylko do odczytu, nie możesz jej dostać oczywiście się go pozbyć. Ponadto, jeśli zmienisz = coś wewnątrz funkcji, nie będziesz musiał się martwić o pozbycie się tego, jeśli użyjesz „local var = value”, zachowaj ostrożność, ponieważ „deklaracja zmienna = wartość” jest również lokalna. W przypadku deklaracji musisz jawnie ustawić ją jako globalną za pomocą "deklaruj -g zmienna = wartość", bez deklaracji musisz jawnie ustawić ją jako lokalną używając "lokalna". Zmienne globalne są tylko kasowane / w nieustawione.
osirisgothra
@osirisgothra: dobra uwaga na temat zmiennych lokalnych. Oczywiście ogólnie najlepiej jest zadeklarować (lub zlokalizować) wszystkie zmienne w funkcji, tak aby była hermetyzowana.
cdarke
3
@chepner powinno być ${foo-bar}zamiast ${foo:bar}. unset a; echo ">${a:-foo}-${a:foo}-${a-foo}<"
sprawdź
17

Jak już powiedziano, używanie unset różni się również w przypadku tablic

$ foo=(4 5 6)

$ foo[2]=

$ echo ${#foo[*]}
3

$ unset foo[2]

$ echo ${#foo[*]}
2
Steven Penny
źródło
2

Tak więc, usuwając indeks tablicy 2, zasadniczo usuwasz ten element z tablicy i zmniejszasz rozmiar tablicy (?).

Zrobiłem własny test.

foo=(5 6 8)
echo ${#foo[*]}
unset foo
echo ${#foo[*]}

Co skutkuje w..

3
0

Więc tylko wyjaśnienie, że wyłączenie całej tablicy w rzeczywistości całkowicie ją usunie.

PdC
źródło
0

W oparciu o powyższe komentarze, oto prosty test:

isunset() { [[ "${!1}" != 'x' ]] && [[ "${!1-x}" == 'x' ]] && echo 1; }
isset()   { [ -z "$(isunset "$1")" ] && echo 1; }

Przykład:

$ unset foo; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's unset
$ foo=     ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
$ foo=bar  ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
Jonathan H.
źródło