Bash script: แบ่งคำในแต่ละตัวอักษร


17

ฉันจะแยกตัวอักษรของคำด้วยตัวอักษรแต่ละตัวในบรรทัดแยกกันได้อย่างไร

ตัวอย่างเช่นเมื่อ"StackOver" ฉันต้องการที่จะเห็น

S
t
a
c
k
O
v
e
r

ฉันใหม่ในการทุบตีดังนั้นฉันไม่มีเงื่อนงำที่จะเริ่ม

คำตอบ:


29

ฉันจะใช้grep:

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

หรือsed:

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

และถ้าพื้นที่ว่างท้ายเป็นปัญหา:

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

ทั้งหมดนี้คือสมมติว่า GNU / Linux


grep -o <<< ¿¿¿ .. - เพื่อค้นหารูปแบบที่ให้ไว้ใช่ไหม? และสิ่งที่มันทำที่นี่ในคำสั่งของคุณ?
Sijaan Hallak

1
@jimmij ฉันไม่สามารถค้นหาความช่วยเหลือเกี่ยวกับสิ่งที่ <<< จริงๆ! ความช่วยเหลือใด ๆ
Sijaan Hallak

3
@SijaanHallak สิ่งนี้เรียกว่าHere stringgrosso modo เทียบเท่ากับการecho foo | ...พิมพ์ที่น้อยลง ดูtldp.org/LDP/abs/html/x17837.html
jimmij

1
@SijaanHallak เปลี่ยน.เป็น\B(ไม่ตรงกับขอบเขตของคำ)
jimmij

1
@SijaanHallak - คุณสามารถเลื่อนอันดับที่สองsedเช่น:sed -et -e's/./\n&/g;//D'
mikeserv

19

คุณอาจต้องการแบ่งกลุ่มของ grapheme แทนที่จะเป็นตัวอักษรหากเจตนาคือการพิมพ์ข้อความในแนวตั้ง เช่นกับ a eด้วยสำเนียงเฉียบพลัน:

  • ด้วยกลุ่มกราฟ ( eด้วยสำเนียงเฉียบพลันของมันจะเป็นหนึ่งกลุ่มแกรม):

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

    (หรือgrep -Po '\X'ด้วย grep GNU ที่สร้างด้วยการรองรับ PCRE)

  • ด้วยตัวละคร (ที่นี่ด้วย GNU grep):

    $ printf '%s\n' $'Ste\u301phane' | grep -o .
    S
    t
    e
    
    p
    h
    a
    n
    e
    
  • foldมีวัตถุประสงค์เพื่อแยกอักขระ แต่ GNU foldไม่รองรับอักขระหลายไบต์ดังนั้นจึงหยุดพักแบบไบต์แทน:

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

บนStackOverซึ่งประกอบด้วยอักขระ ASCII เท่านั้น (ดังนั้นหนึ่งไบต์ต่ออักขระ, หนึ่งอักขระต่อคลัสเตอร์กราฟ) ทั้งสามจะให้ผลลัพธ์เดียวกัน


ฉันประหลาดใจที่grep -Poไม่ได้ทำในสิ่งที่คาดหวัง (เหมือนgrep -P)
jimmij

@jimmij คุณหมายถึงอะไร grep -Po .ค้นหาอักขระ (และการรวมกันของสำเนียงเฉียบพลันหลังจากอักขระบรรทัดใหม่ไม่ถูกต้อง) และgrep -Po '\X'ค้นหากลุ่มกราฟสำหรับฉัน คุณอาจต้องรุ่นล่าสุดของ grep และ / หรือ PCRE เพื่อให้การทำงานอย่างถูกต้อง (หรือพยายามgrep -Po '(*UTF8)\X')
Stéphane Chazelas

2
@SijaanHallak สิ่งเหล่านี้อาจเป็นประโยชน์: joelonsoftware.com/articles/Unicode.html , eev.ee/blog/2015/09/12/dark-corners-of-unicode
jpmc26

6

หากคุณมีperl6ในกล่องของคุณ:

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

ทำงานโดยไม่คำนึงถึงสถานที่ของคุณ


6

มีหลายawkรุ่น

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

