W Bash, jaki jest najprostszy sposób sprawdzenia, czy tablica zawiera określoną wartość?
Edycja : Z pomocą odpowiedzi i komentarzy po kilku testach wymyśliłem:
function contains() {
local n=$#
local value=${!n}
for ((i=1;i < $#;i++)) {
if [ "${!i}" == "${value}" ]; then
echo "y"
return 0
fi
}
echo "n"
return 1
}
A=("one" "two" "three four")
if [ $(contains "${A[@]}" "one") == "y" ]; then
echo "contains one"
fi
if [ $(contains "${A[@]}" "three") == "y" ]; then
echo "contains three"
fi
Nie jestem pewien, czy to najlepsze rozwiązanie, ale wydaje się, że działa.
[[ " ${branches[@]} " =~ " ${value} " ]] && echo "YES" || echo "NO";
if
. Nie sądzę, aby można było uniknąć SC2199 przy takim podejściu. Musisz jawnie zapętlić tablicę, jak pokazano w niektórych innych rozwiązaniach, lub zignorować ostrzeżenie.Poniżej znajduje się mała funkcja do osiągnięcia tego. Wyszukiwany ciąg jest pierwszym argumentem, a reszta to elementy tablicy:
Uruchomienie testowe tej funkcji może wyglądać następująco:
źródło
"${array[@]}"
. W przeciwnym razie elementy zawierające spacje spowodują uszkodzenie funkcjonalności.if elementIn "$table" "${skip_tables[@]}" ; then echo skipping table: ${table}; fi;
Dzięki za pomoc!shift
przesuwa listę argumentów o 1 w lewo (upuszczając pierwszy argument) ifor
bezin
pośrednio iteruje listę argumentów.źródło
case "${myarray[@]}" in *"t"*) echo "found" ;; esac
wyniki:found
Dla ciągów:
źródło
${#}
ponieważ Bash obsługuje rzadkie tablice.Rozwiązanie jednowierszowe
Wyjaśnienie
printf
Oświadczenie drukuje każdy element tablicy w osobnej linii.grep
Oświadczenie używa znaków specjalnych^
i$
znaleźć wiersz zawierający dokładnie wzór podany jakomypattern
(nie więcej, nie mniej).Stosowanie
Aby umieścić to w
if ... then
oświadczeniu:Dodałem
-q
flagę dogrep
wyrażenia, aby nie drukowało dopasowań; po prostu potraktuje istnienie dopasowania jako „prawdziwe”.źródło
Jeśli potrzebujesz wydajności, nie chcesz za każdym razem przeszukiwać całej tablicy.
W takim przypadku możesz utworzyć tablicę asocjacyjną (tablicę skrótów lub słownik), która reprezentuje indeks tej tablicy. Oznacza to, że mapuje każdy element tablicy na swój indeks w tablicy:
Następnie możesz użyć tego w następujący sposób:
I przetestuj członkostwo tak:
Lub też:
Zauważ, że to rozwiązanie działa poprawnie, nawet jeśli w testowanej wartości lub w wartościach tablic są spacje.
Jako bonus otrzymujesz również indeks wartości w tablicy z:
źródło
make_index
jest nieco bardziej wymyślny ze względu na pośrednie; mogłeś użyć stałej nazwy tablicy ze znacznie prostszym kodem.Zwykle używam:
wartość niezerowa oznacza, że znaleziono dopasowanie.
źródło
haystack=(needle1 needle2); echo ${haystack[@]} | grep -o "needle" | wc -w
inarray=$(printf ",%s" "${haystack[@]}") | grep -o ",needle" | wc -w)
inarray=$(printf ",%s" "${haystack[@]}") | grep -x "needle" | wc -l
inarray=$(echo " ${haystack[@]}" | grep -o " needle" | wc -w)
jako -x powoduje, że grep próbuje dopasować cały ciąg wejściowyKolejna wkładka bez funkcji:
Dzięki @Qwerty za informacje dotyczące spacji!
odpowiednia funkcja:
przykład:
źródło
exit 0
robi (zatrzymuje się jak najszybciej, jeśli zostanie znaleziony).|| echo not found
zamiast|| not found
lub powłoka spróbuje wykonać polecenie o nazwie nie z znalezionym argumentem, jeśli żądanej wartości nie ma w tablicy.Teraz poprawnie obsługuje puste tablice.
źródło
"$e" = "$1"
(zamiast"$e" == "$1"
) która wygląda jak błąd."e" == "$1"
jest jaśniejszy składniowo.Oto niewielki wkład:
Uwaga: w ten sposób nie rozróżnia się przypadku „dwa słowa”, ale nie jest to wymagane w pytaniu.
źródło
Jeśli chcesz zrobić szybki i brudny test, aby sprawdzić, czy warto iterować całą tablicę, aby uzyskać dokładne dopasowanie, Bash może traktować tablice jak skalary. Sprawdź dopasowanie w skalarach, jeśli nie, pomijanie pętli pozwala zaoszczędzić czas. Oczywiście możesz uzyskać fałszywe alarmy.
Spowoduje to wyświetlenie „Sprawdzanie” i „Dopasuj”. Dzięki
array=(word "two words" something)
temu wyświetli się tylko „Sprawdzanie”. Dziękiarray=(word "two widgets" something)
nie będzie wyjścia.źródło
words
wyrażeniem regularnym,^words$
które pasuje tylko do całego łańcucha, co całkowicie eliminuje potrzebę sprawdzania każdego elementu osobno?pattern='^words$'; if [[ ${array[@]} =~ $pattern ]]
nigdy nie będzie pasować, ponieważ sprawdza całą tablicę naraz, jakby to był skalar. Indywidualne kontrole w mojej odpowiedzi należy wykonać tylko wtedy, gdy istnieje powód, aby kontynuować na podstawie przybliżonego dopasowania.To działa dla mnie:
Przykładowe wywołanie:
źródło
Jeśli wolisz, możesz użyć równoważnych długich opcji:
źródło
Pożyczanie od Dennis Williamson „s odpowiedzi , następujące rozwiązanie łączy tablic, muszli bezpieczny cytowanie i wyrażeń regularnych, aby uniknąć konieczności: iteracji po pętli; z zastosowaniem rur lub innych podprocesów; lub używając narzędzi innych niż bash.
Powyższy kod działa przy użyciu wyrażeń regularnych Bash w celu dopasowania do skróconej wersji zawartości tablicy. Istnieje sześć ważnych kroków, aby upewnić się, że dopasowanie wyrażenia regularnego nie może zostać oszukane przez sprytne kombinacje wartości w tablicy:
printf
skorupkach-cytowania%q
. Cytowanie powłoki zapewni, że znaki specjalne staną się „bezpieczne dla powłoki” poprzez ucieczkę odwrotnym ukośnikiem\
.%q
; to jedyny sposób, aby zagwarantować, że wartości w tablicy nie mogą być konstruowane w sprytny sposób, aby oszukać dopasowanie wyrażenia regularnego. Wybieram przecinek,,
ponieważ postać ta jest najbezpieczniejsza, gdy zostanie ewaluowana lub wykorzystana w nieoczekiwany sposób.,,%q
jako argumentu doprintf
. Jest to ważne, ponieważ dwa wystąpienia znaku specjalnego mogą pojawiać się obok siebie tylko wtedy, gdy występują jako separator; wszystkie inne przypadki postaci specjalnej zostaną usunięte.${array_str}
, porównać z${array_str},,
.źródło
printf -v pattern ',,%q,,' "$user_input"; if [[ "${array_str},," =~ $pattern ]]
.case "$(printf ,,%q "${haystack[@]}"),," in (*"$(printf ,,%q,, "$needle")"*) true;; (*) false;; esac
Mały dodatek do odpowiedzi @ ghostdog74 na temat używania
case
logiki do sprawdzania, czy tablica zawiera określoną wartość:Lub z
extglob
włączoną opcją, możesz to zrobić w następujący sposób:Również możemy to zrobić
if
instrukcji:źródło
dany :
następnie prosta kontrola:
gdzie
(Powodem przypisywania p oddzielnie zamiast używania wyrażenia bezpośrednio w [[]] jest utrzymanie kompatybilności z bash 4)
źródło
Łącząc kilka przedstawionych tutaj pomysłów, możesz stworzyć elegancki, jeśli zestawienie bez pętli, które pasuje dokładnie do słów .
To nie uruchomi się,
word
lubval
tylko dopasowania całego słowa. Zepsuje się, jeśli każda wartość tablicy zawiera wiele słów.źródło
Generalnie piszę tego rodzaju narzędzia do działania na nazwie zmiennej, a nie na wartości zmiennej, przede wszystkim dlatego, że bash nie może przekazać zmiennych przez odwołanie.
Oto wersja, która działa z nazwą tablicy:
W ten sposób przykład pytania staje się:
itp.
źródło
Korzystanie
grep
iprintf
Sformatuj każdy element tablicy w nowej linii, a następnie
przykład:grep
w liniach.Zauważ, że nie ma to problemów z delimetrami i spacjami.
źródło
Sprawdzanie jednowierszowe bez „grep” i pętli
Podejście to nie korzysta z zewnętrznych narzędzi, takich jak
grep
ani pętle.To, co się tutaj dzieje, to:
IFS
wartości zmiennej;IFS
wartość na tymczasową, oceniając nasze wyrażenie warunkowe w podpowłoce (w parze nawiasów)źródło
Korzystanie z rozszerzania parametrów:
źródło
${myarray[hello]:+_}
działa świetnie dla tablic asocjacyjnych, ale nie dla zwykłych tablic indeksowanych. Pytanie dotyczy znalezienia wartości w obiekcie, a nie sprawdzenia, czy istnieje klucz tablicy asocjacyjnej.Po udzieleniu odpowiedzi przeczytałem inną odpowiedź, która szczególnie mi się podobała, ale była wadliwa i przegłosowana. Zainspirowałem się i oto dwa nowe podejścia, które według mnie są realne.
za pomocą
grep
iprintf
:za pomocą
for
:Aby dodać wyniki not_found, dodaj
|| <run_your_if_notfound_command_here>
źródło
Oto moje zdanie na ten temat.
Wolałbym nie używać basha dla pętli, jeśli mogę tego uniknąć, ponieważ to wymaga czasu. Jeśli coś musi się zapętlić, niech to będzie coś napisanego w języku niższego poziomu niż skrypt powłoki.
Działa to poprzez utworzenie tymczasowej tablicy asocjacyjnej,
_arr
której indeksy pochodzą z wartości tablicy wejściowej. (Zauważ, że tablice asocjacyjne są dostępne w wersji bash 4 i nowszych, więc ta funkcja nie będzie działać we wcześniejszych wersjach bash.) Ustawiliśmy,$IFS
aby uniknąć podziału słów na białe znaki.Funkcja nie zawiera wyraźnych pętli, chociaż wewnętrznie przebija się przez tablicę wejściową w celu zapełnienia
printf
. Format printf używa%q
do zapewnienia, że dane wejściowe są ucieczkowe, dzięki czemu można je bezpiecznie wykorzystać jako klucze tablicy.Zauważ, że wszystko, czego używa ta funkcja, jest wbudowane w bash, więc nie ma żadnych zewnętrznych potoków ciągnących cię w dół, nawet w rozszerzeniu poleceń.
A jeśli nie lubisz używać
eval
... cóż, możesz zastosować inne podejście. :-)źródło
eval
instrukcję na końcu odpowiedzi. :)eval
(nie mam nic przeciwko temu, w przeciwieństwie do większości ludzi, którzy płaczą,eval
to zło, przeważnie nie rozumiejąc, co jest w tym złego). Tylko, że twoje polecenie jest złamane. Może%q
zamiast%s
byłoby lepiej.eval
, oczywiście), ale masz całkowitą rację,%q
wydaje się pomagać, nie psując niczego, co widzę. (Nie zdawałem sobie sprawy, że% q również ucieknie nawiasom kwadratowym.) Kolejny problem, który widziałem i naprawiłem, dotyczył białych znaków. Za=(one "two " three)
, podobnie jak w przypadku Keegana: nie tylkoarray_contains a "two "
otrzymał fałszywy negatyw, alearray_contains a two
również fałszywie pozytywny. Łatwo to naprawić, ustawiającIFS
.eval _arr=( $(eval printf '[%q]="1"\ ' "\"\${$1[@]}\"") )
i możesz porzucićlocal IFS=
. Nadal występuje problem z pustymi polami w tablicy, ponieważ Bash odmówi utworzenia pustego klucza w tablicy asocjacyjnej. Szybki hacky sposób, aby to naprawić, polega na przygotowaniu fikcyjnego bohatera, powiedzx
:eval _arr=( $(eval printf '[x%q]="1"\ ' "\"\${$1[@]}\"") )
ireturn $(( 1 - 0${_arr[x$2]} ))
.Moja wersja techniki wyrażeń regularnych, która została już zasugerowana:
To, co się tutaj dzieje, polega na tym, że rozszerzasz całą tablicę obsługiwanych wartości na słowa i przygotowujesz określony ciąg, w tym przypadku „X-”, do każdej z nich, i robisz to samo z żądaną wartością. Jeśli ten rzeczywiście jest zawarty w tablicy, wynikowy ciąg znaków będzie co najwyżej pasował do jednego z powstałych tokenów lub wcale nie będzie odwrotnie. W tym ostatnim przypadku || operator uruchamia się i wiesz, że masz do czynienia z nieobsługiwaną wartością. Przed tym wszystkim żądana wartość jest usuwana ze wszystkich wiodących i końcowych białych znaków za pomocą standardowych operacji na łańcuchach powłoki.
Uważam, że jest czysty i elegancki, choć nie jestem zbyt pewny, jak skuteczny może być, jeśli Twój zestaw obsługiwanych wartości jest szczególnie duży.
źródło
Oto moje podejście do tego problemu. Oto krótka wersja:
I długa wersja, która moim zdaniem jest znacznie łatwiejsza dla oczu.
Przykłady:
źródło
test_arr=("hello" "world" "two words")
?Miałem przypadek, że musiałem sprawdzić, czy identyfikator znajduje się na liście identyfikatorów wygenerowanych przez inny skrypt / polecenie. Dla mnie zadziałało:
Możesz także skrócić / skompresować w następujący sposób:
W moim przypadku uruchomiłem jq, aby przefiltrować JSON pod kątem listy identyfikatorów i musiałem później sprawdzić, czy mój identyfikator znajduje się na tej liście i to działało najlepiej dla mnie. Nie będzie działać w przypadku ręcznie tworzonych tablic typu,
LIST=("1" "2" "4")
ale w przypadku danych wyjściowych skryptu oddzielonych znakiem nowej linii.PS .: nie mogę skomentować odpowiedzi, ponieważ jestem stosunkowo nowy ...
źródło
Poniższy kod sprawdza, czy dana wartość znajduje się w tablicy i zwraca jej zerowe przesunięcie:
Dopasowanie jest wykonywane dla pełnych wartości, dlatego ustawienie WARTOŚĆ = „trzy” nie będzie pasować.
źródło
Warto to zbadać, jeśli nie chcesz iterować:
Fragment dostosowany z: http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ Myślę, że jest całkiem sprytny.
EDYCJA: Prawdopodobnie możesz po prostu zrobić:
Ale to ostatnie działa tylko wtedy, gdy tablica zawiera unikalne wartości. Szukanie 1 w „143” da fałszywie dodatni, metinks.
źródło
Trochę późno, ale możesz użyć tego:
źródło
Wymyśliłem ten, który okazuje się działać tylko w Zsh, ale myślę, że ogólne podejście jest dobre.
Wyciągasz wzór z każdego elementu tylko wtedy, gdy zaczyna się
${arr[@]/#pattern/}
lub kończy${arr[@]/%pattern/}
. Te dwie substytucje działają w trybie bash, ale oba jednocześnie${arr[@]/#%pattern/}
działają tylko w Zsh.Jeśli zmodyfikowana tablica jest równa oryginałowi, to nie zawiera elementu.
Edytować:
Ten działa w bash:
Po podstawieniu porównuje długość obu tablic. Oczywiście, jeśli tablica zawiera element, podstawienie całkowicie go usunie, a liczba będzie się różnić.
źródło