Inicjowanie zmiennych Bash - czy jest to wymagane, zalecane lub definiowane na bieżąco

9

Czy jest jakaś zaleta / wada inicjowania wartości zmiennej bash w skrypcie przed kodem głównym lub zmiennymi lokalnymi w funkcji przed przypisaniem jej wartości rzeczywistej?

Czy muszę zrobić coś takiego:

init()
{
    name=""
    name=$1
}

init "Mark"

Czy istnieje ryzyko, że zmienne zostaną zainicjowane wartościami śmieci (jeśli nie zostaną zainicjowane) i że będzie to miało negatywny wpływ na wartości zmiennych?

Cyrus
źródło
2
Skąd masz ten pomysł?
6
@DoritoStyle Cóż, jeśli ktoś jest przyzwyczajony do języków niższego poziomu, takich jak C, to jest to całkowicie ważna rzecz, o którą należy się martwić.
Kusalananda
@Kusalananda Czy C nie jest odpowiednikiem tego kodu name = ""; name = argv[1];? Czy to nie jest tak samo bezcelowe?
Joseph Sible-Reinstate Monica
1
@ JosephSible-ReinstateMonica Tak. Kod C, który opublikowałeś, nie ma sensu. Jednak w C, w przeciwieństwie do powłoki, niezainicjowane zmienne nie mają konkretnej dobrze zdefiniowanej wartości. Oznacza to, że inicjowanie zmiennych w językach takich jak C ma sens w wielu okolicznościach. Nie jest to konieczne w powłoce. Inicjowanie zmiennej w C tylko w celu natychmiastowego ustawienia jej na inną wartość jest bezcelowe, jak zauważyłeś.
Kusalananda

Odpowiedzi:

22

Przypisanie pustego łańcucha do zmiennej nie przynosi korzyści, a następnie natychmiastowe przypisanie do niego innego łańcucha zmiennej. Przypisanie wartości do zmiennej powłoki całkowicie zastąpi poprzednią wartość.

Według mojej wiedzy nie ma żadnego zalecenia, które mówi, że powinieneś jawnie inicjować zmienne do pustych łańcuchów. W rzeczywistości może to w niektórych okolicznościach maskować błędy (błędy, które w innym przypadku byłyby widoczne, gdyby działały pod set -u, patrz poniżej).

Nieuzbrojona zmienna, nieużywana od początku skryptu lub jawnie rozbrojona przez uruchomienie na niej unsetpolecenia, nie będzie miała wartości. Wartość takiej zmiennej będzie niczym. Jeśli "$myvariable"użyjesz jako , otrzymasz ekwiwalent ""i nigdy nie dostaniesz „śmieciowych danych”.

Jeśli opcja powłoki nounsetjest ustawiona za pomocą albo, set -o nounsetalbo set -uodwołanie się do zmiennej nieustawionej spowoduje, że powłoka wygeneruje błąd (i powłoka nieinteraktywna zakończy działanie):

$ set -u
$ echo "$myvariable"
/bin/sh: myvariable: parameter not set

lub w bash:

$ set -u
$ echo "$myvariable"
bash: myvariable: unbound variable

Zmienne powłoki zostaną zainicjowane przez środowisko, jeśli nazwa zmiennej odpowiada istniejącej zmiennej środowiskowej.

Jeśli spodziewasz się, że używasz zmiennej, która może być zainicjowana przez środowisko w ten sposób (i jeśli jest niepożądana), możesz jawnie ją rozbroić przed główną częścią skryptu:

unset myvariable    # unset so that it doesn't inherit a value from the environment

który również usunąłby ją jako zmienną środowiskową, lub możesz po prostu zignorować jej wartość początkową i po prostu nadpisać ją przypisaniem (co spowodowałoby również zmianę wartości zmiennej środowiskowej).

Nigdy nie natrafisz na niezainicjowane śmieci w zmiennej powłoki (chyba że, jak wspomniano, śmieci już istniały w zmiennej środowiskowej o tej samej nazwie).

Kusalananda
źródło
3
Chociaż nie ma wartości, aby ustawić zmienną na pustą wartość, a następnie natychmiast ustawić ją tak, jak robi to dosłownie OP, istnieje wartość w ustawieniu pustej wartości (lub unsetting) przed uruchomieniem jakiegoś rodzaju forlub whilepętli, aby ustawić ją na obliczoną wartość, jeśli istnieje jakakolwiek szansa, że ​​pętla faktycznie nie uruchomi się z powodu niespełnienia warunków; a zmienna mogła zostać ustawiona na inną wartość w środowisku odziedziczonym przez skrypt. Ale prawdopodobnie lepiej jest umieścić wszystko w a main()i zdefiniować zmienne jako local.
Monty Harder
Można również wykryć zmienne nieuzbrojone za pomocą operatorów :${myvariable-defaultvalue}
interpretacji
3

Edycja : Ups, najwyraźniej deklaracja różni się od inicjalizacji. W każdym razie zostawię to tutaj, aby nowicjusze, tacy jak ja, mogli wyciągnąć wnioski z mojego błędu.


Zaletą deklarowania zmiennych lokalnych w funkcji jest to, że można łatwo skopiować kod.

Powiedzmy na przykład, że mam funkcję:

foo(){
    local name
    name="$1"
    echo "$name"
}

Jeśli chcę przekształcić go w skrypt, po prostu ignoruję localinstrukcję i kopiuję wszystko inne:

#!/bin/bash
name="$1"
echo "$name"

Gdyby deklaracja i przypisanie znajdowały się w tym samym wierszu, musiałbym ręcznie edytować localczęść przed przekształceniem jej w skrypt:

foo(){
    local name="$1"
    echo "$name"
}

W tym przykładzie nie jest to wielka sprawa, ale jeśli masz do czynienia z większymi, bardziej złożonymi funkcjami, może to być bardziej bolesne.

wjandrea
źródło
1
Pytanie nie dotyczyło połączenia deklaracji i inicjalizacji. Chodziło o to, czy zainicjować z pustą wartością przed przypisaniem wartości rzeczywistej.
Barmar
@Barmar Oh, właśnie szukałem „inicjalizacji”. Myślałem, że to równoznaczne z „deklaracją” ...
wjandrea