Bash script: chia từ trên mỗi chữ cái


17

Làm cách nào tôi có thể chia các chữ cái của một từ, với mỗi chữ cái trong một dòng riêng biệt?

Ví dụ, cho "StackOver" tôi muốn xem

S
t
a
c
k
O
v
e
r

Tôi mới làm quen với bash nên tôi không biết bắt đầu từ đâu.

Câu trả lời:


29

Tôi sẽ sử dụng grep:

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

hoặc sed:

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

Và nếu không gian trống ở cuối là một vấn đề:

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

Tất cả điều đó giả sử GNU / Linux.


grep -o. <<< ¿¿¿.. -o tìm kiếm cho MẪU được cung cấp phải không? và những gì nó làm ở đây trong lệnh của bạn?
Sijaan Hallak

1
@jimmij Tôi không thể tìm thấy bất kỳ trợ giúp về những gì <<< thực sự làm! Có ai giúp đỡ không?
Sijaan Hallak

3
@SijaanHallak Cái này được gọi là Here string, grosso modo tương đương với việc echo foo | ...chỉ cần gõ ít hơn. Xem tldp.org/LDP/abs/html/x17837.html
jimmij

1
@SijaanHallak thay đổi .thành \B(không khớp với ranh giới từ).
jimmij

1
@SijaanHallak - bạn có thể thả lần thứ hai sednhư sau:sed -et -e's/./\n&/g;//D'
mikeerv

19

Bạn có thể muốn phá vỡ các cụm grapheme thay vì các ký tự nếu mục đích là in văn bản theo chiều dọc. Ví dụ với một edấu trọng âm:

  • Với các cụm grapheme ( evới giọng cấp tính của nó sẽ là một cụm grapheme):

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

    (hoặc grep -Po '\X'với GNU grep được xây dựng với sự hỗ trợ của PCRE)

  • Với các ký tự (ở đây có GNU grep):

    $ printf '%s\n' $'Ste\u301phane' | grep -o .
    S
    t
    e
    
    p
    h
    a
    n
    e
    
  • foldcó nghĩa là phá vỡ các ký tự, nhưng GNU foldkhông hỗ trợ các ký tự nhiều byte, do đó, nó phá vỡ các byte thay vào đó:

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

Trên StackOver chỉ bao gồm các ký tự ASCII (vì vậy một byte cho mỗi ký tự, một ký tự trên cụm grapheme), cả ba sẽ cho kết quả như nhau.


Tôi ngạc nhiên grep -Pokhông làm những gì người ta mong đợi (như thế grep -P).
jimmij

@jimmij, ý bạn là gì? grep -Po .tìm các ký tự (và một dấu kết hợp cấp tính theo sau một ký tự dòng mới là không hợp lệ) và grep -Po '\X'tìm các cụm biểu đồ cho tôi. Bạn có thể cần một phiên bản gần đây của grep và / hoặc PCRE để nó hoạt động chính xác (hoặc thử grep -Po '(*UTF8)\X')
Stéphane Chazelas


6

Nếu bạn có perl6 trong hộp của bạn:

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

làm việc bất kể địa phương của bạn.


6

Với nhiều awkphiên bản

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

Tuyệt quá! Nhưng trên phiên bản nAWK của tôi ("One True AWK") không hoạt động. Tuy nhiên, đây là một mẹo nhỏ: awk -v FS='' -v OFS='\n' '{$1=$1};1' (tự hỏi liệu điều đó có dễ mang theo hơn không vì -F ''có thể mang lại ERE //:)
xóa

4

Dưới đây sẽ là chung chung:

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


4

Vì bạn đặc biệt yêu cầu một câu trả lời trong bash, đây là một cách để làm điều đó trong bash thuần túy:

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

Lưu ý rằng điều này sẽ bắt dòng mới ở cuối " tài liệu ở đây ". Nếu bạn muốn tránh điều đó, nhưng vẫn lặp lại các ký tự có vòng lặp bash, hãy sử dụng printfđể tránh dòng mới.

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

4

Ngoài ra Python 2 có thể được sử dụng từ dòng lệnh:

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

hoặc là:

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

hoặc (như nhận xét bởi 1_CR) với Python 3 :

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

4

Bạn có thể sử dụng fold (1)lệnh. Nó hiệu quả hơn 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
$

Một sự khác biệt đáng kể là nếp gấp sẽ tái tạo các dòng trống trong đầu ra:

$ 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

Bạn có thể xử lý các ký tự đa nhân như:

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

Điều này có thể khá tiện lợi khi bạn làm việc với đầu vào trực tiếp vì không có bộ đệm ở đó và một ký tự được in ngay sau khi toàn bộ .


NP, chúng ta có nên thêm một lưu ý về miền địa phương?
cuonglm

Không hoạt động để kết hợp các ký tự như câu trả lời của Stéphane Chazelas, nhưng với việc chuẩn hóa đúng cách, điều này không thành vấn đề.
kay thất vọng trong SE

@Kay - nó hoạt động để kết hợp các ký tự nếu bạn muốn - đó là những gì sedcác kịch bản dành cho. Tôi không có khả năng viết ngay bây giờ - tôi khá buồn ngủ. Tuy nhiên, nó thực sự hữu ích khi đọc một thiết bị đầu cuối.
mikeerv

@cuonglm - nếu bạn thích. nó chỉ nên làm việc cho miền địa phương, mặc dù có một libc lành mạnh.
mikeerv

Lưu ý rằng ddsẽ phá vỡ các ký tự đa dòng, do đó đầu ra sẽ không còn là văn bản nữa nên hành vi của sed sẽ không được chỉ định theo POSIX.
Stéphane Chazelas

3

Bạn cũng có thể sử dụng ranh giới từ ..

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

1

Trong bash:

Điều này hoạt động với bất kỳ văn bản nào và chỉ với nội bộ bash (không có tiện ích bên ngoài được gọi), vì vậy, nên nhanh chóng trên các chuỗi rất ngắn.

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

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

Đầu ra:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

Nếu bạn có thể thay đổi IFS và thay đổi các tham số vị trí, bạn cũng có thể tránh lệnh gọi shell phụ:

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

cập nhật ở đây là cách hacky | nhanh nhất | purBashBasing!

$ 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

cho nhiều điều tuyệt vời

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
}

Điều này sẽ bao giờ cho kết quả khác nhau fold -b1?
JigglyNaga

vì mỗi byte có chiều rộng = 1 nên kết quả sẽ giống nhau!
Giô-na

1
Vì vậy, làm thế nào đây không phải là một bản sao của câu trả lời trước đó ?
JigglyNaga

bởi vì nó cho thấy cùng một cmd với sự khác biệt, và đó là điều tốt đẹp để biết.
Giô-na

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

điều này sẽ phân chia từ của bạn và lưu trữ nó trong mảng var.


1
for x in $(echo "$yourWordhere" | grep -o '.')
do
    code to perform operation on individual character $x of your word
done
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.