Ada dua cara untuk menafsirkan pertanyaan ini; Saya akan membahas kedua kasus ini. Anda mungkin ingin menampilkan garis:
- yang berisi urutan empat digit yang dengan sendirinya bukan bagian dari urutan digit yang lebih panjang, atau
- yang berisi urutan empat digit tetapi tidak lagi urutan angka (bahkan tidak terpisah).
Misalnya, (1) akan ditampilkan 1234a56789
, tetapi (2) tidak.
Jika Anda ingin menampilkan semua baris yang berisi urutan empat digit yang dengan sendirinya bukan bagian dari urutan digit yang lebih lama, salah satu caranya adalah:
grep -P '(?<!\d)\d{4}(?!\d)' file
Ini menggunakan ekspresi reguler Perl , yang didukung oleh Ubuntu grep
( GNU grep ) -P
. Itu tidak akan cocok dengan teks suka 12345
, juga tidak akan cocok dengan 1234
atau 2345
yang merupakan bagian dari itu. Tapi itu akan cocok dengan 1234
in 1234a56789
.
Dalam ekspresi reguler Perl:
\d
berarti angka apa saja (ini adalah cara singkat untuk mengatakan [0-9]
atau [[:digit:]]
).
x{4}
cocok x
4 kali. ( {
}
Sintaks tidak khusus untuk ekspresi reguler Perl; itu dalam ekspresi reguler yang diperluas grep -E
juga.) Begitu \d{4}
juga dengan \d\d\d\d
.
(?<!\d)
adalah pernyataan pandangan ke belakang negatif lebar nol. Itu berarti "kecuali didahului oleh \d
."
(?!\d)
adalah pernyataan pandangan ke depan negatif lebar nol. Itu berarti "kecuali diikuti oleh \d
."
(?<!\d)
dan (?!\d)
jangan mencocokkan teks di luar urutan empat digit; alih-alih, mereka akan (saat digunakan bersama-sama) mencegah urutan empat digit dari dirinya sendiri dicocokkan jika itu adalah bagian dari urutan angka yang lebih panjang.
Menggunakan hanya melihat-belakang atau hanya melihat-depan tidak cukup karena urutan empat digit paling kanan atau paling kiri masih akan cocok.
Salah satu manfaat menggunakan pernyataan lihat-belakang dan lihat-depan adalah bahwa pola Anda hanya cocok dengan urutan empat digit itu sendiri, dan bukan teks di sekitarnya. Ini bermanfaat saat menggunakan penyorotan warna (dengan --color
opsi).
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
Secara default di Ubuntu, setiap pengguna memiliki filealias grep='grep --color=auto'
mereka . Jadi Anda mendapatkan penyorotan warna secara otomatis ketika Anda menjalankan perintah sederhana dimulai dengan (ini adalah saat alias diperluas) dan output standar adalah terminal (inilah yang memeriksa). Cocok biasanya disorot dalam warna merah (dekat dengan vermilion ), tetapi saya telah menunjukkannya dalam huruf miring dicetak tebal. Berikut screenshotnya:~.bashrc
grep
--color=auto
Dan Anda bahkan dapat membuat grep
cetak hanya teks yang cocok, dan bukan seluruh baris, dengan -o
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
Cara Alternatif, Tanpa Tegas dan Tegas
Namun, jika Anda:
- memerlukan perintah yang juga akan berjalan pada sistem yang
grep
tidak mendukung -P
atau tidak ingin menggunakan ekspresi reguler Perl, dan
- tidak perlu mencocokkan empat digit secara khusus - yang biasanya terjadi jika tujuan Anda hanya untuk menampilkan garis yang berisi kecocokan, dan
- tidak apa-apa dengan solusi yang sedikit kurang elegan
... maka Anda dapat mencapai ini dengan ekspresi reguler yang diperluas sebagai gantinya:
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
Ini cocok dengan empat digit dan karakter non-digit - atau awal atau akhir garis - yang mengelilinginya. Secara khusus:
[0-9]
cocok dengan digit mana pun (seperti [[:digit:]]
, atau \d
dalam ekspresi reguler Perl) dan {4}
berarti "empat kali." Jadi [0-9]{4}
cocok dengan urutan empat digit.
[^0-9]
cocok dengan karakter yang tidak berada dalam kisaran 0
melalui 9
. Ini sama dengan [^[:digit:]]
(atau \D
, dalam ekspresi reguler Perl).
^
, ketika itu tidak muncul dalam [
]
tanda kurung, cocok dengan awal baris. Demikian pula, $
cocok dengan akhir garis.
|
berarti atau dan tanda kurung untuk pengelompokan (seperti dalam aljabar). Jadi (^|[^0-9])
cocok dengan awal baris atau karakter non-digit, sementara ($|[^0-9])
cocok dengan akhir baris atau karakter non-digit.
Jadi kecocokan hanya terjadi pada garis yang berisi urutan empat digit ( [0-9]{4}
) yang secara bersamaan:
- di awal baris atau didahului oleh non-digit (
(^|[^0-9])
), dan
- di akhir baris atau diikuti oleh non-digit (
($|[^0-9])
).
Jika, di sisi lain, Anda ingin menampilkan semua baris yang mengandung urutan empat digit, tetapi tidak mengandung salah urutan lebih dari empat digit (bahkan salah satu yang terpisah dari urutan lain dari hanya empat digit), maka secara konseptual Anda tujuannya adalah menemukan garis yang cocok dengan satu pola tetapi tidak yang lain.
Oleh karena itu, bahkan jika Anda tahu bagaimana melakukannya dengan pola tunggal, saya sarankan menggunakan sesuatu seperti saran kedua matt ,grep
untuk dua pola secara terpisah.
Anda tidak mendapatkan banyak manfaat dari fitur lanjutan dari ekspresi reguler Perl saat melakukan itu, jadi Anda mungkin memilih untuk tidak menggunakannya. Namun sesuai dengan gaya di atas, berikut adalah pemendekan dari solusi matt menggunakan \d
(dan kawat gigi) sebagai pengganti [0-9]
:
grep -P '\d{4}' file | grep -Pv '\d{5}'
Sejak digunakan [0-9]
, cara matt lebih portabel - ini akan bekerja pada sistem yang grep
tidak mendukung ekspresi reguler Perl. Jika Anda menggunakan [0-9]
(atau [[:digit:]]
) alih-alih \d
, tetapi terus menggunakan {
}
, Anda mendapatkan portabilitas cara matt sedikit lebih ringkas:
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
Cara Alternatif, Dengan Pola Tunggal
Jika Anda benar-benar lebih suka grep
perintah itu
- menggunakan ekspresi reguler tunggal (bukan dua
grep
s dipisahkan oleh pipa , seperti di atas)
- untuk menampilkan garis yang mengandung setidaknya satu urutan empat digit,
- tetapi tidak ada urutan lima (atau lebih) digit,
- dan Anda tidak keberatan mencocokkan seluruh baris, bukan hanya digit (Anda mungkin tidak keberatan ini)
... maka Anda dapat menggunakan:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
The -x
merek bendera grep
hanya menampilkan garis-garis di mana seluruh pertandingan line (bukan setiap baris mengandung pertandingan).
Saya telah menggunakan ekspresi reguler Perl karena saya pikir singkatnya \d
dan \D
secara substansial meningkatkan kejelasan dalam kasus ini. Tetapi jika Anda membutuhkan sesuatu yang portabel untuk sistem yang grep
tidak mendukung -P
, Anda dapat menggantinya dengan [0-9]
dan [^0-9]
(atau dengan [[:digit:]]
dan [^[:digit]]
):
grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file
Cara kerja ekspresi reguler ini adalah:
Di tengah, \d{4}
atau [0-9]{4}
cocok dengan satu urutan empat digit. Kita mungkin memiliki lebih dari satu, tetapi kita harus memiliki setidaknya satu.
Di sebelah kiri, (\d{0,4}\D)*
atau ([0-9]{0,4}[^0-9])*
cocok dengan nol atau lebih ( *
) contoh tidak lebih dari empat digit diikuti oleh non-digit. Nol digit (yaitu, tidak ada) adalah satu kemungkinan untuk "tidak lebih dari empat digit." Ini cocok dengan (a) string kosong atau (b) string yang diakhiri dengan non-digit dan tidak mengandung urutan lebih dari empat digit.
Karena teks tepat di sebelah kiri tengah \d{4}
(atau [0-9]{4}
) harus kosong atau diakhiri dengan non-digit, ini mencegah pusat \d{4}
dari mencocokkan empat digit yang memiliki digit (kelima) lainnya tepat di sebelah kiri mereka.
Di sebelah kanan, (\D\d{0,4})*
atau ([^0-9][0-9]{0,4})*
cocok dengan nol atau lebih ( *
) contoh non-digit diikuti oleh tidak lebih dari empat digit (yang, seperti sebelumnya, bisa empat, tiga, dua, satu, atau bahkan tidak sama sekali). Ini cocok dengan (a) string kosong atau (b) string yang dimulai dengan non-digit dan tidak mengandung urutan lebih dari empat digit.
Karena teks segera di sebelah kanan pusat \d{4}
(atau [0-9]{4}
) harus kosong atau mulai dengan non-digit, ini mencegah pusat \d{4}
dari mencocokkan empat digit yang memiliki digit (kelima) lainnya tepat di sebelah kanannya.
Ini memastikan empat digit urutan hadir di suatu tempat, dan tidak ada urutan lima digit atau lebih yang hadir di mana saja.
Tidak buruk atau salah melakukannya dengan cara ini. Tetapi mungkin alasan paling penting untuk mempertimbangkan alternatif ini adalah bahwa ia mengklarifikasi manfaat menggunakan (atau serupa) sebagai gantinya, seperti yang disarankan di atas dan dalam jawaban matt .grep -P '\d{4}' file | grep -Pv '\d{5}'
Dengan cara itu, jelas tujuan Anda adalah memilih garis yang berisi satu hal tetapi bukan yang lain. Plus sintaksinya lebih sederhana (sehingga mungkin lebih cepat dipahami oleh banyak pembaca / pengelola).
1234a12345
ditampilkan, atau tidak?