Verwenden von unset vs. Setzen einer Variablen auf leer

108

Ich schreibe gerade ein Bash-Test-Framework, in dem in einer Testfunktion sowohl Standard-Bash-Tests ( [[) als auch vordefinierte Matcher verwendet werden können. Matcher sind Wrapper für '[[' und setzen neben der Rückgabe eines Rückkehrcodes eine aussagekräftige Nachricht, die besagt, was erwartet wurde.

Beispiel:

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

            return 1
    fi
}

Wenn also ein Matcher verwendet wird und dieser fehlschlägt, wird nur dann eine error_message gesetzt.

Jetzt, irgendwann später, teste ich, ob die Tests erfolgreich waren. Wenn es erfolgreich war, drucke ich die Erwartung in grün, wenn es fehlgeschlagen ist, in rot.

Darüber hinaus ist möglicherweise eine error_message festgelegt, sodass ich teste, ob eine Nachricht vorhanden ist, sie drucke und dann deaktiviere (da der folgende Test möglicherweise keine festlegt error_message):

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

    unset -v error_message
fi

Nun ist meine Frage, ob es besser ist, die Variable zu deaktivieren oder sie einfach auf '' zu setzen, wie

error_message=''

Welches ist besser? Macht es tatsächlich einen Unterschied? Oder sollte ich ein zusätzliches Flag haben, das angibt, dass die Nachricht gesetzt wurde?

Hilfsmethode
quelle
1
Wenn Sie nie error_messagemit etwas anderem vergleichen , würde ich sagen, dass es keine Rolle spielt. Ich denke jedoch, dass Sie möchten [[ $error_message ]], andernfalls testen Sie, ob die Literalzeichenfolge "error_message" vorhanden ist.
Chepner
@chepner Ja, war ein Tippfehler. Behoben.
Hilfsmethode

Antworten:

143

Meistens sehen Sie keinen Unterschied, es sei denn, Sie verwenden 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

Es hängt also wirklich davon ab, wie Sie die Variable testen werden.

Ich werde hinzufügen, dass meine bevorzugte Art zu testen, ob es eingestellt ist, ist:

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

oder

[[ -z $var ]]  # True if zero length
cdarke
quelle
43
var=ist nicht "nicht gesetzt". Es ist nur eine nicht zitierte leere Zeichenfolge. Darüber hinaus set -ukönnen die verschiedenen Formen der Parametererweiterung von bash auch zwischen nicht gesetzten und null-Werten unterscheiden: Erweitert ${foo:bar}sich auf "bar", wenn nicht gesetzt fooist, aber "" wenn fooist null, während ${foo:-bar}erweitert auf "bar", wenn foo nicht gesetzt oder null ist.
Chepner
1
[[ -n $var ]]ist falsch, wenn varauf die leere Zeichenfolge gesetzt ist.
Chepner
1
Wenn Sie 'declare -p' verwenden, um nach der Variablen 'Existenz' zu suchen, zeigt var = 'declare - var = "" an. Sie müssen unset verwenden, um sie zu entfernen. Wenn es sich um eine schreibgeschützte Variable handelt, können Sie sie natürlich nicht erhalten natürlich loswerden. Wenn Sie var = etwas in einer Funktion verwenden, müssen Sie sich keine Sorgen machen, wenn Sie "local var = value" verwenden. Seien Sie jedoch vorsichtig, da "declare var = value" auch local ist. Im Fall von declare Sie müssen es explizit global mit "declare -g var = value" festlegen, ohne zu deklarieren, müssen Sie es explizit mit "local" als lokal festlegen. Globale Variablen werden nur gelöscht / w nicht gesetzt.
Osirisgothra
@osirisgothra: guter Punkt über lokale Variablen. Im Allgemeinen ist es natürlich am besten, alle Variablen in einer Funktion so zu deklarieren (oder zu lokalisieren), dass sie gekapselt sind.
Cdarke
3
@chepner sollte ${foo-bar}statt sein ${foo:bar}. Testen Sie selbst:unset a; echo ">${a:-foo}-${a:foo}-${a-foo}<"
Liviu Chircu
17

Wie bereits erwähnt, unterscheidet sich die Verwendung von unset auch bei Arrays

$ foo=(4 5 6)

$ foo[2]=

$ echo ${#foo[*]}
3

$ unset foo[2]

$ echo ${#foo[*]}
2
Steven Penny
quelle
2

Wenn Sie also den Array-Index 2 deaktivieren, entfernen Sie im Wesentlichen dieses Element im Array und verringern die Array-Größe (?).

Ich habe meinen eigenen Test gemacht ..

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

Was in ... endet..

3
0

Nur um zu verdeutlichen, dass das Deaktivieren des gesamten Arrays es tatsächlich vollständig entfernt.

PdC
quelle
0

Basierend auf den obigen Kommentaren ist hier ein einfacher Test:

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

Beispiel:

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