Ekspresi reguler menggunakan \\ vs using \

10

Kenapa

grep e\\.g\\. <<< "this is an e.g. wow"

dan

grep e\.g\. <<< "this is an e.g. wow"

melakukan hal yang sama?

Jika saya menambahkan garis miring ketiga, hasilnya juga sama. TETAPI, setelah saya menambahkan tebasan keempat, itu tidak lagi berfungsi. Ini ada hubungannya dengan pertanyaan dari ujian lama untuk kelas. Ia bertanya apakah yang dengan dua backslash satu akan bekerja untuk menghasilkan garis dengan "misalnya" Saya awalnya berpikir itu tidak akan berhasil, tetapi saya mencoba untuk memastikan dan ternyata berhasil. Apa penjelasannya?

Wyatt Grant
sumber
Saya pikir bash akan menerima \\\.dan memberi grep \.tetapi tidak. pertanyaan yang bagus

Jawaban:

9

Pertama, perhatikan bahwa slash tunggal terlalu cocok:

$ echo $'eegg \n e.g.' | grep e\.g\.
eegg
 e.g.

Sejauh menyangkut Bash , periode yang lolos sama dengan periode. Bash lolos pada periode untuk Grep . Untuk grep, suatu periode cocok dengan apa pun.

Sekarang, pertimbangkan:

$ echo $'eegg \n e.g.' | grep e\\.g\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\.g\\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\\.g\\\\.
$

Ketika Bash melihat double-slash, menguranginya menjadi tebasan tunggal dan meneruskannya ke grep yang, dalam tes pertama dari tiga tes di atas, melihat, seperti yang kita inginkan, tebasan tunggal sebelum periode. Jadi, ini melakukan hal yang benar.

Dengan tebasan tiga kali lipat, Bash mengurangi dua tebasan pertama menjadi tebasan tunggal. Kemudian ia melihat \.. Karena periode yang lolos tidak memiliki arti khusus untuk Bash, ini direduksi menjadi periode yang sederhana. Hasilnya adalah grep melihat, seperti yang kita inginkan, garis miring sebelum titik.

Dengan empat tebasan, Bash mengurangi setiap pasangan menjadi tebasan tunggal. Bash meneruskan untuk meraih dua garis miring dan satu periode. grep melihat dua garis miring dan periode dan mengurangi dua garis miring ke satu literal slash. Kecuali jika input memiliki garis miring yang diikuti oleh karakter apa pun, tidak ada kecocokan.

Untuk menggambarkan yang terakhir, ingatlah bahwa di dalam tanda kutip tunggal, semua karakter adalah literal. Dengan demikian, mengingat tiga baris input berikut, perintah grep hanya cocok pada baris dengan slash literal pada input:

$ echo 'eegg
e.g.
e\.g\.' |  grep e\\\\.g\\\\.
e\.g\.

Ringkasan perilaku Bash

Untuk Bash, aturannya adalah

  • Dua garis miring dikurangi menjadi satu garis miring.

  • Garis miring di depan karakter normal, seperti titik, hanyalah karakter normal (titik).

Jadi:

$ echo \. \\. \\\. \\\\.
. \. \. \\.

Ada cara sederhana untuk menghindari semua kebingungan ini: pada baris perintah Bash, ekspresi reguler harus ditempatkan dalam tanda kutip tunggal. Di dalam tanda kutip tunggal, Bash meninggalkan semuanya sendirian.

$ echo '\. \\. \\\. \\\\.'  # Note single-quotes
\. \\. \\\. \\\\.
John1024
sumber
Pertanyaan: Dibutuhkan dua garis miring terbalik untuk bash untuk melihatnya sebagai garis miring terbalik (satu adalah urutan melarikan diri, yang lainnya adalah garis miring terbalik literal). Jadi, ketika ada 3 apakah bash memperlakukan pemukul ketiga sebagai urutan melarikan diri juga? Karena tidak ada yang lolos, kemudian dibuang?
Franz Kafka
@DanielAmaya Yang ketiga diperlakukan sebagai pelarian untuk karakter yang mengikuti. Dalam kasus kami, karakter itu adalah periode dan, untuk bash (tidak seperti grep), periode yang lolos adalah periode yang sederhana. bash kemudian melewati periode polos ke grep.
John1024
@DanielAmaya Lihat jawaban yang diperbarui untuk echopernyataan yang menggambarkan apa yang dilakukan bash dalam kasus ini.
John1024
2
@DanielAmaya Dalam kedua kasus, bash mengurangi Dua garis miring pertama menjadi garis miring tunggal. Apa yang tersisa adalah \.atau .. Untuk bash, keduanya sama: keduanya setara dengan periode sederhana. Karenanya, secara total, apa yang dikirimkan bash ke grep adalah sama untuk keduanya: tebasan tunggal diikuti oleh suatu periode.
John1024
1
Hanya tambahan kecil - menggunakan echocara tidak bisa diandalkan untuk menguji regexp karena banyak implementasi program ini. Misalnya di bawah zsh saya (built-in echo) echo \. \\. \\\. \\\\. \\\\\.memberi . \. \. \. \., tetapi /bin/echo \. \\. \\\. \\\\. \\\\\.kembali . \. \. \\. \\.. Sesuatu seperti printf "%s" ...mungkin cara yang lebih baik.
jimmij
4

