perintah grep untuk menampilkan semua baris yang dimulai dan diakhiri dengan karakter yang sama

8

Saya ingin tahu cara menggunakan grepuntuk menampilkan semua baris yang dimulai dan diakhiri dengan karakter yang sama.

Nayan Jariwala
sumber

Jawaban:

14

POSIXly:

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

Ini tidak akan berfungsi jika baris dimulai atau diakhiri dengan karakter byte yang tidak valid, jika Anda ingin membahas kasus itu, Anda dapat menambahkan LC_ALL=C, meskipun hanya LC_ALL=Cbekerja dengan data karakter byte tunggal.


perl6 tampaknya menjadi alat terbaik, jika Anda memilikinya di kotak Anda:

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

Meskipun masih tersedak karakter yang tidak valid.


Catatan yang perl6akan mengubah teks Anda dengan mengubahnya menjadi NFC:

$ 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

Secara internal, perl6simpan string dalam NFGbentuk (kepanjangan Normalization Form Grapheme), yang perl6ditemukan cara untuk menangani grafem yang tidak dikomposisi dengan benar:

$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.chars.say'
1
$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.codes.say'
2
cuonglm
sumber
2
Penanganan Perl terhadap teks Unicode merupakan contoh yang patut dicontoh, sampai-sampai banyak tugas "sederhana" di Perl secara praktis tidak mungkin diterapkan dengan menggunakan alat lain, setidaknya dengan tingkat kebenaran yang sama.
Dietrich Epp
1
Perlu dicatat bahwa perl6akan mengubah teks (mengubahnya menjadi NFC (bentuk normalisasi "terdiri")).
Stéphane Chazelas
@ StéphaneChazelas: Ya, titik adil. Perhatikan juga bahwa string dalam perl6adalah store in NFGform ( Gfor Grapheme), yang merupakan perl6cara untuk menangani grafem yang tidak dikomposisi dengan benar.
cuonglm
10

Bukan grep tetapi awk:

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

Kasus-kasus khusus ini ditangani:

  • itu tidak mencetak garis kosong
  • selalu mencetak garis 1-karakter

FS kosong membagi catatan menjadi satu karakter per bidang dalam gawk, mawkdan busybox awk(byte, bukan karakter untuk dua yang terakhir), tetapi tidak standar dan tidak bekerja dalam implementasi yang awkditurunkan dari yang asli oleh A, W dan K seperti pada BSD dan Unives komersial. Lebih portabel tetapi lebih banyak untuk mengetik:

awk '/./ && substr($0,1,1) == substr($0,length)'
rudimeier
sumber
1
Perhatikan bahwa FSstring kosong tidak standar, dan tidak akan berfungsi dalam beberapa awkimplementasi.
cuonglm
2
Alternatif yang menghindari pemisahan dan sepenuhnya portabel (bahkan untuk awk 'lama' Solaris yang sangat buruk) awk 'length&&substr($0,1,1)==substr($0,length)'(perhatikan argumen default lengthadalah $0, dan aksi default adalah {print $0})
dave_thompson_085
@ dave_thompson_085: thx, saya hanya menggunakan petunjuk tindakan default Anda untuk memiliki perintah terpendek.
rudimeier
Firne. Satu koreksi kecil; pengujian saya untuk Solaris old awk keliru (saya tidak sengaja mengaktifkan xpg4), tetapi metode ini berhasil nawkyang hampir sama buruknya :-)
dave_thompson_085
8
grep -xe '\(.\).*\1' -e .

Contoh:

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

-xadalah untuk pencocokan tepat (kecocokan pada seluruh baris). \1menjadi referensi kembali ke karakter yang ditangkap di \(.\). Kami menambahkan -e .untuk menangani kasus khusus garis yang berisi satu karakter tunggal.

Itu mengasumsikan input berisi teks yang valid di lokal saat ini.

Kecocokannya adalah pada karakter , bukan byte (misalnya, é dalam UTF-8 adalah dua byte 0xc3 0xa9 misalnya), atau cluster graphem (itu tidak akan berfungsi jika é itu ditulis dalam bentuk terurai dengan ediikuti oleh U + 0301 menggabungkan aksen akut misalnya).

Untuk bekerja pada cluster graphem, dengan grepyang mendukung -PPCRE:

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

Itu mengasumsikan dekomposisi adalah sama untuk dua cluster, misalnya yang dinyatakan c U+0301 U+0327tidak akan cocok dengan yang dinyatakan sebagai c U+0327 U+0301atau ć( U+0107) U+0327atau ç( U+00E7) U+0301atau ḉ ( U+1E09). Untuk itu, Anda perlu melakukan pemeriksaan pada formulir yang dinormalisasi:

$ printf '\ue7\u301 blah \u107\u327\n' |
  perl -MUnicode::Normalize -C -ne '
    print if /^\X$/ || NFC($_) =~ /^(\X).*\1$/'
ḉ blah ḉ
Stéphane Chazelas
sumber
1
Jika sudah perl6, maka Anda perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'harus melakukan semua pekerjaan untuk Anda.
cuonglm
1

Alternatif python2 cepat:

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

Contoh:

$ 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$
Sergiy Kolodyazhnyy
sumber
Gagal jika baris berisi spasi tambahan atau spasi terdepan, misalnya `121`.
cuonglm
@cuonglm itu benar. Tetapi apakah membuntuti atau memimpin ruang putih merupakan persyaratan? Ini pekerjaan yang diminta - periksa apakah karakter utama dan karakter terakhir sama. Spasi masih karakter ascii, bukan?
Sergiy Kolodyazhnyy
@cuonglm milikmu gagal dengan tertinggal dan memimpin ruang juga, by the way :)
Sergiy Kolodyazhnyy
Kode Anda menghilangkan spasi putih awal dan akhir, sehingga mengubah baris input. Juga memberikan kesalahan untuk baris kosong.
rudimeier
@Serg: Bagaimana? jawaban saya hanya mengerti, itu tidak mengubah input.
cuonglm