Je dois supprimer un élément d'un tableau dans bash Shell . En général, je ferais simplement:
array=("${(@)array:#<element to remove>}")
Malheureusement, l'élément que je veux supprimer est une variable, je ne peux donc pas utiliser la commande précédente . Voici un exemple:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Une idée?
Ce qui suit fonctionne comme vous le souhaitez dans bash
et zsh
:
$ array=(pluto pippo)
$ delete=(pluto)
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings
Si besoin de supprimer plus d'un élément:
...
$ delete=(pluto pippo)
for del in ${delete[@]}
do
array=("${array[@]/$del}") #Quotes when working with strings
done
Caveat
Cette technique supprime les préfixes correspondant à $delete
des éléments, pas nécessairement des éléments entiers.
Mettre à jour
Pour vraiment supprimer un élément exact, vous devez parcourir le tableau, en comparant la cible à chaque élément et en utilisant unset
pour supprimer une correspondance exacte.
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
Notez que si vous procédez ainsi et qu'un ou plusieurs éléments sont supprimés, les index ne seront plus une séquence continue d'entiers.
$ declare -p array
declare -a array=([0]="pluto" [2]="bob")
Le fait est que les tableaux n'ont pas été conçus pour être utilisés comme structures de données mutables. Ils sont principalement utilisés pour stocker des listes d'éléments dans une seule variable sans avoir à gaspiller un caractère en tant que séparateur (par exemple, pour stocker une liste de chaînes pouvant contenir des espaces).
Si les lacunes sont un problème, vous devez reconstruire le tableau pour combler les lacunes:
for i in "${!array[@]}"; do
new_array+=( "${array[i]}" )
done
array=("${new_array[@]}")
unset new_array
Vous pouvez créer un nouveau tableau sans l'élément indésirable, puis le réassigner à l'ancien tableau. Cela fonctionne dans bash
:
array=(pluto pippo)
new_array=()
for value in "${array[@]}"
do
[[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[@]}")
unset new_array
Cela donne:
echo "${array[@]}"
pippo
C'est le moyen le plus direct de supprimer une valeur si vous connaissez sa position.
$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
Pour développer les réponses ci-dessus, vous pouvez utiliser les méthodes suivantes pour supprimer plusieurs éléments d'un tableau, sans correspondance partielle:
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
Cela donnera un tableau contenant: (Deux sur trois trois quatre "un six")
Voici une solution en une ligne avec mapfile:
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "<regexp>")
Exemple:
$ 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
Cette méthode permet une grande flexibilité en modifiant/échangeant la commande grep et ne laisse aucune chaîne vide dans le tableau.
Le script POSIX Shell n'a pas de tableaux.
Donc, vous utilisez probablement un dialecte spécifique tel que bash
, korn shell ou zsh
.
Par conséquent, vous ne pouvez pas répondre à votre question dès maintenant.
Peut-être que cela fonctionne pour vous:
unset array[$delete]
Voici une petite fonction (probablement très spécifique à bash) impliquant la variable bash indirection et unset
; c'est une solution générale qui n'implique pas de substitution de texte ni d'élimination d'éléments vides et ne présente aucun problème pour citer/espaces blancs, etc. .
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
Utilisez-le comme delete_ary_elmt ELEMENT ARRAYNAME
sans aucun sigil $
. Changer le == $Word
pour == $Word*
pour les correspondances de préfixe; utilisez ${elmt,,} == ${Word,,}
pour les correspondances sans distinction de casse; etc., tout ce que bash [[
supporte.
Cela fonctionne en déterminant les index du tableau en entrée et en les itérant à l'envers (ainsi, la suppression d'éléments ne bousille pas l'ordre des itérations). Pour obtenir les index, vous devez accéder au tableau d'entrée par nom, ce qui peut être fait via la variable bash indirection x=1; varname=x; echo ${!varname} # prints "1"
.
Vous ne pouvez pas accéder aux tableaux par leur nom comme aryname=a; echo "${$aryname[@]}
, cela vous donnera une erreur. Vous ne pouvez pas faire aryname=a; echo "${!aryname[@]}"
, cela vous donne les indices de la variable aryname
(bien que ce ne soit pas un tableau). Ce qui fonctionne est aryref="a[@]"; echo "${!aryref}"
, qui imprimera les éléments du tableau a
, en préservant les guillemets Shell-Word et les espaces, exactement comme echo "${a[@]}"
. Mais cela ne fonctionne que pour imprimer les éléments d'un tableau, pas pour imprimer sa longueur ou ses index (aryref="!a[@]"
ou aryref="#a[@]"
ou "${!!aryref}"
ou "${#!aryref}"
, ils échouent tous).
Je copie donc le tableau d'origine par son nom via bash indirection et récupère les index de la copie. Pour parcourir les index en sens inverse, j'utilise une boucle de style C pour. Je pourrais aussi le faire en accédant aux index via ${!arycopy[@]}
et en les inversant avec tac
, qui est une cat
qui permet de changer l'ordre de la ligne d'entrée.
Une solution de fonction sans indirection variable devrait probablement impliquer eval
, ce qui peut ou non être sûr à utiliser dans cette situation (je ne peux pas dire).
Pour éviter les conflits avec l'index de tableau à l'aide de unset
- voir https://stackoverflow.com/a/49626928/3223785 et https://stackoverflow.com/a/47798640/3223785 Pour plus d'informations, réaffectez le tableau à lui-même: 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
[Réf .: https://tecadmin.net/working-with-array-bash-script/ ]
Utilisation de unset
Pour supprimer un élément à un index particulier, nous pouvons utiliser unset
et ensuite copier dans un autre tableau. Seul unset
n'est pas requis dans ce cas. Étant donné que unset
ne supprime pas l'élément, il ne fait que définir une chaîne null pour l'index particulier du tableau.
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]}"
La sortie est
aa cc dd ee
1st val is , 2nd val is cc
aa cc dd ee
1st val is cc, 2nd val is dd
Utilisation de :<idx>
Nous pouvons également supprimer un ensemble d’éléments en utilisant :<idx>
. Par exemple, si nous voulons supprimer le 1er élément, nous pouvons utiliser :1
comme mentionné ci-dessous.
declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
arr2=("${arr[@]:1}")
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"
La sortie est
bb cc dd ee
1st val is cc, 2nd val is dd
En ZSH, cela est extrêmement facile (notez que cela utilise plus de syntaxe compatible bash que nécessaire si possible pour faciliter la compréhension):
# 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[@]}"
Résultats:
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
Pour supprimer le premier élément du tableau
unset 'array[0]'
Pour supprimer le dernier élément du tableau
unset 'array[-1]'
http://wiki.bash-hackers.org/syntax/pe#substring_removal
$ {PARAMETER # PATTERN} # supprimer du début
$ {PARAMETER ## PATTERN} # supprimer du début, match gourmand
$ {PARAMETER% PATTERN} # supprimer de la fin
$ {PARAMETER %% PATTERN} # supprimer de la fin, match gourmand
Pour faire un élément remove complet, vous devez faire une commande unset avec une instruction if. Si vous ne vous souciez pas de supprimer les préfixes d'autres variables ou de prendre en charge les espaces dans le tableau, vous pouvez simplement supprimer les guillemets et oublier les boucles for.
Voir l'exemple ci-dessous pour connaître différentes façons de nettoyer un tableau.
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
Sortie
Choose an option:
1) foobar
2) foo bar
3) s
4) Quit
Option?
J'espère que cela pourra aider.
En fait, je viens de remarquer que la syntaxe du shell a en quelque sorte un comportement intégré qui permet une reconstruction facile du tableau lorsque, comme le pose la question, un élément doit être supprimé.
# 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
Remarquez comment nous avons construit le tableau en utilisant la syntaxe x+=()
de bash?
Vous pouvez en fait ajouter plusieurs éléments à cela, le contenu d'un autre tableau à la fois.
Il y a aussi cette syntaxe, par exemple si vous voulez supprimer le 2ème élément:
array=("${array[@]:0:1}" "${array[@]:2}")
ce qui est en fait la concaténation de 2 onglets. Le premier de l'index 0 à l'index 1 (exclusif) et le second de l'index 2 à la fin.