Outputnya sama hanya untuk string Anda, tetapi secara umum ekspresi reguler itu melakukan hal yang berbeda. Mari kita sedikit modifikasi contoh Anda dengan menambahkan pola kedua e,g,(dengan koma), ketiga e\.g\.(titik), keempat e\,g\,(koma), dan -oopsi untuk grep untuk mencetak hanya bagian yang cocok.

  • Dalam kasus berikut .cocok dengan char apa pun (perhatikan ''sekitar e.g., saya akan datang untuk itu nanti)

    $ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    e,g,
  • Selanjutnya kita melarikan diri .dengan backslash \, jadi hanya literal yang .akan cocok:

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
  • Tetapi kita dapat melarikan diri \dengan yang lain \, sehingga literal \akan dicocokkan diikuti oleh .(yaitu setiap karakter):

    $ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    e\,g\,
  • Tetapi jika kita ingin mencocokkan hanya \.belum \,maka yang lain \diperlukan untuk melarikan diri makna khusus dari titik:

    $ grep -o 'e\\\.g\\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.

Sekarang, karena Anda tidak menggunakan ''argumen grep, Anda perlu menambahkan backslash lain untuk menghindari backslash dari interpretasi shell, jadi:

grep 'e\.g\.'     => grep e\\.g\\.
grep 'e\\.g\\.'   => grep e\\\\.g\\\\.  (each backslash has to be quoted separately)
grep 'e\\\.g\\\.' => grep e\\\\\\.g\\\\\\. (3 x 2 = 6 backslashes in total)
jimmij
sumber
3

Ketika Anda melakukan grep e\.g\., shell mengkonsumsi backslash, sehingga Anda melakukan grep e.g., yang cocok. Saat Anda melakukan grep e\\.g\\., shell kembali mengkonsumsi garis miring, dan sekarang Anda melakukan grep e\.\g., yang lagi-lagi cocok. Sekarang, garis miring terbalik ke shell terlihat seperti \\. Jadi, ketika Anda memilikinya \\, yang pertama adalah urutan pelarian, yang kedua adalah backslash literal. Ketika Anda melakukan grep e\\\.g\\\., itu masih berakhir menjadi grep e\.\g., karena tidak ada urutan pelarian ( \) sebelum yang pertama \untuk membuatnya menjadi literal \. Perlu diingat \ adalah garis miring terbalik, sehingga grep e\\\\.\\\\gakhirnya menjadi grep e\\.g\\., yang jelas tidak cocok.

Untuk melihat bagaimana shell melihat apa yang Anda lakukan, gunakan echo (mis. echo grep e\\.g\\. <<< "this is an e.g. wow"Vs. echo grep e\\\\.g\\\\. <<< "this is an e.g. wow")

Franz Kafka
sumber
0

Kedua perintah menghasilkan output yang sama hanya untuk input Anda tetapi sebaliknya mereka berbeda. Untuk memahami apa yang sedang terjadi kita harus tahu bagaimana parameter ditafsirkan pertama kali oleh bashdan kemudian oleh grep.

Lolos dalam bash

\adalah karakter khusus yang membatalkan arti khusus dari karakter berikut termasuk \dirinya sendiri. Jika karakter berikut tidak memiliki arti khusus maka dilewatkan tanpa perubahan. Contoh dengan perintah dan hasil:

  • echo \a: a- karakter biasa lolos memberikan karakter
  • echo \\: \- karakter khusus lolos memberikan karakter
  • echo \\\a: \a- kombinasi spesial, biasa
  • echo \\\\: \\- kombinasi spesial, spesial

echoakan mencetak string yang dihasilkan setelah bashmengartikannya. Informasi lebih lanjut: dokumentasi pesta , pesta hacker wiki , POSIX spesifikasi .

.tidak memiliki arti khusus dalam bash. Ini adalah karakter biasa untuk shell. Di bawah ini adalah urutan yang relevan dengan contoh Anda:

  • echo .: .
  • echo \.: .
  • echo \\.: \.
  • echo \\\.: \.
  • echo \\\\.: \\.

Solusi sederhana untuk string literal di bash

Untuk melewati parameter secara harfiah, bashAnda dapat menggunakan satu petik yang 'lolos. Di antara kutipan tunggal, Anda tidak perlu memedulikan arti khusus karakter karena kutipan tunggal adalah satu-satunya karakter dengan makna khusus di sana. Anda dapat memasukkan satu kutipan setelah melampirkan bagian pertama dari string. Contoh
echo 'part1'\''part2':: part1'part2

Regex dalam grep

\adalah karakter escape dengan arti yang sama seperti di bash. .adalah karakter khusus yang mewakili kemunculan tunggal karakter apa pun . Lihat: POSIX regex , GNU grep regex . Contoh ekspresi regex:

  • .- cocok dengan karakter seperti aatau.
  • \.- hanya cocok .secara harfiah

Contoh Anda

Pada baris kedua setiap contoh di bawah ini Anda akan menemukan setara dengan satu kutipan 'menunjukkan yang string literal dilewatkan oleh bashuntuk grep. Kemudian setelah grepmelakukan pelarian satu-satunya karakter khusus yang mungkin dalam contoh adalah .mencocokkan karakter apa pun. Di baris ketiga ada deskripsi yang cocok dengan ekspresi itu.

  • grep e.g. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    ekarakter gapa saja karakter apa saja - cocok e.g.dan mungkin string lain sepertieagb
  • grep e\.g\. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    ekarakter gapa saja karakter apa saja - cocok e.g.dan mungkin string lain sepertiexgy
  • grep e\\.g\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.secara harfiah - hanya cocoke.g.
  • grep e\\\.g\\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.secara harfiah - hanya cocoke.g.
  • grep e\\\\.g\\\\. <<< "this is an e.g. wow"
    grep 'e\\.g\\.' <<< "this is an e.g. wow"
    e\karakter g\apa pun karakter apa saja - tidak cocoke.g.
pabouk
sumber