Comment déplacer un tableau bash à un index au milieu?


12
1  #!/bin/bash
2  # query2.sh
3
4  numbers=(53 8 12 9 784 69 8 7 1)
5  i=4
6
7  echo ${numbers[@]} # <--- this echoes "53 8 12 9 784 69 8 7 1" to stdout.
8  echo ${numbers[i]} # <--- this echoes "784" to stdout.
9
10 unset numbers[i]
11
12 echo ${numbers[@]} # <--- this echoes "53 8 12 9 69 8 7 1" to stdout.
13 echo ${numbers[i]} # <--- stdout is blank.

Pourquoi, à la ligne 13, la sortie standard est-elle vide, étant donné que le tableau semble avoir été mis à jour à en juger par la sortie standard de la ligne 12?

Et par conséquent, que dois-je faire pour obtenir la réponse voulue, "69"?


1
Compte tenu du type de travail de codage que cette question implique, vous devriez prendre un avertissement: voir Y a
Wildcard

Réponses:


21

unsetsupprime un élément. Il ne renumérote pas les éléments restants.

Nous pouvons utiliser declare -ppour voir exactement ce qui arrive à numbers:

$ unset "numbers[i]"
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

Observez qu'il numbersn'a plus d'élément 4.

Un autre exemple

Observer:

$ a=()
$ a[1]="element 1"
$ a[22]="element 22"
$ declare -p a
declare -a a=([1]="element 1" [22]="element 22")

Le tableau ane comporte aucun élément de 2 à 21. Bash n'exige pas que les indices de tableau soient consécutifs.

Méthode suggérée pour forcer une renumérotation des indices

Commençons par le numberstableau avec l'élément manquant 4:

$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

Si nous voulons que les indices changent, alors:

$ numbers=("${numbers[@]}")
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [4]="69" [5]="8" [6]="7" [7]="1")

Il y a maintenant un numéro d'élément 4et il a une valeur 69.

Autre méthode pour supprimer un élément et renuméroter un tableau en une seule étape

Encore une fois, définissons numbers:

$ numbers=(53 8 12 9 784 69 8 7 1)

Comme suggéré par Toby Speight dans les commentaires, une méthode pour supprimer le quatrième élément et renuméroter les éléments restants en une seule étape:

$ numbers=("${numbers[@]:0:4}" "${numbers[@]:5}")
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [4]="69" [5]="8" [6]="7" [7]="1")

Comme vous pouvez le voir, le quatrième élément a été supprimé et tous les éléments restants ont été renumérotés.

${numbers[@]:0:4}tableau slices numbers: il prend les quatre premiers éléments en commençant par l'élément 0.

De même, ${numbers[@]:5}tranche de tableau numbers: elle prend tous les éléments en commençant par l'élément 5 et en continuant à la fin du tableau.

Obtention des indices d'un tableau

Les valeurs d'un tableau peuvent être obtenues avec ${a[@]}. Pour rechercher les indices (ou clés ) correspondant à ces valeurs, utilisez ${!a[@]}.

Par exemple, considérons à nouveau notre tableau numbersavec l'élément manquant 4:

$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

Pour voir quels indices sont attribués:

$ echo "${!numbers[@]}"
0 1 2 3 5 6 7 8

Encore une fois, il 4manque dans la liste des indices.

Documentation

De man bash:

Le unsetbuiltin est utilisé pour détruire les tableaux. unset name[subscript]détruit l'élément de tableau à l'index subscript. Les indices négatifs des tableaux indexés sont interprétés comme décrit ci-dessus. Des précautions doivent être prises pour éviter les effets secondaires indésirables causés par l'expansion du nom de chemin. unset name, où nameest un tableau, ou unset name[subscript], où subscriptest * ou @, supprime l'ensemble du tableau.


1
Notez que la syntaxe du tableau shell est vraiment juste un moyen de faciliter le traitement des variables nommées de manière similaire. Il n'y a pas de tableau lui-même; en fait, après avoir écrit a=(), la variable an'est toujours pas définie jusqu'à ce que vous l'attribuiez réellement à l'un de ses indices.
chepner

@ John1024: Merci pour cette réponse. Pourriez-vous éventuellement l'étendre pour inclure une réponse suggérée pour atteindre le résultat souhaité?
Anthony Webber

@AnthonyWebber Bien sûr. J'ai ajouté une section à la réponse pour montrer comment forcer une renumérotation des indices.
John1024

2
Juste pour mentionner une approche alternative (qui pourrait être mieux adaptée à du code): au lieu de unset numbers[4], attribuez le tableau entier en utilisant le découpage, c'est-à-dire numbers=("${numbers[@]:0:4}" "${numbers[@]:5}")(je posterais comme réponse, mais je n'ai pas le temps de l'expliquer correctement).
Toby Speight

@ John1024: Je vous en remercie. Et thnx Toby :)
Anthony Webber

5

bashles tableaux comme dans ksh, ne sont pas vraiment des tableaux, ils ressemblent plus à des tableaux associatifs avec des clés limitées à des entiers positifs (ou soi-disant tableaux clairsemés ). Pour un shell avec des tableaux réels, vous pouvez jeter un oeil à des coquilles comme rc, es, fish, yash, zsh(ou même csh/ tcshsi ces obus ont tant de problèmes auxquels ils sont mieux évités).

Dans zsh:

a=(1 2 3 4 5)
a[3]=() # remove the 3rd element
a[1,3]=() # remove the first 3 elements
a[-1]=() # remove the last element

(Notez que dans zsh, le unset 'a[3]'définit en fait sur la chaîne vide pour une meilleure compatibilité avec ksh)

dans yash:

a=(1 2 3 4 5)
array -d a 3 # remove the 3rd element
array -d a 1 2 3 # remove the first 3 elements
array -d a -1 # remove the last element

dans fish(pas un shell de type Bourne contrairement à bash/ zsh):

set a 1 2 3 4 5
set -e a[3] # remove the 3rd element
set -e a[1..3] # remove the first 3 elements
set -e a[-1] # remove the last element

dans es(basé sur rc, pas comme Bourne)

a = 1 2 3 4 5
a = $a(... 2 4 ...) # remove the 3rd element
a = $a(4 ...) # remove the first 3 elements
a = $a(... `{expr $#a - 1}) # remove the last element
# or a convoluted way that avoids forking expr:
a = $a(... <={@{*=$*(2 ...); return $#*} $a})

dans kshetbash

Vous pouvez utiliser les tableaux comme des tableaux normaux si vous le faites:

a=("${a[@]}")

après chaque opération de suppression ou d'insertion qui peut avoir rendu la liste des index non contiguës ou ne pas commencer à 0. Notez également que ksh/ les bashtableaux commencent à 0, pas à 1 (sauf pour $@(à certains égards)).

Cela va en effet ranger les éléments et les déplacer dans l'index 0, 1, 2 ... en séquence.

Notez également que vous devez citer le number[i]dans:

unset 'number[i]'

Sinon, cela serait traité comme unset numberis'il y avait un fichier appelé numberidans le répertoire courant.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.