Potrzebowaliśmy skryptu, który symuluje tablice asocjacyjne lub strukturę danych podobną do mapy dla skryptów powłoki.
bash
shell
hashtable
associative-array
Irfan Zulfiqar
źródło
źródło
Inną opcją, jeśli przenośność nie jest Twoim głównym celem, jest użycie tablic asocjacyjnych wbudowanych w powłokę. Powinno to działać w bash 4.0 (dostępne teraz w większości głównych dystrybucji, ale nie na OS X, chyba że zainstalujesz go samodzielnie), ksh i zsh:
W zależności od powłoki może być konieczne wykonanie
typeset -A newmap
zamiastdeclare -A newmap
lub w niektórych przypadkach może to nie być konieczne.źródło
test -z ${variable+x}
(x
nie ma znaczenia, może to być dowolny ciąg). W przypadku tablicy asocjacyjnej w Bash możesz zrobić podobnie; używaćtest -z ${map[key]+x}
.Kolejny 4 sposób bez bash.
Możesz także wrzucić tam instrukcję if do wyszukiwania. if [[$ var = ~ / blah /]]. lub cokolwiek.
źródło
Myślę, że musisz cofnąć się i pomyśleć o tym, czym naprawdę jest mapa lub tablica asocjacyjna. Wszystko to jest sposobem na przechowywanie wartości dla danego klucza i szybkie i wydajne odzyskanie tej wartości. Możesz również chcieć mieć możliwość iteracji po kluczach, aby pobrać każdą parę klucz-wartość lub usunąć klucze i powiązane z nimi wartości.
Teraz pomyśl o strukturze danych, której używasz cały czas w skryptach powłoki, a nawet tylko w powłoce bez pisania skryptu, który ma takie właściwości. Zaskoczony? To system plików.
Naprawdę, wszystko, czego potrzebujesz, aby mieć tablicę asocjacyjną w programowaniu powłoki, to katalog tymczasowy.
mktemp -d
jest konstruktorem tablicy asocjacyjnej:Jeśli nie masz ochoty używać
echo
icat
, zawsze możesz napisać kilka małych opakowań; te są wzorowane na Irfan, chociaż po prostu wyświetlają wartość, a nie ustawiają dowolne zmienne, takie jak$value
:edycja : To podejście jest właściwie trochę szybsze niż wyszukiwanie liniowe przy użyciu seda sugerowanego przez pytającego, a także bardziej niezawodne (pozwala kluczom i wartościom zawierać -, =, spację, qnd ": SP:"). Fakt, że używa systemu plików, nie spowalnia go; w rzeczywistości nigdy nie ma gwarancji, że te pliki zostaną zapisane na dysku, chyba że zadzwonisz
sync
; w przypadku takich plików tymczasowych o krótkim czasie życia nie jest nieprawdopodobne, że wiele z nich nigdy nie zostanie zapisanych na dysku.Zrobiłem kilka testów porównawczych kodu Irfana, modyfikacji kodu Irfana przez Jerry'ego i mojego kodu, używając następującego programu sterownika:
Wyniki:
źródło
Bash4 obsługuje to natywnie. Nie używaj
grep
lubeval
, są najbrzydszymi hackami.Pełną, szczegółową odpowiedź z przykładowym kodem można znaleźć pod adresem : /programming/3467959
źródło
Przykład:
źródło
Teraz odpowiadam na to pytanie.
Poniższe skrypty symulują tablice asocjacyjne w skryptach powłoki. Jego proste i bardzo łatwe do zrozumienia.
Mapa to nic innego jak niekończący się ciąg, który ma keyValuePair zapisany jako --name = Irfan --designation = SSE --company = My: SP: Own: SP: Company
spacje są zastępowane przez „: SP:” dla wartości
edycja: Właśnie dodano kolejną metodę pobierania wszystkich kluczy.
źródło
eval
„ing dane tak, jakby jego kod bash, a co więcej: nie uda się go cytować poprawnie. Obie powodują masę błędów i wstrzyknięcie dowolnego kodu.W przypadku Bash 3 istnieje szczególny przypadek, który ma ładne i proste rozwiązanie:
Jeśli nie chcesz obsługiwać wielu zmiennych lub klucze są po prostu nieprawidłowymi identyfikatorami zmiennych, a Twoja tablica ma mniej niż 256 pozycji , możesz nadużywać wartości zwracanych przez funkcję. To rozwiązanie nie wymaga żadnej podpowłoki, ponieważ wartość jest łatwo dostępna jako zmienna, ani żadnej iteracji, aby wydajność krzyczała. Jest również bardzo czytelny, prawie jak wersja Bash 4.
Oto najbardziej podstawowa wersja:
Pamiętaj, używaj pojedynczych cudzysłowów w
case
, w przeciwnym razie podlega globalizacji. Naprawdę przydatne do statycznych / zamrożonych skrótów od samego początku, ale można by napisać generator indeksów zhash_keys=()
tablicy.Uważaj, domyślnie jest to pierwszy, więc możesz odłożyć na bok element zerowy:
Uwaga: długość jest teraz nieprawidłowa.
Alternatywnie, jeśli chcesz zachować indeksowanie od zera, możesz zarezerwować inną wartość indeksu i zabezpieczyć się przed nieistniejącym kluczem, ale jest mniej czytelny:
Lub, aby zachować poprawną długość, przesuń indeks o jeden:
źródło
Możesz używać dynamicznych nazw zmiennych i pozwolić im działać jak klucze w tablicy mieszającej.
Na przykład, jeśli masz plik wejściowy z dwiema kolumnami, imieniem i kredytem, jak w przykładzie poniżej, i chcesz zsumować dochód każdego użytkownika:
Polecenie poniżej zsumuje wszystko, używając zmiennych dynamicznych jako kluczy, w postaci mapy _ $ {person} :
Aby przeczytać wyniki:
Wynik będzie:
Rozwijając te techniki, rozwijam na GitHub funkcję, która działa podobnie jak obiekt HashMap , shell_map .
W celu tworzenia " instancji HashMap " funkcja shell_map może tworzyć swoje kopie pod różnymi nazwami. Każda nowa kopia funkcji będzie miała inną zmienną $ FUNCNAME. Następnie $ FUNCNAME jest używany do tworzenia przestrzeni nazw dla każdej instancji Map.
Klucze mapy są zmiennymi globalnymi w postaci $ FUNCNAME_DATA_ $ KEY, gdzie $ KEY jest kluczem dodanym do mapy. Te zmienne są zmiennymi dynamicznymi .
Poniżej zamieszczę uproszczoną wersję, abyś mógł użyć jako przykładu.
Stosowanie:
źródło
Jeszcze inny sposób niezgodny z bash-4 (tj. Bash 3, Mac):
Wydruki:
Funkcja z
case
atrybutami działa jak tablica asocjacyjna. Niestety nie może go używaćreturn
, więc maecho
swoje wyjście, ale nie stanowi to problemu, chyba że jesteś purystą, który stroni od rozwidlających podpowłok.źródło
Szkoda, że wcześniej nie widziałem tego pytania - napisałem bibliotekę shell-framework, która zawiera między innymi mapy (tablice asocjacyjne). Ostatnią wersję można znaleźć tutaj .
Przykład:
źródło
Dodanie kolejnej opcji, jeśli jq jest dostępne:
źródło
Jak już wspomniałem, prawdą jest, że najlepszą metodą jest zapisanie klucza / wartości do pliku, a następnie użycie grep / awk do ich odzyskania. Brzmi jak wszelkiego rodzaju niepotrzebne IO, ale pamięć podręczna dysku włącza się i sprawia, że jest niezwykle wydajna - znacznie szybsza niż próba przechowywania ich w pamięci za pomocą jednej z powyższych metod (jak pokazują testy porównawcze).
Oto szybka, czysta metoda, którą lubię:
Jeśli chcesz wymusić pojedynczą wartość na klucz, możesz również wykonać małą akcję grep / sed w hput ().
źródło
kilka lat temu napisałem bibliotekę skryptów dla basha, która wspierała między innymi tablice asocjacyjne (logowanie, pliki konfiguracyjne, rozszerzone wsparcie dla argumentów wiersza poleceń, generowanie pomocy, testowanie jednostkowe itp.). Biblioteka zawiera opakowanie dla tablic asocjacyjnych i automatycznie przełącza się na odpowiedni model (wewnętrzny dla bash4 i emulowany dla poprzednich wersji). Nazywał się shell-framework i był hostowany na origo.ethz.ch, ale dziś zasób jest zamknięty. Jeśli ktoś nadal tego potrzebuje, mogę się tym z tobą podzielić.
źródło
Shell nie ma wbudowanej mapy, takiej jak struktura danych, używam nieprzetworzonego ciągu do opisywania takich elementów:
podczas wyodrębniania przedmiotów i ich atrybutów:
Wydaje się, że nie jest to mądre niż odpowiedź innych ludzi, ale łatwe do zrozumienia dla nowych ludzi.
źródło
Zmodyfikowałem rozwiązanie Vadima w następujący sposób:
Zmiana polega na map_get, aby zapobiec zwracaniu błędów, jeśli zażądasz klucza, który nie istnieje, chociaż efektem ubocznym jest to, że po cichu ignoruje brakujące mapy, ale lepiej pasuje do mojego przypadku użycia, ponieważ po prostu chciałem sprawdzić klucz, aby pominąć elementy w pętli.
źródło
Późna odpowiedź, ale rozważ rozwiązanie problemu w ten sposób, używając wbudowanego bash odczytanego, jak zilustrowano we fragmencie kodu z poniższego skryptu firewall ufw. Takie podejście ma tę zaletę, że wykorzystuje dowolną liczbę rozdzielonych zestawów pól (nie tylko 2). Użyliśmy | separator, ponieważ specyfikatory zakresu portów mogą wymagać dwukropka, np. 6001: 6010 .
źródło