ที่ดี! แต่สำหรับรุ่น nAWK ของฉัน ("One True AWK") ที่ใช้งานไม่ได้ อย่างไรก็ตามเรื่องนี้ไม่หลอกลวง: awk -v FS='' -v OFS='\n' '{$1=$1};1' (สงสัยว่าถ้าเป็นแบบพกพามากขึ้นเนื่องจาก-F ''อาจผลผลิต ERE: //)
eruve

4

ด้านล่างจะเป็นแบบทั่วไป:

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


4

เนื่องจากคุณขอคำตอบเป็นการเฉพาะในการทุบตีต่อไปนี้เป็นวิธีการใช้การทุบตีบริสุทธิ์:

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

โปรดทราบว่านี่จะขึ้นบรรทัดใหม่ในตอนท้ายของ " เอกสารที่นี่ " หากคุณต้องการหลีกเลี่ยงปัญหานั้น แต่ยังคงวนซ้ำอักขระที่มี bash loop ให้ใช้printfเพื่อหลีกเลี่ยงการขึ้นบรรทัดใหม่

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

4

นอกจากนี้Python 2ยังสามารถใช้งานได้จากบรรทัดคำสั่ง:

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

หรือ:

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

หรือ (ตามความเห็นโดย 1_CR) ด้วยPython 3 :

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

4

คุณสามารถใช้fold (1)คำสั่ง มันจะมีประสิทธิภาพมากกว่าและgrepsed

$ 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
$

ความแตกต่างที่สำคัญอย่างหนึ่งคือการพับที่จะสร้างบรรทัดว่างในเอาต์พุต:

$ 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
$ 

3

คุณสามารถจัดการอักขระหลายไบต์เช่น:

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

ซึ่งจะมีประโยชน์สวยเมื่อคุณกำลังทำงานกับการถ่ายทอดสดการป้อนข้อมูลเพราะมีบัฟเฟอร์ไม่มีและตัวอักษรที่มีการพิมพ์เป็นเร็ว ๆ นี้ก็เป็นทั้ง


NP เราควรเพิ่มบันทึกเกี่ยวกับสถานที่หรือไม่
cuonglm

ใช้งานไม่ได้กับการรวมอักขระเช่นคำว่าStéphane Chazelas แต่ด้วยการฟื้นฟูที่เหมาะสมสิ่งนี้ไม่สำคัญ
kay ผิดหวังใน SE

@Kay - มันใช้งานได้สำหรับการรวมตัวอักษรถ้าคุณต้องการ - นั่นคือสิ่งที่sedสคริปต์สำหรับ ฉันไม่น่าจะเขียนเรื่องนี้เลยตอนนี้ - ฉันค่อนข้างง่วงนอน มันมีประโยชน์จริง ๆ เมื่ออ่านเทอร์มินัล
mikeserv

@cuonglm - ถ้าคุณชอบ มันควรจะทำงานให้กับสถานที่ที่กำหนดให้ libc มีสติแม้ว่า
mikeserv

โปรดทราบว่าddจะแบ่งอักขระหลายไบต์ดังนั้นเอาต์พุตจะไม่เป็นข้อความอีกต่อไปดังนั้นพฤติกรรมของ sed จะไม่ได้รับการระบุตาม POSIX
Stéphane Chazelas

3

คุณอาจใช้ขอบเขตของคำได้เช่นกัน ..

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

1

ในทุบตี:

สิ่งนี้ใช้ได้กับข้อความใด ๆ และมีเพียง bash internals (ไม่มียูทิลิตี้ภายนอกที่เรียกว่า) ดังนั้นควรรวดเร็วในสตริงที่สั้นมาก

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

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

เอาท์พุท:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

หากการเปลี่ยน IFS และเปลี่ยนพารามิเตอร์ตำแหน่งคุณสามารถหลีกเลี่ยงการเรียก sub-shell ได้:

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

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

อัปเดต ที่นี่เป็นแฮ็ค | เร็วที่สุด | วิธี 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

สำหรับสุดยอดมากขึ้น

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
}

สิ่งนี้จะให้ผลลัพธ์ที่แตกต่างกันfold -b1หรือไม่
JigglyNaga

เนื่องจากแต่ละไบต์มีความกว้าง = 1 ผลลัพธ์จะเหมือนกัน!
Jonah

1
ดังนั้นนี่ไม่ใช่คำตอบที่ซ้ำกันก่อนหน้านี้อย่างไร
JigglyNaga

เพราะมันแสดงให้เห็นว่า cmd เดียวกันนั้นมี argyment ที่แตกต่างกันและเป็นเรื่องที่น่ารู้
Jonah

1
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')

varนี้จะแยกคำของคุณและเก็บไว้ในอาร์เรย์


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.