74 votes

Bash : Diviser une chaîne de caractères en un tableau de caractères

J'ai une chaîne de caractères dans un shell Bash script que je veux diviser en un tableau de caractères, non pas basé sur un délimiteur, mais juste un caractère par indice de tableau. Comment puis-je faire cela ? Idéalement, il ne devrait pas utiliser de programmes externes. Laissez-moi reformuler ça. Mon but est la portabilité, donc des choses comme sed qui sont susceptibles d'être sur n'importe quel système compatible POSIX sont bien.

2 votes

bash n'est pas une donnée si votre plateforme est POSIX.

3 votes

@tripleee Les tableaux ne sont pas non plus des tableaux.

0 votes

Bien sûr. J'essaie de donner un sens à la question. Peut-être que le PO veut cibler Bash sur un autre système POSIX ?

141voto

xdazz Points 85907

Essayez

echo "abcdefg" | fold -w1

Edit : Ajout d'une solution plus élégante suggérée dans les commentaires.

echo "abcdefg" | grep -o .

2 votes

Malgré le fait qu'une commande externe soit utilisée, +1 par souci de concision.

10 votes

unstableme.blogspot.fi/2009/07/ a une suggestion plutôt élégante echo "abcdefg" | grep -o .

7 votes

@xdazz cela ne fonctionne pas sur Unicode. Essayez ceci echo "" | fold -w1 Il imprime des espaces et des points d'interrogation. Cependant, la solution de @tripleee echo "" | grep -o . fonctionne bien. C'est drôle comme les petits programmes ne passent pas le cap de la stackoverflow.com/q/796986/161278 :). Merci quand même pour votre réponse élégante.

41voto

Mat Points 104488

Vous pouvez déjà accéder à chaque lettre individuellement sans conversion de tableau :

$ foo="bar"
$ echo ${foo:0:1}
b
$ echo ${foo:1:1}
a
$ echo ${foo:2:1}
r

Si ce n'est pas suffisant, vous pouvez utiliser quelque chose comme ça :

$ bar=($(echo $foo|sed  's/\(.\)/\1 /g'))
$ echo ${bar[1]}
a

Si tu ne peux même pas utiliser sed ou quelque chose comme ça, vous pouvez utiliser la première technique ci-dessus combinée avec une boucle while utilisant la longueur de la chaîne originale ( ${#foo} ) pour construire le tableau.

Attention : le code ci-dessous ne fonctionne pas si la chaîne contient des espaces. Je pense que La réponse de Vaughn Cato a plus de chance de survivre avec des caractères spéciaux.

thing=($(i=0; while [ $i -lt ${#foo} ] ; do echo ${foo:$i:1} ; i=$((i+1)) ; done))

16voto

mr.spuratic Points 2205

Comme alternative à l'itération sur 0 .. ${#string}-1 avec une boucle for/while, il y a deux autres moyens auxquels je pense pour faire cela avec seulement bash : en utilisant =~ et en utilisant printf . (Il y a une troisième possibilité en utilisant eval et un {..} expression de la séquence, mais cela manque de clarté).

Avec un environnement correct et l'activation de NLS dans bash, ils fonctionneront comme prévu avec les caractères non ASCII, éliminant ainsi les sources potentielles d'échec avec les anciens outils système tels que sed si c'est un problème. Ils fonctionneront à partir de bash-3.0 (version 2005).

Utilisation de =~ et les expressions régulières, en convertissant une chaîne de caractères en un tableau en une seule expression :

string="wonkabars"
[[ "$string" =~ ${string//?/(.)} ]]       # splits into array
printf "%s\n" "${BASH_REMATCH[@]:1}"      # loop free: reuse fmtstr
declare -a arr=( "${BASH_REMATCH[@]:1}" ) # copy array for later

La façon dont cela fonctionne est d'effectuer une expansion de string qui substitue chaque caractère unique à (.) puis de faire correspondre cette expression régulière générée avec un regroupement pour capturer chaque caractère individuel en BASH_REMATCH[] . L'indice 0 correspond à la chaîne de caractères entière, puisque ce tableau spécial est en lecture seule, vous ne pouvez pas le supprimer. :1 lorsque le tableau est développé pour sauter l'index 0, si nécessaire. Quelques tests rapides pour des chaînes de caractères non triviales (>64 caractères) montrent que cette méthode est substantiellement plus rapide que celle qui utilise les opérations bash sur les chaînes et les tableaux.

Ce qui précède fonctionne avec les chaînes de caractères contenant des sauts de ligne, =~ soutient POSIX ERE où . correspond à tout sauf à NUL par défaut, c'est à dire que la regex est compilée sans REG_NEWLINE . (Le comportement du traitement de texte POSIX services publics est autorisé à être différent par défaut à cet égard, et l'est généralement).

Deuxième option, en utilisant printf :

string="wonkabars"
ii=0
while printf "%s%n" "${string:ii++:1}" xx; do 
  ((xx)) && printf "\n" || break
done 

Cette boucle incrémente l'indice ii pour imprimer un caractère à la fois, et s'interrompt lorsqu'il n'y a plus de caractères. Ce serait encore plus simple si la commande bash printf retournait le nombre de caractères imprimés (comme en C) plutôt qu'un état d'erreur, le nombre de caractères imprimés est capturé dans le fichier xx en utilisant %n . (Cela fonctionne au moins jusqu'à bash-2.05b).

Avec bash-3.1 et printf -v var vous avez un peu plus de flexibilité, et vous pouvez éviter de tomber à la fin de la chaîne si vous faites autre chose qu'imprimer les caractères, par exemple pour créer un tableau :

declare -a arr
ii=0
while printf -v cc "%s%n" "${string:(ii++):1}" xx; do 
    ((xx)) && arr+=("$cc") || break
done

13voto

Vaughn Cato Points 30511

Si votre chaîne est stockée dans la variable x, cela produit un tableau y avec les caractères individuels :

i=0
while [ $i -lt ${#x} ]; do y[$i]=${x:$i:1};  i=$((i+1));done

15 votes

Ceci : for (( i=0 ; i < ${#x} ; i++ )); do y[i]=${x:i:1}; done semble plus idiomatique pour bash.

5voto

La solution la plus simple, la plus complète et la plus élégante :

$ read -a ARRAY <<< $(echo "abcdefg" | sed 's/./& /g')  

et de tester

$ echo ${ARRAY[0]}
  a

$ echo ${ARRAY[1]}
  b

Explication : read -a lit le stdin comme un tableau et l'assigne à la variable ARRAY en traitant les espaces comme délimiteurs pour chaque élément du tableau.

L'évaluation de l'écho de la chaîne de caractères à sed ajoute juste les espaces nécessaires entre chaque caractère.

Nous utilisons Ici String (<<<) pour alimenter le stdin de la commande de lecture.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X