How can I split a word's letters, with each letter in a separate line?
For example, given "StackOver"
I would like to see
S
t
a
c
k
O
v
e
r
I'm new to bash so I have no clue where to start.
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It only takes a minute to sign up.
Sign up to join this communityHow can I split a word's letters, with each letter in a separate line?
For example, given "StackOver"
I would like to see
S
t
a
c
k
O
v
e
r
I'm new to bash so I have no clue where to start.
I would use grep
:
$ grep -o . <<<"StackOver"
S
t
a
c
k
O
v
e
r
or sed
:
$ sed 's/./&\n/g' <<<"StackOver"
S
t
a
c
k
O
v
e
r
And if empty space at the end is an issue:
sed 's/\B/&\n/g' <<<"StackOver"
All of that assuming GNU/Linux.
Here string
, grosso modo equivalent of echo foo | ...
just less typing. See tldp.org/LDP/abs/html/x17837.html
sed
like: sed -et -e's/./\n&/g;//D'
You may want to break on grapheme clusters instead of characters if the intent is to print text vertically. For instance with a e
with an acute accent:
With grapheme clusters (e
with its acute accent would be one grapheme cluster):
$ perl -CLAS -le 'for (@ARGV) {print for /\X/g}' $'Ste\u301phane'
S
t
é
p
h
a
n
e
(or grep -Po '\X'
with GNU grep built with PCRE support)
With characters (here with GNU grep
):
$ printf '%s\n' $'Ste\u301phane' | grep -o .
S
t
e
p
h
a
n
e
fold
is meant to break on characters, but GNU fold
doesn't support multi-byte characters, so it breaks on bytes instead:
$ printf '%s\n' $'Ste\u301phane' | fold -w 1
S
t
e
�
�
p
h
a
n
e
On StackOver which only consists of ASCII characters (so one byte per character, one character per grapheme cluster), all three would give the same result.
grep -Po
doesn't do what one would expect (like grep -P
does).
grep -Po .
finds characters (and a combining acute accent following a newline character is invalid), and grep -Po '\X'
finds graphem clusters for me. You may need a recent version of grep and/or PCRE for it to work properly (or try grep -Po '(*UTF8)\X'
)
Jan 5, 2016 at 0:23
If you have perl6 in your box:
$ perl6 -e 'for @*ARGS -> $w { .say for $w.comb }' 'cường'
c
ư
ờ
n
g
work regardless of your locale.
With many awk
versions
awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver'
awk -v FS='' -v OFS='\n' '{$1=$1};1'
(wondering if that's more portable since -F ''
might yield the ERE: //
)
echo StackOver | sed -e 's/./&\n/g'
S
t
a
c
k
O
v
e
r
You can use the fold (1)
command. It is more efficient than grep
and 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
$
One significant difference is that fold will reproduce empty lines in the output:
$ 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
$
The below will be generic:
$ awk -F '' \
'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name>
Since you specifically asked for an answer in bash, here's a way to do it in pure bash:
while read -rn1; do echo "$REPLY" ; done <<< "StackOver"
Note that this will catch the newline at the end of the "here document". If you want to avoid that, but still iterate over the characters with a bash loop, use printf
to avoid the newline.
printf StackOver | while read -rn1; do echo "$REPLY" ; done
You can handle multibyte characters like:
<input \
dd cbs=1 obs=2 conv=unblock |
sed -e:c -e '/^.*$/!N;s/\n//;tc'
Which can be pretty handy when you're working with live input because there's no buffering there and a character is printed as soon it is whole.
sed
scripts are for. i'm not likely to write one right about now - im pretty sleepy. it's really useful, though, when reading a terminal.
dd
will break multibyte characters, so the output will not be text anymore so the behaviour of sed will be unspecified as per POSIX.
Jan 5, 2016 at 22:09
You may use word boundaries also..
$ perl -pe 's/(?<=.)(\B|\b)(?=.)/\n/g' <<< "StackOver"
S
t
a
c
k
O
v
e
r
In bash:
This works with any text and with only bash internals (no external utility called), so, should be fast on short strings.
str="StackOvér áàéèëêếe"
[[ $str =~ ${str//?/(.)} ]] # Use a regex to split.
printf '%s\n' "${BASH_REMATCH[@]:1}" # Print all characters.
Output:
S
t
a
c
k
O
v
é
r
á
à
é
è
ë
ê
ế
e
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
updates here is the hacky|fastest|pureBashBased way !
$ 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
for more 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
}
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')
this will split your word and store it in array var
.
for x in $(echo "$yourWordhere" | grep -o '.')
do
code to perform operation on individual character $x of your word
done
Using Raku (formerly known as Perl_6)
~$ echo "StackOvér áàéèëêếe" | raku -ne '.chars.put;'
18
~$ echo "StackOvér áàéèëêếe" | raku -ne '.put for .comb;'
S
t
a
c
k
O
v
é
r
á
à
é
è
ë
ê
ế
e