Script Bash: séparer le mot sur chaque lettre

Comment puis-je séparer les lettres d'un mot, avec chaque lettre dans une ligne distincte?

Par exemple, étant donné "StackOver" je voudrais voir

 S t a c k O v e r 

Je suis nouveau à bash donc je n'ai aucune idée par où commencer.

Je voudrais utiliser grep :

 $ grep -o . <<<"StackOver" S t a c k O v e r 

ou sed :

 $ sed 's/./&\n/g' <<<"StackOver" S t a c k O v e r 

Et si l'espace vide à la fin est un problème:

 sed 's/\B/&\n/g' <<<"StackOver" 

Tout cela en supposant GNU / Linux.

Vous voudrez peut-être rompre sur les grappes graphem au lieu de caractères si l'intention est d'imprimer du text verticalement. Par exemple avec un e avec un accent aigu:

  • Avec des grappes de graphes ( e avec son accent aigu serait un cluster graphe):

     $ perl -CLAS -le 'for (@ARGV) {print for /\X/g}' $'Ste\u301phane' S t é p h a n e 

    (ou grep -Po '\X' avec GNU grep construit avec le support PCRE)

  • Avec des caractères (ici avec GNU grep ):

     $ printf '%s\n' $'Ste\u301phane' | grep -o . S t e p h a n e 
  • fold est censé décomposer les caractères, mais le fold GNU ne prend pas en charge les caractères multi-octets, donc il se casse sur les octets à la place:

     $ printf '%s\n' $'Ste\u301phane' | fold -w 1 S t e     p h a n e 

Sur StackOver qui ne contient que des caractères ASCII (donc un octet par caractère, un caractère par cluster), les trois donneront le même résultat.

Si vous avez perl6 dans votre boite:

 $ perl6 -e 'for @*ARGS -> $w { .say for $w.comb }' 'cường' c ư ờ n g 

travailler indépendamment de vos parameters régionaux.

Avec de nombreuses versions awk

 awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver' 

Le ci-dessous sera générique:

 $ awk -F '' \ 'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name> 

Vous pouvez gérer des caractères multi-octets comme:

 <input \ dd cbs=1 obs=2 conv=unblock | sed -e:c -e '/^.*$/!N;s/\n//;tc' 

Ce qui peut être très pratique lorsque vous travaillez avec une input en direct car il n'y a pas de tampon et un caractère est imprimé dès qu'il est entier .

Puisque vous avez spécifiquement demandé une réponse dans bash, voici un moyen de le faire en pure bash:

 while read -rn1; do echo "$REPLY" ; done <<< "StackOver" 

Notez que cela attrapera la nouvelle ligne à la fin du " ici document ". Si vous voulez éviter cela, mais continuez à parcourir les caractères avec une boucle bash, utilisez printf pour éviter la nouvelle ligne.

 printf StackOver | while read -rn1; do echo "$REPLY" ; done 

Python 2 peut également être utilisé à partir de la command line:

 python <<< "for x in 'StackOver': print x" 

ou:

 echo "for x in 'StackOver': print x" | python 

ou (comme commenté par 1_CR) avec Python 3 :

 python3 -c "print(*'StackOver',sep='\n')" 

Vous pouvez utiliser la command fold (1) . Il est plus efficace que grep et sed .

 $ time grep -o . <bigfile >/dev/null real 0m3.868s user 0m3.784s sys 0m0.056s $ time fold -b1 <bigfile >/dev/null real 0m0.555s user 0m0.528s sys 0m0.016s $ 

Une différence significative est que replier reproduira les lignes vides dans la sortie:

 $ grep -o . <(printf "A\nB\n\nC\n\n\nD\n") A B C D $ fold -b1 <(printf "A\nB\n\nC\n\n\nD\n") A B C D $ 

Vous pouvez aussi utiliser des limites de mots.

 $ perl -pe 's/(?<=.)(\B|\b)(?=.)/\n/g' <<< "StackOver" S t a c k O v e r 
 echo StackOver | sed -e 's/./&\n/g' S t a c k O v e r 

En bash:

Cela fonctionne avec n'importe quel text et avec seulement des internes bash (aucun utilitaire externe appelé), donc, devrait être rapide sur les strings très courtes.

 str="Stéphane áàéèëêếe" [[ $str =~ ${str//?/(.)} ]] (set -- "${BASH_REMATCH[@]:1}"; IFS=$'\n'; echo "$*") 

Sortie:

 S t é p h a n e á à é è ë ê ế e 

Si vous pouvez modifier IFS et modifier les parameters de position, vous pouvez également éviter l'appel sous-shell:

 str="Stéphane áàéèëêếe" [[ $str =~ ${str//?/(.)} ]] set -- "${BASH_REMATCH[@]:1}" IFS=$'\n' echo "$*" 
 s=stackoverflow; $ time echo $s | fold -w1 stackov e r real 0m0.014s user 0m0.000s sys 0m0.004s 

les mises à jour ici sont le moyen le plus rapide | pureBashBased hacky!

 $ time eval eval printf \'%s\\\\n\' \\\${s:\{0..$((${#s}-1))}:1} s t a c k o v e r real 0m0.001s user 0m0.000s sys 0m0.000s 

pour plus d'awesomeness

 function foldh () { if (($#)); then local s="$@"; eval eval printf \'%s\\\\n\' \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\"; else while read s; do eval eval printf \'%s\\\\n\' \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\"; done; fi } function foldv () { if (($#)); then local s="$@"; eval eval echo \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\"; else while read s; do eval eval echo \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\"; done; fi }