Ich muss ein Element aus einem Array in der Bash-Shell entfernen. Im Allgemeinen würde ich einfach tun:
array=("${(@)array:#<element to remove>}")
Leider ist das Element, das ich entfernen möchte, eine Variable, sodass ich den vorherigen Befehl nicht verwenden kann. Hier unten ein Beispiel:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Irgendeine Idee?
zsh
.array=( ${array[@]/$delete} )
funktioniert wie erwartet in Bash. Hast du das einfach verpasst=
?Antworten:
Folgendes funktioniert wie in
bash
undzsh
:Wenn Sie mehr als ein Element löschen müssen:
Vorbehalt
Diese Technik entfernt tatsächlich Präfixe, die mit
$delete
den Elementen übereinstimmen , nicht unbedingt ganze Elemente.Aktualisieren
Um ein genaues Element wirklich zu entfernen, müssen Sie durch das Array gehen, das Ziel mit jedem Element vergleichen und
unset
eine genaue Übereinstimmung löschen.Wenn Sie dies tun und ein oder mehrere Elemente entfernt werden, sind die Indizes keine fortlaufende Folge von Ganzzahlen mehr.
Die einfache Tatsache ist, dass Arrays nicht für die Verwendung als veränderbare Datenstrukturen konzipiert wurden. Sie werden hauptsächlich zum Speichern von Elementlisten in einer einzelnen Variablen verwendet, ohne dass ein Zeichen als Trennzeichen verschwendet werden muss (z. B. zum Speichern einer Liste von Zeichenfolgen, die Leerzeichen enthalten können).
Wenn Lücken ein Problem darstellen, müssen Sie das Array neu erstellen, um die Lücken zu füllen:
quelle
$ array=(sun sunflower)
$ delete=(sun)
$ echo ${array[@]/$delete}
Ergebnisse inflower
(pluto1 pluto2 pippo)
ist, werden Sie am Ende mit(1 2 pippo)
.for element in "${array[@]}" do if [[ $element ]]; then echo ${element} fi done
Sie können ein neues Array ohne das unerwünschte Element erstellen und es dann wieder dem alten Array zuweisen. Dies funktioniert in
bash
:Dies ergibt:
quelle
Dies ist der direkteste Weg, um einen Wert zu deaktivieren, wenn Sie seine Position kennen.
quelle
echo ${array[1]}
, Sie erhalten eine Nullzeichenfolge. Und umthree
Sie zu bekommen , müssen Sie tunecho ${array[2]}
. Esunset
ist also nicht der richtige Mechanismus, um ein Element im Bash-Array zu entfernen.${array[1]+x}
ist eine Nullzeichenfolge, daherarray[1]
nicht gesetzt.unset
ändert die Indizes der verbleibenden Elemente nicht. Das Zitieren des Arguments für nicht gesetzt ist nicht erforderlich. Die Art und Weise, ein Array-Element zu zerstören, wird im Bash-Handbuch beschrieben .${array[1]}
existiert, nur weil die Größe 2 ist. Wenn Sie die Indizes möchten, überprüfen Sie${!array[@]}
.Hier ist eine einzeilige Lösung mit Mapfile:
Beispiel:
Diese Methode ermöglicht eine große Flexibilität durch Ändern / Austauschen des Befehls grep und hinterlässt keine leeren Zeichenfolgen im Array.
quelle
printf '%s\n' "${array[@]}"
anstelle dieses hässlichenIFS
/echo
Dings.-d $'\0'
funktioniert einwandfrei, nur-d
ohne das Argument nicht.-d $'\0'
ist das gleiche wie-d $'\0 something'
oder nur-d ''
.$'\0'
aus Gründen der Klarheit zu verwendenDiese Antwort ist spezifisch für den Fall, dass mehrere Werte aus großen Arrays gelöscht werden, bei denen die Leistung wichtig ist.
Die am häufigsten gewählten Lösungen sind (1) Mustersubstitution in einem Array oder (2) Iteration über die Array-Elemente. Das erste ist schnell, kann jedoch nur Elemente mit einem bestimmten Präfix behandeln, das zweite hat O (n * k), n = Arraygröße, k = zu entfernende Elemente. Assoziative Arrays sind relativ neue Funktionen und waren möglicherweise nicht üblich, als die Frage ursprünglich gestellt wurde.
Für den exakten Übereinstimmungsfall mit großem n und k kann die Leistung von O (n k) auf O (n + k log (k)) verbessert werden . In der Praxis nimmt O (n) an, dass k viel niedriger als n ist. Der größte Teil der Beschleunigung basiert auf der Verwendung eines assoziativen Arrays zur Identifizierung der zu entfernenden Elemente.
Leistung (n-Array-Größe, zu löschende k-Werte). Die Leistung misst Sekunden der Benutzerzeit
Wie erwartet ist die
current
Lösung linear zu N * K und diefast
Lösung ist praktisch linear zu K mit einer viel niedrigeren Konstante. Diefast
Lösung istcurrent
aufgrund des zusätzlichen Aufbaus etwas langsamer als die Lösung, wenn k = 1 ist.Die 'schnelle' Lösung: Array = Liste der Eingaben, Löschen = Liste der zu entfernenden Werte.
Vergleich mit der
current
Lösung anhand der am häufigsten gewählten Antwort.quelle
Hier ist eine (wahrscheinlich sehr bash-spezifische) kleine Funktion, die die Indirektion von Bash-Variablen beinhaltet und
unset
; Es handelt sich um eine allgemeine Lösung, bei der kein Text ersetzt oder leere Elemente verworfen werden und keine Probleme mit Anführungszeichen / Leerzeichen usw. auftreten.Verwenden Sie es wie
delete_ary_elmt ELEMENT ARRAYNAME
ohne$
Siegel. Schalten Sie das== $word
für== $word*
für Präfix-Übereinstimmungen; Verwendung${elmt,,} == ${word,,}
für Übereinstimmungen ohne Berücksichtigung der Groß- und Kleinschreibung; usw., was auch immer Bash[[
unterstützt.Dabei werden die Indizes des Eingabearrays ermittelt und rückwärts durchlaufen (das Löschen von Elementen führt also nicht zu einer Verschiebung der Iterationsreihenfolge). Um die Indizes zu erhalten, müssen Sie nach Namen auf das Eingabearray zugreifen. Dies kann über die Indirektion von Bash-Variablen erfolgen
x=1; varname=x; echo ${!varname} # prints "1"
.Sie können nicht namentlich auf Arrays zugreifen
aryname=a; echo "${$aryname[@]}
, dies gibt Ihnen einen Fehler. Sie können nicht tunaryname=a; echo "${!aryname[@]}"
, dies gibt Ihnen die Indizes der Variablenaryname
(obwohl es sich nicht um ein Array handelt). Was funktioniert, istaryref="a[@]"; echo "${!aryref}"
, dass die Elemente des Arrays gedruckt werdena
, wobei Shell-Word-Anführungszeichen und Leerzeichen genau wie erhalten bleibenecho "${a[@]}"
. Dies funktioniert jedoch nur zum Drucken der Elemente eines Arrays, nicht zum Drucken seiner Länge oder Indizes (aryref="!a[@]"
oderaryref="#a[@]"
oder"${!!aryref}"
oder"${#!aryref}"
, alle schlagen fehl).Also kopiere ich das ursprüngliche Array durch seinen Namen per Bash-Indirektion und erhalte die Indizes aus der Kopie. Um die Indizes in umgekehrter Reihenfolge zu durchlaufen, verwende ich eine for-Schleife im C-Stil. Ich könnte es auch tun, indem ich über auf die Indizes zugreife
${!arycopy[@]}
und sie umkehretac
, was eine Umkehrungcat
der Eingabezeilenreihenfolge ist.Eine Funktionslösung ohne variable Indirektion müsste wahrscheinlich beinhalten
eval
, was in dieser Situation sicher sein kann oder nicht (ich kann es nicht sagen).quelle
delete_ary_elmt "d" array
das Array auszuführen und anschließend erneut zu drucken. Sie werden sehen, dass das falsche Element entfernt wird. Das Entfernen des letzten Elements funktioniert dann auch nie.Um die obigen Antworten zu erweitern, können Sie die folgenden Elemente verwenden, um mehrere Elemente ohne teilweise Übereinstimmung aus einem Array zu entfernen:
Dies führt zu einem Array, das Folgendes enthält: (zwei, zwei, drei, drei, vier "eins, sechs")
quelle
Wenn sich jemand in einer Position befindet, in der er sich die Werte von set -e oder set -x merken und wiederherstellen muss, lesen Sie bitte diese Übersicht, in der die erste Lösung zum Löschen von Arrays zum Verwalten des eigenen Stapels verwendet wird:
https://gist.github.com/kigster/94799325e39d2a227ef89676eed44cc6
quelle
Nur teilweise Antwort
So löschen Sie das erste Element im Array
So löschen Sie das letzte Element im Array
quelle
unset
.array0
im aktuellen Verzeichnis haben, wird diese, da siearray[0]
glob ist, zuerstarray0
vor dem Befehl unset erweitert.Verwenden von
unset
Um ein Element an einem bestimmten Index zu entfernen, können wir es verwenden
unset
und dann in ein anderes Array kopieren. Nur geradeunset
ist in diesem Fall nicht erforderlich. Daunset
das Element nicht entfernt wird, wird lediglich eine Nullzeichenfolge für den bestimmten Index im Array festgelegt.Ausgabe ist
Verwenden von
:<idx>
Wir können einige Elemente auch mit entfernen
:<idx>
. Wenn wir zum Beispiel das erste Element entfernen möchten, können wir es:1
wie unten erwähnt verwenden.Ausgabe ist
quelle
Das POSIX-Shell-Skript verfügt nicht über Arrays.
Sie verwenden also höchstwahrscheinlich einen bestimmten Dialekt wie
bash
Kornschalen oderzsh
.Daher kann Ihre Frage derzeit nicht beantwortet werden.
Vielleicht funktioniert das für Sie:
quelle
Eigentlich ist mir gerade aufgefallen, dass in der Shell-Syntax ein Verhalten eingebaut ist, das eine einfache Rekonstruktion des Arrays ermöglicht, wenn, wie in der Frage gestellt, ein Element entfernt werden sollte.
Beachten Sie, wie wir das Array mit der Bash-
x+=()
Syntax erstellt haben?Sie können damit tatsächlich mehr als ein Element hinzufügen, den Inhalt eines ganzen anderen Arrays gleichzeitig.
quelle
http://wiki.bash-hackers.org/syntax/pe#substring_removal
Um ein vollständig entferntes Element auszuführen, müssen Sie einen nicht gesetzten Befehl mit einer if-Anweisung ausführen. Wenn Sie keine Präfixe aus anderen Variablen entfernen oder Leerzeichen im Array unterstützen möchten, können Sie einfach die Anführungszeichen löschen und for-Schleifen vergessen.
Im folgenden Beispiel finden Sie einige verschiedene Möglichkeiten zum Bereinigen eines Arrays.
Ausgabe
Hoffentlich hilft das.
quelle
In ZSH ist dies kinderleicht (beachten Sie, dass zum besseren Verständnis mehr bash-kompatible Syntax als erforderlich verwendet wird):
Ergebnisse:
quelle
Es gibt auch diese Syntax, z. B. wenn Sie das 2. Element löschen möchten:
Das ist in der Tat die Verkettung von 2 Registerkarten. Der erste vom Index 0 bis zum Index 1 (exklusiv) und der zweite vom Index 2 bis zum Ende.
quelle
Was ich tue ist:
BAM, dieser Gegenstand wird entfernt.
quelle
array=('first item' 'second item')
.Dies ist eine schnelle und schmutzige Lösung, die in einfachen Fällen funktioniert, aber nicht funktioniert, wenn (a) Regex-Sonderzeichen enthalten
$delete
sind oder (b) in Elementen überhaupt Leerzeichen vorhanden sind. Beginnen mit:Löschen Sie alle Einträge, die genau übereinstimmen
$delete
:in resultierenden
echo $array
-> Pippo, und sicherstellen , dass es ein Array:echo $array[1]
-> Pippofmt
ist ein wenig dunkel:fmt -1
Zeilenumbrüche in der ersten Spalte (um jedes Element in eine eigene Zeile zu setzen. Hier tritt das Problem bei Elementen in Leerzeichen auf.)fmt -999999
wickeln es in eine Zeile zurück und setzen die Leerzeichen zwischen den Elementen zurück. Es gibt andere Möglichkeiten, dies zu tun, wie zxargs
.Nachtrag: Wenn Sie nur die erste Übereinstimmung löschen möchten, verwenden Sie sed wie hier beschrieben :
quelle
Wie wäre es mit so etwas wie:
quelle
Um Konflikte mit dem Array-Index zu vermeiden, indem Sie
unset
- siehe https://stackoverflow.com/a/49626928/3223785 und https://stackoverflow.com/a/47798640/3223785 für weitere Informationen - das Array sich selbst neu zuweisen :ARRAY_VAR=(${ARRAY_VAR[@]})
.[Ref.: Https://tecadmin.net/working-with-array-bash-script/ ]
quelle
quelle