2016-07-11 6 views
5

Z uwagi na fakt, że Bash, działając w trybie set -o nounset (alias set -u), może uznać puste tablice za niezaładowane, niezależnie od tego, czy zostały one faktycznie przypisane pusta wartość, należy zachować ostrożność podczas próby rozszerzenia tablicy - jednym z obejść jest sprawdzenie, czy długość tablicy wynosi zero. Nie wspominając już o tym, że uzyskanie liczby elementów w tablicy jest wspólną operacją samą w sobie.Uzyskaj długość pustej tablicy lub anuluj jej ustawienie, gdy działa opcja "słownik"

Podczas pracy z Bash 4.2.47 (1) - wypuszczam w openSUSE 42.1, przyzwyczaiłem się do tego, że uzyskanie rozmiaru tablicy z ${#ARRAY_NAME[@]} powiedzie się, gdy tablica jest pusta lub unset. Jednak podczas sprawdzania mojego skryptu za pomocą Bash 4.3.46 (1) - w FreeBSD 10.3 okazało się, że operacja ta może zakończyć się niepowodzeniem z ogólnym komunikatem o błędzie "niezwiązana zmienna". Zapewnienie domyślnej wartości rozszerzenia nie wydaje się działać dla długości tablicy. Zapewnienie alternatywnych łańcuchów poleceń wydaje się działać, ale nie w funkcji wywoływanej przez rozszerzenie podpórki - funkcje kończą się po pierwszej awarii. Co jeszcze może tu być jakiejkolwiek pomocy?

Rozważmy następujący przykład:

function Size() 
{ 
    declare VAR="$1" 
    declare REF="\${#${VAR}[@]}" 
    eval "echo \"${REF}\" || echo 0" 2>/dev/null || echo 0 
} 

set -u 
declare -a MYARRAY 

echo "size: ${#MYARRAY[@]}" 
echo "size: ${#MYARRAY[@]-0}" 
echo "Size: $(Size 'MYARRAY')" 
echo -n "Size: "; Size 'MYARRAY' 

w środowisku openSUSE, wszystkie linie wyjściowe 0echo, jak oczekiwano. W FreeBSD ten sam wynik jest możliwy tylko wtedy, gdy tablicy jawnie przypisano pustą wartość: MYARRAY=(); w przeciwnym razie obydwa zapytania śródliniowe w pierwszych dwóch wierszach zawodzą, trzecia linia po prostu wyprowadza Size: (co oznacza, że ​​wynik rozwinięcia jest pusty), a tylko ostatnia linia kończy się całkowicie dzięki zewnętrznemu || echo 0 - jednak przekazanie wyniku do ekranu jest nie to, co zwykle ma na celu uzyskanie długości tablicy.

Oto podsumowanie moich obserwacji:

        Bash 4.2 Bash 4.3 
            openSUSE FreeBSD 

counting elements of unset array  OK  FAILED 
counting elements of empty array  OK  OK 

content expansion of unset array  FAILED FAILED 
content expansion of unset array(*) OK  OK 
content expansion of empty array  FAILED FAILED 
content expansion of empty array(*) OK  OK 
    (* with fallback value supplied) 

Dla mnie to wygląda dość niespójne. Czy istnieje w tym celu rozwiązanie przyszłościowe i wieloplatformowe?

+0

proszę dodać wyjście 'echo $ -' do Twojego pytania. – Cyrus

+0

@Cyrus Jest to 'huB' w obu systemach podczas uruchamiania skryptu,' himBH' w linii poleceń. –

+1

Dlaczego nie po prostu zrobić 'set + u', a następnie wykonać niezbędne kontrole, a następnie' set -u'? –

Odpowiedz

1

jako rozwiązanie tymczasowe, ja poszedłem trasy sugerowanego przez @ William pursell i tylko wyłączonym opcją nounset podczas kwerendy:

function GetArrayLength() 
{ 
    declare ARRAY_NAME="$1" 
    declare INDIRECT_REFERENCE="\${#${ARRAY_NAME}[@]}" 
    case "$-" in 
    *'u'*) 
     set +u 
     eval "echo \"${INDIRECT_REFERENCE}\"" 
     set -u 
     ;; 
    *) 
     eval "echo \"${INDIRECT_REFERENCE}\"" 
     ;; 
    esac 
} 

(Korzystanie if zamiast case prowadzi do pomijalnie wolniejsze wykonanie na moim teście Ponadto, case pozwala łatwo dopasować dodatkowe opcje, jeśli będzie to kiedyś konieczne.)

Próbowałem też wykorzystując fakt, że ekspansja treść (z awaryjnej lub wartości odtworzeniowej) zwykle powiedzie nawet dla unset tablic:

function GetArrayLength() 
{ 
    declare ARRAY_NAME="$1" 
    declare INDIRECT_REFERENCE="${ARRAY_NAME}[@]" 
    if [[ -z "${!INDIRECT_REFERENCE+isset}" ]]; then 
     echo 0 
    else 
     INDIRECT_REFERENCE="\${#${ARRAY_NAME}[@]}" 
     eval "echo \"${INDIRECT_REFERENCE}\"" 
    fi 
} 

Jednak okazuje się, że Bash nie optymalizuje ${a[@]+b} ekspansji, jak wykonanie czas wyraźnie wzrasta w przypadku większych tablic - chociaż jest najmniejszy dla pustych lub nieuzbrojonych tablic.

Niemniej jednak, jeśli ktoś ma lepsze rozwiązanie, spadał swobodnie dodawać inne odpowiedzi.

1

Istnieją znane (udokumentowane) różnice między smakami Linux i BSD z bash. Sugerowałbym napisanie twojego kodu zgodnie ze standardem POSIX. Możesz zacząć tutaj, aby uzyskać więcej informacji -> www2.opengroup.org.

Mając to na uwadze, można zacząć bash z opcją wiersza polecenia --posix czy można wykonać polecenie set -o posix podczas bash jest uruchomiony. Albo spowoduje, że bash będzie zgodne ze standardem POSIX.

Powyższa sugestia zwiększy prawdopodobieństwo spójności między platformami.

+1

Niezły punkt. Jednak w tym konkretnym przypadku nie widzę różnicy między 'posix off' i' posix on'. To samo dotyczy innych zauważonych rozbieżności, takich jak dopasowanie do wyrażenia regularnego. –

Powiązane problemy