Ich muss ein Element aus einem Array in der bash-Shell entfernen .. __ Im Allgemeinen würde ich einfach Folgendes tun:
array=("${(@)array:#<element to remove>}")
Leider ist das Element, das ich entfernen möchte, eine Variable, daher kann ich den vorherigen Befehl nicht verwenden. Hier ein Beispiel:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Irgendeine Idee?
Das Folgende funktioniert in bash
und zsh
wie Sie möchten:
$ array=(pluto pippo)
$ delete=(pluto)
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings
Wenn Sie mehr als ein Element löschen müssen:
...
$ delete=(pluto pippo)
for del in ${delete[@]}
do
array=("${array[@]/$del}") #Quotes when working with strings
done
Vorbehalt
Diese Technik entfernt tatsächlich Präfixe, die mit $delete
übereinstimmen, aus den Elementen, nicht unbedingt aus ganzen Elementen.
Update
Um ein genaues Element wirklich zu entfernen, müssen Sie durch das Array gehen, das Ziel mit jedem Element vergleichen und unset
verwenden, um eine genaue Übereinstimmung zu löschen.
array=(pluto pippo bob)
delete=(pippo)
for target in "${delete[@]}"; do
for i in "${!array[@]}"; do
if [[ ${array[i]} = "${delete[0]}" ]]; then
unset 'array[i]'
fi
done
done
Wenn Sie dies tun und ein oder mehrere Elemente entfernt werden, sind die Indizes nicht länger eine fortlaufende Folge von Ganzzahlen.
$ declare -p array
declare -a array=([0]="pluto" [2]="bob")
Die einfache Tatsache ist, dass Arrays nicht für die Verwendung als veränderliche Datenstrukturen konzipiert wurden. Sie werden hauptsächlich zum Speichern von Listen von Elementen 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 sind, müssen Sie das Array neu erstellen, um die Lücken zu füllen:
for i in "${!array[@]}"; do
new_array+=( "${array[i]}" )
done
array=("${new_array[@]}")
unset new_array
Sie können ein neues Array ohne das unerwünschte Element aufbauen und es dann wieder dem alten Array zuweisen. Dies funktioniert in bash
:
array=(pluto pippo)
new_array=()
for value in "${array[@]}"
do
[[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[@]}")
unset new_array
Dies ergibt:
echo "${array[@]}"
pippo
Dies ist der direkteste Weg, um einen Wert aufzuheben, wenn Sie wissen, wo er sich befindet.
$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
Um die obigen Antworten zu erweitern, können Sie folgende Elemente verwenden, um mehrere Elemente ohne teilweise Übereinstimmung aus einem Array zu entfernen:
ARRAY=(one two onetwo three four threefour "one six")
TO_REMOVE=(one four)
TEMP_ARRAY=()
for pkg in "${ARRAY[@]}"; do
for remove in "${TO_REMOVE[@]}"; do
KEEP=true
if [[ ${pkg} == ${remove} ]]; then
KEEP=false
break
fi
done
if ${KEEP}; then
TEMP_ARRAY+=(${pkg})
fi
done
ARRAY=("${TEMP_ARRAY[@]}")
unset TEMP_ARRAY
Dies führt zu einem Array, das Folgendes enthält: (Zwei auf drei, drei, "eins, sechs")
Hier ist eine einzeilige Lösung mit Mapfile:
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "<regexp>")
Beispiel:
$ arr=("Adam" "Bob" "Claire"$'\n'"Smith" "David" "Eve" "Fred")
$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"
Size: 6 Contents: Adam Bob Claire
Smith David Eve Fred
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "^Claire\nSmith$")
$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"
Size: 5 Contents: Adam Bob David Eve Fred
Diese Methode bietet große Flexibilität durch Ändern/Austauschen des Befehls grep und lässt keine leeren Zeichenfolgen im Array zurück.
Das POSIX-Shell-Skript hat keine Arrays.
Wahrscheinlich verwenden Sie also einen bestimmten Dialekt wie bash
, Kornmuscheln oder zsh
.
Daher kann Ihre Frage ab sofort nicht beantwortet werden.
Vielleicht funktioniert das für Sie:
unset array[$delete]
Hier ist eine (wahrscheinlich sehr bash-spezifische) kleine Funktion, die die Indirektion der bash-Variablen und unset
beinhaltet. Hierbei handelt es sich um eine allgemeine Lösung, bei der keine Textelemente ersetzt oder leere Elemente verworfen werden und es keine Probleme mit Zitaten/Leerzeichen usw. gibt.
delete_ary_elmt() {
local Word=$1 # the element to search for & delete
local aryref="$2[@]" # a necessary step since '${!$2[@]}' is a syntax error
local arycopy=("${!aryref}") # create a copy of the input array
local status=1
for (( i = ${#arycopy[@]} - 1; i >= 0; i-- )); do # iterate over indices backwards
elmt=${arycopy[$i]}
[[ $elmt == $Word ]] && unset "$2[$i]" && status=0 # unset matching elmts in orig. ary
done
return $status # return 0 if something was deleted; 1 if not
}
array=(a 0 0 b 0 0 0 c 0 d e 0 0 0)
delete_ary_elmt 0 array
for e in "${array[@]}"; do
echo "$e"
done
# prints "a" "b" "c" "d" in lines
Verwenden Sie es wie delete_ary_elmt ELEMENT ARRAYNAME
ohne $
-Siegel. Wechseln Sie den == $Word
in == $Word*
für Präfix-Übereinstimmungen. Verwenden Sie ${elmt,,} == ${Word,,}
für Übereinstimmungen, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird. usw., was auch immer bash [[
unterstützt.
Es funktioniert, indem die Indizes des Eingabearrays bestimmt und rückwärts iteriert werden (durch das Löschen von Elementen wird die Reihenfolge der Iterationen nicht beeinträchtigt). Um die Indizes zu erhalten, müssen Sie mit dem Namen auf das Eingabe-Array zugreifen. Dies kann über die Indirektion der Bash-Variablen x=1; varname=x; echo ${!varname} # prints "1"
erfolgen.
Sie können auf Arrays nicht über den Namen wie aryname=a; echo "${$aryname[@]}
zugreifen. Dies gibt einen Fehler. Sie können aryname=a; echo "${!aryname[@]}"
nicht tun, dies gibt Ihnen die Indizes der Variablen aryname
(obwohl es kein Array ist). Was funktioniert, ist aryref="a[@]"; echo "${!aryref}"
, wodurch die Elemente des Arrays a
gedruckt werden. Shell-Word-Anführungszeichen und Whitespace werden genauso wie echo "${a[@]}"
beibehalten. Dies funktioniert jedoch nur zum Drucken der Elemente eines Arrays, nicht zum Drucken der Länge oder der Indizes (aryref="!a[@]"
oder aryref="#a[@]"
oder "${!!aryref}"
oder "${#!aryref}"
, alle schlagen fehl).
Also kopiere ich das ursprüngliche Array per Bash Indirection bei seinem Namen und bekomme die Indizes aus der Kopie. Um die Indizes in umgekehrter Reihenfolge zu durchlaufen, verwende ich einen C-Style für die Schleife. Ich könnte es auch tun, indem ich über ${!arycopy[@]}
auf die Indizes zugreife und sie mit tac
umkehre, was eine cat
ist, die die Reihenfolge der Eingabezeile umkehrt.
Eine Funktionslösung ohne variable Indirektion würde wahrscheinlich eval
beinhalten müssen, was in dieser Situation möglicherweise nicht sicher ist (ich kann es nicht sagen).
Informationen zum Vermeiden von Konflikten mit dem Arrayindex mithilfe von unset
finden Sie unter https://stackoverflow.com/a/49626928/3223785 und https://stackoverflow.com/a/47798640)/3223785 Für weitere Informationen - ordnen Sie das Array sich selbst zu: ARRAY_VAR=(${ARRAY_VAR[@]})
.
#!/bin/bash
ARRAY_VAR=(0 1 2 3 4 5 6 7 8 9)
unset ARRAY_VAR[5]
unset ARRAY_VAR[4]
ARRAY_VAR=(${ARRAY_VAR[@]})
echo ${ARRAY_VAR[@]}
A_LENGTH=${#ARRAY_VAR[*]}
for (( i=0; i<=$(( $A_LENGTH -1 )); i++ )) ; do
echo ""
echo "INDEX - $i"
echo "VALUE - ${ARRAY_VAR[$i]}"
done
exit 0
[Ref .: https://tecadmin.net/working-with-array-bash-script/ ]
unset
verwenden
Um ein Element an einem bestimmten Index zu entfernen, können wir unset
verwenden und dann in ein anderes Array kopieren. Nur gerade unset
ist in diesem Fall nicht erforderlich. Da unset
das Element nicht entfernt, setzt es lediglich eine leere Zeichenfolge auf den jeweiligen Index im Array.
declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
unset 'arr[1]'
declare -a arr2=()
i=0
for element in "${arr[@]}"
do
arr2[$i]=$element
((++i))
done
echo "${arr[@]}"
echo "1st val is ${arr[1]}, 2nd val is ${arr[2]}"
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"
Ausgabe ist
aa cc dd ee
1st val is , 2nd val is cc
aa cc dd ee
1st val is cc, 2nd val is dd
Verwenden von :<idx>
Wir können einige Elemente auch mit :<idx>
entfernen. Wenn wir zum Beispiel das erste Element entfernen möchten, können wir :1
wie unten erwähnt verwenden.
declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
arr2=("${arr[@]:1}")
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"
Ausgabe ist
bb cc dd ee
1st val is cc, 2nd val is dd
In ZSH ist dies ein Kinderspiel (beachten Sie, dass dies zur Vereinfachung des Verständnisses mehr Bash-kompatible Syntax als erforderlich verwendet, als möglich):
# I always include an Edge case to make sure each element
# is not being Word split.
start=(one two three 'four 4' five)
work=(${(@)start})
idx=2
val=${work[idx]}
# How to remove a single element easily.
# Also works for associative arrays (at least in zsh)
work[$idx]=()
echo "Array size went down by one: "
[[ $#work -eq $(($#start - 1)) ]] && echo "OK"
echo "Array item "$val" is now gone: "
[[ -z ${work[(r)$val]} ]] && echo OK
echo "Array contents are as expected: "
wanted=("${start[@]:0:1}" "${start[@]:2}")
[[ "${(j.:.)wanted[@]}" == "${(j.:.)work[@]}" ]] && echo "OK"
echo "-- array contents: start --"
print -l -r -- "-- $#start elements" ${(@)start}
echo "-- array contents: work --"
print -l -r -- "-- $#work elements" "${work[@]}"
Ergebnisse:
Array size went down by one:
OK
Array item two is now gone:
OK
Array contents are as expected:
OK
-- array contents: start --
-- 5 elements
one
two
three
four 4
five
-- array contents: work --
-- 4 elements
one
three
four 4
five
So löschen Sie das erste Element im Array
unset 'array[0]'
Um das letzte Element im Array zu löschen
unset 'array[-1]'
http://wiki.bash-hackers.org/syntax/pe#substring_removal
$ {PARAMETER # PATTERN} # vom Anfang entfernen
$ {PARAMETER ## PATTERN} # Vom Anfang entfernen, gierige Übereinstimmung
$ {PARAMETER% PATTERN} # vom Ende entfernen
$ {PARAMETER %% PATTERN} # vom Ende entfernen, gierige Übereinstimmung
Um ein vollständiges Element zum Entfernen auszuführen, müssen Sie einen Befehl zum Zurücksetzen mit einer if-Anweisung ausführen. Wenn Sie keine Präfixe aus anderen Variablen entfernen oder Whitespace im Array unterstützen möchten, können Sie die Anführungszeichen weglassen und die Schleifen vergessen.
Im folgenden Beispiel finden Sie einige Möglichkeiten zum Bereinigen eines Arrays.
options=("foo" "bar" "foo" "foobar" "foo bar" "bars" "bar")
# remove bar from the start of each element
options=("${options[@]/#"bar"}")
# options=("foo" "" "foo" "foobar" "foo bar" "s" "")
# remove the complete string "foo" in a for loop
count=${#options[@]}
for ((i = 0; i < count; i++)); do
if [ "${options[i]}" = "foo" ] ; then
unset 'options[i]'
fi
done
# options=( "" "foobar" "foo bar" "s" "")
# remove empty options
# note the count variable can't be recalculated easily on a sparse array
for ((i = 0; i < count; i++)); do
# echo "Element $i: '${options[i]}'"
if [ -z "${options[i]}" ] ; then
unset 'options[i]'
fi
done
# options=("foobar" "foo bar" "s")
# list them with select
echo "Choose an option:"
PS3='Option? '
select i in "${options[@]}" Quit
do
case $i in
Quit) break ;;
*) echo "You selected \"$i\"" ;;
esac
done
Ausgabe
Choose an option:
1) foobar
2) foo bar
3) s
4) Quit
Option?
Hoffentlich hilft das.
Eigentlich ist mir gerade aufgefallen, dass die Shell-Syntax etwas eingebautes Verhalten aufweist, das eine einfache Rekonstruktion des Arrays ermöglicht, wenn ein Element, wie in der Frage gestellt, entfernt werden sollte.
# let's set up an array of items to consume:
x=()
for (( i=0; i<10; i++ )); do
x+=("$i")
done
# here, we consume that array:
while (( ${#x[@]} )); do
i=$(( $RANDOM % ${#x[@]} ))
echo "${x[i]} / ${x[@]}"
x=("${x[@]:0:i}" "${x[@]:i+1}")
done
Beachten Sie, wie wir das Array mit der x+=()
-Syntax von bash erstellt haben?
Sie könnten tatsächlich mehr als ein Element hinzufügen, den Inhalt eines ganzen anderen Arrays auf einmal.
Es gibt auch diese Syntax, z. wenn Sie das 2. Element löschen wollen:
array=("${array[@]:0:1}" "${array[@]:2}")
dies 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.