Skrip Bash: pisahkan kata pada setiap huruf

17

Bagaimana saya bisa membagi huruf kata, dengan masing-masing huruf dalam baris yang terpisah?

Misalnya, mengingat "StackOver" saya ingin melihat

S
t
a
c
k
O
v
e
r

Saya baru mengenal bash jadi saya tidak tahu harus mulai dari mana.

Sijaan Hallak
sumber

Jawaban:

29

Saya akan menggunakan grep:

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

atau sed:

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

Dan jika ruang kosong pada akhirnya adalah masalah:

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

Semua itu dengan asumsi GNU / Linux.

jimmij
sumber
grep -o. <<< ¿¿¿.. -untuk mencari POLA yang disediakan, kan? dan apa fungsinya di sini atas perintah Anda?
Sijaan Hallak
1
@jimmij Saya tidak dapat menemukan bantuan apa pun <<< yang sebenarnya! ada bantuan?
Sijaan Hallak
3
@SijaanHallak Ini disebut Here string, grosso modo setara dengan echo foo | ...kurang mengetik. Lihat tldp.org/LDP/abs/html/x17837.html
jimmij
1
@SijaanHallak berubah .menjadi \B(tidak cocok dengan batas kata).
jimmij
1
@SijaanHallak - Anda dapat menjatuhkan yang kedua sedseperti:sed -et -e's/./\n&/g;//D'
mikeserv
19

Anda mungkin ingin memecah cluster grapheme daripada karakter jika tujuannya adalah untuk mencetak teks secara vertikal. Misalnya dengan eaksen akut:

  • Dengan cluster grapheme ( edengan aksen akutnya akan menjadi satu cluster grapheme):

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

    (atau grep -Po '\X'dengan grep GNU yang dibangun dengan dukungan PCRE)

  • Dengan karakter (di sini dengan GNU grep):

    $ printf '%s\n' $'Ste\u301phane' | grep -o .
    S
    t
    e
    
    p
    h
    a
    n
    e
    
  • folddimaksudkan untuk memecah karakter, tetapi GNU foldtidak mendukung karakter multi-byte, jadi ia memecah pada byte:

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

Pada StackOver yang hanya terdiri dari karakter ASCII (jadi satu byte per karakter, satu karakter per cluster grapheme), ketiganya akan memberikan hasil yang sama.

Stéphane Chazelas
sumber
Saya terkejut grep -Potidak melakukan apa yang diharapkan (seperti grep -Phalnya).
jimmij
@jimmij, apa maksudmu? grep -Po .menemukan karakter (dan aksen akut kombinasi setelah karakter baris baru tidak valid), dan grep -Po '\X'menemukan cluster graphem untuk saya. Anda mungkin memerlukan versi grep dan / atau PCRE terbaru agar dapat berfungsi dengan baik (atau coba grep -Po '(*UTF8)\X')
Stéphane Chazelas
6

Jika Anda memiliki perl6 di kotak Anda:

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

bekerja terlepas dari lokal Anda.

cuonglm
sumber
6

Dengan banyak awkversi

awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver'
iruvar
sumber
Bagus! Tetapi pada versi nAWK saya ("One True AWK") itu tidak berhasil. Namun ini melakukan trik: awk -v FS='' -v OFS='\n' '{$1=$1};1' (bertanya-tanya apakah itu lebih portabel karena -F ''mungkin menghasilkan ERE: //)
eruve
4

Di bawah ini akan menjadi generik:

$ awk -F '' \
   'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name>
pengguna150073
sumber
4
echo StackOver | sed -e 's/./&\n/g'
S
t
a
c
k
O
v
e
r
henderson
sumber
Ini tidak akan membantu karena mencetak baris baru di akhir
Sijaan Hallak
4

Karena Anda secara khusus meminta jawaban dalam bash, inilah cara untuk melakukannya dalam bash murni:

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

Perhatikan bahwa ini akan menangkap baris baru di akhir dokumen "di sini ". Jika Anda ingin menghindari itu, tetapi tetap mengulangi karakter dengan bash loop, gunakan printfuntuk menghindari baris baru.

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

Juga Python 2 dapat digunakan dari baris perintah:

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

atau:

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

atau (seperti yang dikomentari oleh 1_CR) dengan Python 3 :

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

Anda bisa menggunakan fold (1)perintah. Ini lebih efisien daripada grepdan 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
$

Satu perbedaan signifikan adalah lipatan akan mereproduksi garis kosong di 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
$ 
joeytwiddle
sumber
3

Anda dapat menangani karakter multibyte seperti:

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

Yang bisa sangat berguna ketika Anda bekerja dengan input langsung karena tidak ada buffering di sana dan karakter dicetak segera setelah semuanya utuh .

mikeserv
sumber
NP, haruskah kita menambahkan catatan tentang lokal?
cuonglm
Tidak berfungsi untuk menggabungkan karakter seperti Stéphane Chazelas menjawab, tetapi dengan normalisasi yang tepat ini seharusnya tidak masalah.
Kay kecewa pada
@Kay - ini berfungsi untuk menggabungkan karakter jika Anda menginginkannya - itulah gunanya sedskrip. saya tidak akan menulis yang benar tentang sekarang - saya cukup mengantuk. itu benar-benar berguna, ketika membaca terminal.
mikeserv
@cuonglm - jika Anda suka. seharusnya hanya bekerja untuk lokal, diberikan libc yang waras.
mikeserv
Catatan yang ddakan memecah karakter multibyte, sehingga output tidak akan menjadi teks lagi sehingga perilaku sed tidak ditentukan sesuai POSIX.
Stéphane Chazelas
3

Anda dapat menggunakan batas kata juga ..

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

Dalam bash:

Ini berfungsi dengan teks apa saja dan dengan hanya bash internal (tidak ada utilitas eksternal yang disebut), jadi, harus cepat pada string yang sangat singkat.

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

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

Keluaran:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

Jika boleh mengubah IFS dan mengubah parameter posisi, Anda juga dapat menghindari panggilan sub-shell:

str="Stéphane áàéèëêếe"
[[ $str =~ ${str//?/(.)} ]]
set -- "${BASH_REMATCH[@]:1}"
IFS=$'\n'
echo "$*"
sorontar
sumber
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

pembaruan di sini adalah cara hacky | tercepat | pureBash!

$ 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

untuk lebih kedahsyatan

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
}
Jonah
sumber
Apakah ini akan memberikan hasil yang berbeda fold -b1?
JigglyNaga
karena setiap byte memiliki lebar = 1 hasilnya akan sama!
Jonah
1
Jadi bagaimana ini bukan duplikat dari jawaban sebelumnya ?
JigglyNaga
karena itu menunjukkan cmd yang sama dengan argyment yang berbeda, dan itu bagus untuk diketahui.
Jonah
1
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')

ini akan membagi kata Anda dan menyimpannya dalam array var.

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