Lệnh grep để hiển thị tất cả các dòng bắt đầu và kết thúc với cùng một ký tự


8

Tôi muốn biết cách sử dụng grepđể hiển thị tất cả các dòng bắt đầu và kết thúc với cùng một ký tự.

Câu trả lời:


14

POSIXly:

pattern='\(.\).*\1
.'
grep -x -- "$pattern" file

Nó sẽ không hoạt động nếu dòng bắt đầu hoặc kết thúc bằng ký tự byte không hợp lệ, nếu bạn muốn bao gồm trường hợp đó, bạn có thể thêm LC_ALL=C, mặc dù chỉ LC_ALL=Choạt động với dữ liệu ký tự một byte.


perl6 dường như là công cụ tốt nhất, nếu bạn có nó trong hộp của mình:

$ printf '\ue7\u301 blah \u107\u327\n121\n1\n123\n' |
  perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'
ḉ blah ḉ
121
1

Mặc dù nó vẫn bóp nghẹt các ký tự không hợp lệ.


Lưu ý rằng perl6sẽ thay đổi văn bản của bạn bằng cách chuyển nó NFCthành dạng:

$ printf '\u0044\u0323\u0307\n' |
  perl6 -pe ''                  |
  perl -CI -ne 'printf "U+%04x\n", ord for split //'
U+1e0c
U+0307
U+000a

$ printf '\u0044\u0323\u0307\n' |
  perl -pe ''                   |
  perl -CI -ne 'printf "U+%04x\n", ord for split //'
U+0044
U+0323
U+0307
U+000a

Trong nội bộ, perl6lưu trữ chuỗi NFGdưới dạng (viết tắt Normalization Form Grapheme), đây là perl6cách được phát minh để đối phó với các biểu đồ chưa được phân tách đúng cách:

$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.chars.say'
1
$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.codes.say'
2

2
Việc xử lý văn bản Unicode của Perl không có gì là mẫu mực, đến mức nhiều tác vụ "đơn giản" trong Perl thực tế không thể thực hiện bằng các công cụ khác, ít nhất là với cùng một mức độ chính xác.
Dietrich Epp

1
Cần lưu ý rằng perl6sẽ thay đổi văn bản mặc dù (chuyển nó thành NFC (dạng chuẩn hóa "sáng tác")).
Stéphane Chazelas

@ StéphaneChazelas: Vâng, điểm công bằng. Cũng lưu ý rằng chuỗi trong perl6được lưu trữ ở NFGdạng ( Gfor Grapheme), đó là perl6cách để đối phó với các biểu đồ không được phân tách đúng.
cuonglm

10

Không phải grep mà là awk:

awk -F "" 'NF && $1 == $NF'

Những trường hợp đặc biệt được xử lý:

  • nó không in các dòng trống
  • nó luôn in các dòng 1 ký tự

Một FS trống chia các bản ghi vào một ký tự cho mỗi lĩnh vực trong gawk, mawkbusybox awk(byte, không ký tự cho sau này hai), nhưng không phải là tiêu chuẩn và không làm việc trong hiện thực của awknguồn gốc từ một bản gốc bằng A, W và K như trên BSD và các đơn vị thương mại. Di động hơn nhưng nhiều hơn để gõ:

awk '/./ && substr($0,1,1) == substr($0,length)'

1
Lưu ý rằng FSvì chuỗi rỗng không phải là tiêu chuẩn và sẽ không hoạt động trong một số awktriển khai.
cuonglm

2
Thay thế mà tránh được tách và hoàn toàn di động (thậm chí đến tối đa khủng khiếp Solaris awk 'cũ') awk 'length&&substr($0,1,1)==substr($0,length)'(lưu ý đối số mặc định của length$0, và hành động mặc định là {print $0})
dave_thompson_085

@ dave_thndry_085: thx, tôi chỉ sử dụng gợi ý hành động mặc định của bạn để có lệnh ngắn nhất.
rudimeier

Firne. Một sửa chữa nhỏ; thử nghiệm của tôi cho Solaris cũ awk đã bị nhầm (tôi vô tình có xpg4 trên) nhưng phương pháp này hoạt động nawkgần như tồi tệ :-)
dave_thedom_085

8
grep -xe '\(.\).*\1' -e .

Thí dụ:

$ printf '%s\n' il y était cet été  | grep -xe '\(.\).*\1' -e .
y
été

-xlà cho trận đấu chính xác (trận đấu trên toàn bộ dòng). \1là một tài liệu tham khảo ngược cho nhân vật bị bắt trong \(.\). Chúng tôi thêm một -e .để chăm sóc trường hợp đặc biệt của một dòng chứa một ký tự đơn.

Nó giả sử đầu vào chứa văn bản hợp lệ trong ngôn ngữ hiện tại.

Trận đấu diễn ra trên ký tự , không phải byte (ví dụ, trong é trong UTF-8 là hai byte 0xc3 0xa9 chẳng hạn), cũng không phải cụm graphem (nó sẽ không hoạt động nếu những é đó được viết ở dạng phân tách của chúng etheo sau là U + 0301 kết hợp giọng cấp tính chẳng hạn).

Để làm việc trên các cụm đồ thị, với một grephỗ trợ -Pcho PCRE:

$ printf 'e\u0301te\u0301\n' | grep -xPe '(\X).*\1|\X'
été

Giả định rằng sự phân tách là giống nhau cho hai cụm, ví dụ, một biểu thức c U+0301 U+0327sẽ không khớp với một biểu thức là c U+0327 U+0301hoặc ć( U+0107) U+0327hoặc ç( U+00E7) U+0301hoặc ( U+1E09). Vì vậy, bạn cần thực hiện kiểm tra trên một biểu mẫu chuẩn hóa:

$ printf '\ue7\u301 blah \u107\u327\n' |
  perl -MUnicode::Normalize -C -ne '
    print if /^\X$/ || NFC($_) =~ /^(\X).*\1$/'
ḉ blah ḉ

1
Nếu bạn có perl6, sau đó perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'nên làm tất cả các công việc cho bạn.
cuonglm

1

Thay thế nhanh python2:

python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l[0])]' < input.txt

Thí dụ:

$ python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l[0])]' < input.txt  | cat -A 
nathan$
 ookie $
a line a$

Sẽ không thành công nếu dòng chứa dấu cách hoặc dấu cách hàng đầu, ví dụ `121`.
cuonglm

@cuonglm đó là sự thật. Nhưng dấu vết hoặc khoảng trắng hàng đầu là một yêu cầu? Điều này thực hiện công việc yêu cầu - kiểm tra xem nhân vật chính và nhân vật cuối cùng có giống nhau không. Khoảng trắng vẫn là một nhân vật ascii, phải không?
Sergiy Kolodyazhnyy

@cuonglm của bạn đã thất bại với dấu vết và dẫn đầu không gian, nhân tiện :)
Sergiy Kolodyazhnyy

Mã của bạn loại bỏ các khoảng trắng hàng đầu và dấu, vì vậy nó thay đổi dòng đầu vào. Ngoài ra, nó cung cấp một lỗi cho các dòng trống.
rudimeier

@Serg: Thế nào? Câu trả lời của tôi chỉ grepping, nó không sửa đổi đầu vào.
cuonglm
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.