Muszę usunąć element z tablicy w powłoce bash. Generalnie po prostu zrobiłbym:
array=("${(@)array:#<element to remove>}")
Niestety element, który chcę usunąć, jest zmienną, więc nie mogę użyć poprzedniego polecenia. Oto przykład:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Dowolny pomysł?
zsh
.array=( ${array[@]/$delete} )
działa zgodnie z oczekiwaniami w Bash. Czy po prostu przegapiłeś=
?Odpowiedzi:
Następujące działa tak, jak chcesz w
bash
izsh
:Jeśli chcesz usunąć więcej niż jeden element:
Caveat
Ta technika w rzeczywistości usuwa przedrostki pasujące
$delete
do elementów, niekoniecznie całe elementy.Aktualizacja
Aby naprawdę usunąć dokładny element, musisz przejść przez tablicę, porównać cel z każdym elementem i użyć
unset
do usunięcia dokładnego dopasowania.Zauważ, że jeśli to zrobisz i jeden lub więcej elementów zostanie usuniętych, indeksy nie będą już ciągłą sekwencją liczb całkowitych.
Prosty fakt jest taki, że tablice nie zostały zaprojektowane do użytku jako zmienne struktury danych. Są używane głównie do przechowywania list elementów w pojedynczej zmiennej bez konieczności marnowania znaku jako separatora (np. Do przechowywania listy ciągów, które mogą zawierać spacje).
Jeśli luki stanowią problem, musisz przebudować tablicę, aby wypełnić luki:
źródło
$ array=(sun sunflower)
$ delete=(sun)
$ echo ${array[@]/$delete}
wyniki wflower
(pluto1 pluto2 pippo)
, to skończysz z(1 2 pippo)
.for element in "${array[@]}" do if [[ $element ]]; then echo ${element} fi done
Możesz utworzyć nową tablicę bez niepożądanego elementu, a następnie przypisać ją z powrotem do starej tablicy. Działa to w
bash
:To daje:
źródło
Jest to najbardziej bezpośredni sposób usunięcia wartości, jeśli znasz jej pozycję.
źródło
echo ${array[1]}
, otrzymasz pusty ciąg. A żeby dostaćthree
, musisz zrobićecho ${array[2]}
. Nieunset
jest to więc właściwy mechanizm usuwania elementu z tablicy bash.${array[1]+x}
jest ciągiem zerowym, więc niearray[1]
jest ustawione.unset
nie zmienia indeksów pozostałych elementów. Podawanie argumentu za nieustawionym nie jest potrzebne. Sposób niszczenia elementu tablicy jest opisany w podręczniku Bash .${array[1]}
istnieje tylko dlatego, że rozmiar wynosi 2. Jeśli chcesz indeksów, sprawdź${!array[@]}
.Oto rozwiązanie jednowierszowe z plikiem mapy:
Przykład:
Ta metoda pozwala na dużą elastyczność poprzez modyfikację / wymianę polecenia grep i nie pozostawia żadnych pustych ciągów w tablicy.
źródło
printf '%s\n' "${array[@]}"
zamiast tego brzydkiegoIFS
/echo
rzeczy.-d $'\0'
działa doskonale, podczas gdy samo-d
bez argumentu nie.-d $'\0'
to, że jest taki sam-d $'\0 something'
lub sprawiedliwy-d ''
.$'\0'
dla jasnościTa odpowiedź jest specyficzna dla przypadku usuwania wielu wartości z dużych tablic, w których ważna jest wydajność.
Najczęściej głosowanymi rozwiązaniami są (1) podstawianie wzorców w tablicy lub (2) iterowanie po elementach tablicy. Pierwsza jest szybka, ale może obsługiwać tylko elementy, które mają odrębny prefiks, druga ma O (n * k), n = rozmiar tablicy, k = elementy do usunięcia. Tablica asocjacyjna jest stosunkowo nową funkcją i mogła nie być powszechna, gdy pytanie zostało pierwotnie opublikowane.
Dla przypadku dokładnego dopasowania, z dużymi n i k, możliwe jest zwiększenie wydajności od O (n k) do O (n + k log (k)). W praktyce O (n) zakładając, że k jest znacznie mniejsze niż n. Większość przyspieszenia opiera się na wykorzystaniu tablicy asocjacyjnej do identyfikacji elementów do usunięcia.
Wydajność (rozmiar n-tablicy, wartości k do usunięcia). Wydajność mierzy sekundy czasu użytkownika
Zgodnie z oczekiwaniami,
current
rozwiązanie jest liniowe do N * K, afast
rozwiązanie jest praktycznie liniowe do K, ze znacznie niższą stałą.fast
Rozwiązanie jest nieco wolniejsza vscurrent
roztworu gdy k = 1, ze względu na dodatkową konfigurację.Rozwiązanie „Szybkie”: tablica = lista danych wejściowych, usuń = lista wartości do usunięcia.
Porównanie z
current
rozwiązaniem, na podstawie najczęściej głosowanej odpowiedzi.źródło
Oto (prawdopodobnie bardzo specyficzna dla basha) mała funkcja obejmująca pośrednią zmienną basha i
unset
; jest to ogólne rozwiązanie, które nie obejmuje podstawiania tekstu ani odrzucania pustych elementów i nie ma problemów z cytowaniem / białymi spacjami itp.Użyj go jak
delete_ary_elmt ELEMENT ARRAYNAME
bez$
pieczęci. Przełącz== $word
for== $word*
dla dopasowań przedrostków; użyj${elmt,,} == ${word,,}
do dopasowań bez rozróżniania wielkości liter; itp., cokolwiek[[
obsługuje bash .Działa poprzez określenie indeksów tablicy wejściowej i iterację po nich wstecz (więc usuwanie elementów nie zaburza kolejności iteracji). Aby uzyskać indeksy, musisz uzyskać dostęp do tablicy wejściowej według nazwy, co można zrobić za pośrednictwem pośredniej zmiennej bash
x=1; varname=x; echo ${!varname} # prints "1"
.Nie możesz uzyskać dostępu do tablic według nazwy, na przykład
aryname=a; echo "${$aryname[@]}
powoduje to błąd. Nie możesz tego zrobićaryname=a; echo "${!aryname[@]}"
, to daje indeksy zmiennejaryname
(chociaż nie jest to tablica). Toaryref="a[@]"; echo "${!aryref}"
, co działa , to wypisanie elementów tablicya
, z zachowaniem cytowania słów powłoki i białych znaków dokładnie tak, jakecho "${a[@]}"
. Ale to działa tylko przy drukowaniu elementów tablicy, a nie przy drukowaniu jej długości lub indeksów (aryref="!a[@]"
lubaryref="#a[@]"
albo"${!!aryref}"
albo"${#!aryref}"
, wszystkie zawodzą).Więc kopiuję oryginalną tablicę według jej nazwy przez pośredni bash i pobieram indeksy z kopii. Aby iterować indeksy w odwrotnej kolejności, używam pętli for w stylu C. Mógłbym to również zrobić, uzyskując dostęp do indeksów za pomocą
${!arycopy[@]}
i odwracając je za pomocątac
, co powodujecat
zmianę kolejności linii wejściowych.Rozwiązanie funkcyjne bez zmiennej pośredniej prawdopodobnie musiałoby obejmować
eval
, co może, ale nie musi, być bezpieczne w tej sytuacji (nie mogę powiedzieć).źródło
delete_ary_elmt "d" array
a następnie ponownie wydrukować tablicę. Zobaczysz, że zły element zostanie usunięty. Usunięcie ostatniego elementu również wtedy nigdy nie zadziała.Aby rozwinąć powyższe odpowiedzi, można użyć następujących poleceń do usunięcia wielu elementów z tablicy bez częściowego dopasowania:
Spowoduje to powstanie tablicy zawierającej: (dwa jeden dwa trzy trzy cztery „jeden sześć”)
źródło
Jeśli ktoś znajdzie się w sytuacji, w której musi zapamiętać wartości set -e lub set -x i móc je przywrócić, zapoznaj się z tym streszczeniem, które używa pierwszego rozwiązania do usuwania tablicy do zarządzania własnym stosem:
https://gist.github.com/kigster/94799325e39d2a227ef89676eed44cc6
źródło
Tylko częściowa odpowiedź
Aby usunąć pierwszy element z tablicy
Aby usunąć ostatni element z tablicy
źródło
unset
.array0
w bieżącym katalogu, to ponieważarray[0]
jest to glob, zostanie on najpierw rozwiniętyarray0
przed komendą unset.Za pomocą
unset
Aby usunąć element pod określonym indeksem, możemy użyć,
unset
a następnie skopiować do innej tablicy. Tylkounset
w tym przypadku nie jest to wymagane. Ponieważunset
nie usuwa elementu, po prostu ustawia ciąg pusty na określony indeks w tablicy.Wyjście jest
Za pomocą
:<idx>
Możemy usunąć jakiś zestaw elementów za pomocą
:<idx>
również. Na przykład, jeśli chcemy usunąć pierwszy element, możemy użyć,:1
jak wspomniano poniżej.Wyjście jest
źródło
Skrypt powłoki POSIX nie ma tablic.
Więc najprawdopodobniej używasz określonego dialektu, takiego jak
bash
korn shells lubzsh
.W związku z tym na twoje pytanie nie można obecnie odpowiedzieć.
Może to działa dla Ciebie:
źródło
Właściwie właśnie zauważyłem, że składnia powłoki ma pewne wbudowane zachowanie, które pozwala na łatwą rekonstrukcję tablicy, gdy, zgodnie z pytaniem, element powinien zostać usunięty.
Zauważ, jak zbudowaliśmy tablicę przy użyciu
x+=()
składni basha ?W rzeczywistości możesz dodać więcej niż jeden element jednocześnie, zawartość całej innej tablicy.
źródło
http://wiki.bash-hackers.org/syntax/pe#substring_removal
Aby wykonać pełne usunięcie elementu, musisz wykonać polecenie unset z instrukcją if. Jeśli nie zależy Ci na usuwaniu prefiksów z innych zmiennych lub obsłudze białych znaków w tablicy, możesz po prostu porzucić cudzysłowy i zapomnieć o pętlach for.
Zobacz poniższy przykład, aby zobaczyć kilka różnych sposobów czyszczenia tablicy.
Wynik
Mam nadzieję, że to pomoże.
źródło
W ZSH jest to banalnie proste (zauważ, że używa bardziej składni kompatybilnej z bash niż jest to konieczne, aby ułatwić zrozumienie):
Wyniki:
źródło
Jest też taka składnia, np. Jeśli chcesz usunąć drugi element:
co jest w rzeczywistości połączeniem 2 zakładek. Pierwszy od indeksu 0 do indeksu 1 (wyłączny), a drugi od indeksu 2 do końca.
źródło
Co robię to:
BAM, ta pozycja została usunięta.
źródło
array=('first item' 'second item')
.Jest to szybkie i brudne rozwiązanie, które będzie działać w prostych przypadkach, ale zepsuje się, jeśli (a)
$delete
w elementach znajdują się znaki specjalne wyrażenia regularnego lub (b) w jakimkolwiek elemencie są spacje. Począwszy od:Usuń wszystkie wpisy dokładnie pasujące do
$delete
:w wyniku
echo $array
-> pippo i upewniając się, że jest to tablica:echo $array[1]
-> pippofmt
jest trochę niejasny:fmt -1
zawija w pierwszej kolumnie (aby umieścić każdy element w osobnym wierszu. W tym miejscu pojawia się problem z elementami wfmt -999999
spacjach ). rozpakowuje go z powrotem do jednej linii, przywracając odstępy między elementami. Są na to inne sposoby, na przykładxargs
.Dodatek: Jeśli chcesz usunąć tylko pierwsze dopasowanie, użyj seda, jak opisano tutaj :
źródło
A może coś takiego:
źródło
Aby uniknąć konfliktów z indeksem tablicy przy użyciu
unset
- patrz https://stackoverflow.com/a/49626928/3223785 i https://stackoverflow.com/a/47798640/3223785 więcej informacji - przypisanie tablicy do siebie:ARRAY_VAR=(${ARRAY_VAR[@]})
.[Odn .: https://tecadmin.net/working-with-array-bash-script/ ]
źródło
źródło