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"?

Anthony Webber
la source
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.

John1024
la source
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.

Stéphane Chazelas
la source