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
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 ?
1 votes
L'intention initiale était de créer un shell script qui pourrait être partagé en ligne sans en savoir beaucoup sur la plateforme de l'utilisateur. Je voulais donc une compatibilité aussi grande que possible entre OS X, Ubuntu, etc. Je n'ai pas besoin d'une compatibilité à 100% avec des variations exotiques d'Unix.