Script bash: fractionner le mot sur chaque lettre

17

Comment puis-je diviser les lettres d'un mot, avec chaque lettre sur une ligne distincte?

Par exemple, étant donné "StackOver" que j'aimerais voir

S
t
a
c
k
O
v
e
r

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

Sijaan Hallak
la source

Réponses:

29

J'utiliserais 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.

jimmij
la source
grep -o. <<< ¿¿¿.. -o recherche le MOTIF fourni non? et que fait-il ici dans votre commandement?
Sijaan Hallak
1
@jimmij Je ne trouve aucune aide sur ce que <<< fait vraiment! de l'aide?
Sijaan Hallak
3
@SijaanHallak C'est ce qu'on appelle Here string, l'équivalent grosso modo d'un echo foo | ...peu moins de frappe. Voir tldp.org/LDP/abs/html/x17837.html
jimmij
1
@SijaanHallak changement .à \B(ne correspond pas sur une frontière de mot).
jimmij
1
@SijaanHallak - vous pouvez laisser tomber le second sedcomme:sed -et -e's/./\n&/g;//D'
mikeserv
19

Vous souhaiterez peut-être casser les grappes de graphèmes au lieu des caractères si l'intention est d'imprimer le texte verticalement. Par exemple avec un eavec un accent aigu:

  • Avec des grappes de graphèmes ( eavec son accent aigu serait un grappe de graphèmes):

    $ 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 personnages (ici avec GNU grep):

    $ printf '%s\n' $'Ste\u301phane' | grep -o .
    S
    t
    e
    
    p
    h
    a
    n
    e
    
  • foldest censé casser les caractères, mais GNU foldne prend pas en charge les caractères multi-octets, il se brise donc 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 se compose que de caractères ASCII (donc un octet par caractère, un caractère par grappe de graphèmes), les trois donneraient le même résultat.

Stéphane Chazelas
la source
Je suis surpris de grep -Pone pas faire ce à quoi on pourrait s'attendre (comme le grep -Pfait).
jimmij
@jimmij, que voulez-vous dire? grep -Po .trouve des caractères (et un accent aigu combiné après un caractère de nouvelle ligne n'est pas valide) et grep -Po '\X'trouve des grappes de graphes pour moi. Vous aurez peut-être besoin d'une version récente de grep et / ou PCRE pour qu'elle fonctionne correctement (ou essayez grep -Po '(*UTF8)\X')
Stéphane Chazelas
2
@SijaanHallak Ceux-ci pourraient être utiles: joelonsoftware.com/articles/Unicode.html , eev.ee/blog/2015/09/12/dark-corners-of-unicode
jpmc26
6

Si vous avez perl6 dans votre boîte:

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

travailler quel que soit votre environnement local.

cuonglm
la source
6

Avec de nombreuses awkversions

awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver'
iruvar
la source
Génial! Mais sur ma version de nAWK ("One True AWK") cela ne fonctionne pas. Cependant , cela fait l'affaire: awk -v FS='' -v OFS='\n' '{$1=$1};1' ( se demandant si c'est plus facile à transporter car -F ''pourrait donner l'ERE: //)
eruve
4

Ce qui suit sera générique:

$ awk -F '' \
   'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name>
user150073
la source
4
echo StackOver | sed -e 's/./&\n/g'
S
t
a
c
k
O
v
e
r
Henderson
la source
Cela n'aidera pas car il imprime une nouvelle ligne à la fin
Sijaan Hallak
4

Puisque vous avez spécifiquement demandé une réponse en bash, voici une façon de le faire en bash pur:

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

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

printf StackOver | while read -rn1; do echo "$REPLY" ; done
wyrm
la source
4

Aussi Python 2 peut être utilisé à partir de la ligne de commande:

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')"
agold
la source
4

Vous pouvez utiliser la fold (1)commande. Il est plus efficace que grepet 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 le pli 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
$ 
joeytwiddle
la source
3

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 assez pratique lorsque vous travaillez avec une entrée en direct car il n'y a pas de mise en mémoire tampon et un caractère est imprimé dès qu'il est entier .

mikeserv
la source
NP, devrions-nous ajouter une note sur les paramètres régionaux?
cuonglm
Ne fonctionne pas pour combiner des personnages comme la réponse de Stéphane Chazelas, mais avec une normalisation appropriée, cela ne devrait pas avoir d'importance.
kay est déçu le
@Kay - cela fonctionne pour combiner des personnages si vous le souhaitez - c'est à cela que sedservent les scripts. je ne suis pas susceptible d'en écrire un en ce moment - je suis assez endormi. il est cependant très utile lors de la lecture d'un terminal.
mikeserv
@cuonglm - si vous le souhaitez. cela devrait juste fonctionner pour les paramètres régionaux, étant donné une libc saine, cependant.
mikeserv
Notez que ddcela cassera les caractères multi-octets, donc la sortie ne sera plus du texte donc le comportement de sed ne sera pas spécifié selon POSIX.
Stéphane Chazelas
3

Vous pouvez également utiliser des limites de mots ..

$ perl -pe 's/(?<=.)(\B|\b)(?=.)/\n/g' <<< "StackOver"
S
t
a
c
k
O
v
e
r
Avinash Raj
la source
1

En bash:

Cela fonctionne avec n'importe quel texte et avec uniquement des internes bash (aucun utilitaire externe appelé), donc, devrait être rapide sur des chaînes très courtes.

str="Stéphane áàéèëêếe"

[[ $str =~ ${str//?/(.)} ]]
(set -- "${BASH_REMATCH[@]:1}"; IFS=$'\n'; echo "$*")

Production:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

S'il est correct de changer IFS et de changer les paramètres de position, vous pouvez également éviter l'appel de sous-shell:

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

$ time echo $s | fold -w1                                                                                                                                          
s                                                                                                                                                                          
t                                                                                                                                                                          
a                                                                                                                                                                          
c                                                                                                                                                                          
k                                                                                                                                                                          
o                                                                                                                                                                          
v
e
r

real    0m0.014s
user    0m0.000s
sys     0m0.004s

mises à jour ici est la manière la plus rapide | pureBashBased!

$ 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 de génialité

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
}
Jonas
la source
Cela donnera-t-il un jour des résultats différents fold -b1?
JigglyNaga
puisque chaque octet a une largeur = 1, le résultat sera le même!
Jonah
1
Alors, comment est-ce que ce n'est pas un double de la réponse précédente ?
JigglyNaga
car il montre le même cmd avec une argyment différente, et c'est bien de savoir.
Jonah
1
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')

cela divisera votre mot et le stockera dans un tableau var.

Chinmay Katil
la source
1
for x in $(echo "$yourWordhere" | grep -o '.')
do
    code to perform operation on individual character $x of your word
done
Chinmay Katil
la source