Bagaimana cara membedakan file yang mengabaikan komentar (baris dimulai dengan #)?

55

Saya punya dua file konfigurasi, yang asli dari manajer paket dan yang disesuaikan dimodifikasi oleh saya sendiri. Saya telah menambahkan beberapa komentar untuk menggambarkan perilaku.

Bagaimana saya bisa menjalankan difffile konfigurasi, melewatkan komentar? Baris yang dikomentari ditentukan oleh:

  • spasi kosong opsional (tab dan spasi) opsional
  • tanda pagar ( #)
  • karakter apa pun lainnya

Ekspresi reguler (paling sederhana) yang melewatkan persyaratan pertama adalah #.*. Saya mencoba opsi --ignore-matching-lines=RE( -I RE) dari GNU diff 3.0, tetapi saya tidak bisa membuatnya bekerja dengan RE itu. Saya juga mencoba .*#.*dan .*\#.*tidak berhasil. Secara harfiah menempatkan garis ( Port 631) karena REtidak cocok dengan apa pun, juga tidak membantu menempatkan RE di antara garis miring.

Seperti yang disarankan dalam rasa "beda" alat regex tampaknya kurang? , Saya mencoba grep -G:

grep -G '#.*' file

Ini sepertinya cocok dengan komentar, tetapi tidak berhasil diff -I '#.*' file1 file2.

Jadi, bagaimana seharusnya opsi ini digunakan? Bagaimana saya bisa diffmelewati baris tertentu (dalam kasus saya, komentar)? Tolong jangan menyarankan grepfile dan membandingkan file sementara.

Lekensteyn
sumber
12
The -Ipilihan menyebabkan blok untuk diabaikan hanya jika semua lini cocok regexp. Jadi Anda dapat mengabaikan perubahan hanya-komentar seperti itu, tetapi bukan perubahan komentar yang dekat dengan perubahan non-komentar.
Gilles 'SO- stop being evil'
@Gilles: Terima kasih, sekarang saya mengerti mengapa diff -Itidak berperilaku seperti yang saya harapkan. Saya memperbarui jawaban saya dengan contoh yang menjelaskan perilaku ini untuk saya.
Lekensteyn

Jawaban:

49

Menurut Gilles, -Iopsi hanya mengabaikan garis jika tidak ada yang lain di dalam set yang cocok kecuali untuk pertandingan -I. Saya tidak sepenuhnya mendapatkannya sampai saya mengujinya.

Ujian

Tiga file terlibat dalam pengujian saya:
File test1:

    text

File test2:

    text
    #comment

File test3:

    changed text
    #comment

Perintah:

$ # comparing files with comment-only changes
$ diff -u -I '#.*' test{1,2}
$ # comparing files with both comment and regular changes
$ diff -u -I '#.*' test{2,3}
--- test2       2011-07-20 16:38:59.717701430 +0200
+++ test3       2011-07-20 16:39:10.187701435 +0200
@@ -1,2 +1,2 @@
-text
+changed text
 #comment

Cara alternatif

Karena tidak ada jawaban sejauh ini menjelaskan cara menggunakan -Iopsi dengan benar, saya akan memberikan alternatif yang bekerja di shell bash:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
  • diff -u - unified diff
    • -B - abaikan garis kosong
  • <(command)- fitur bash yang disebut substitusi proses yang membuka deskriptor file untuk perintah, ini menghilangkan kebutuhan untuk file sementara
  • grep - perintah untuk mencetak garis (bukan) yang cocok dengan suatu pola
    • -v - tampilkan garis yang tidak cocok
    • E - Gunakan persamaan reguler yang diperluas
    • '^\s*(#|$)' - ekspresi reguler yang cocok dengan komentar dan baris kosong
      • ^ - cocok dengan awal garis
      • \s* - cocokkan spasi putih (tab dan spasi) jika ada
      • (#|$) cocok dengan tanda pagar, atau sebagai alternatif, akhir baris
Lekensteyn
sumber
6

Mencoba:

diff -b -I '^#' -I '^ #' file1 file2

Harap dicatat bahwa regex harus cocok dengan baris yang sesuai di kedua file dan cocok dengan setiap baris yang diubah di bongkahan agar berfungsi, jika tidak maka masih akan menunjukkan perbedaan.

Gunakan tanda kutip tunggal untuk melindungi pola dari pengembangan shell dan untuk menghindari karakter yang dicadangkan oleh regex (mis. Tanda kurung).

Kita bisa membaca secara diffutilsmanual:

Namun, -Ihanya mengabaikan penyisipan atau penghapusan garis yang berisi ekspresi reguler jika setiap baris yang diubah dalam bingkisan (setiap penyisipan dan setiap penghapusan) cocok dengan ekspresi reguler.

Dengan kata lain, untuk setiap perubahan yang tidak dapat diabaikan, diffmencetak seluruh rangkaian perubahan di sekitarnya, termasuk yang dapat diabaikan. Anda dapat menentukan lebih dari satu ekspresi reguler untuk diabaikan baris dengan menggunakan lebih dari satu -Iopsi. diffmencoba mencocokkan setiap baris dengan setiap ekspresi reguler, dimulai dengan yang terakhir diberikan.

Perilaku ini juga dijelaskan dengan baik oleh Armel di sini .

Terkait: Bagaimana saya bisa melakukan diff yang mengabaikan semua komentar?

kenorb
sumber
2

Setelah mencari di web, cara alternatif Lekensteyn adalah yang lebih baik yang saya temukan.

Tapi saya ingin menggunakan keluaran dif sebagai patch ... dan ada masalah karena nomor baris dicatat karena "grep -v".

Jadi saya bermaksud untuk meningkatkan baris perintah ini:

diff -u -B <(sed 's/^[[:blank:]]*#.*$/ /' file1)  <(sed 's/^[[:blank:]]*#.*$/ /' file2)

Itu tidak sempurna tetapi nomor baris disimpan dalam file tambalan.

Namun, jika baris baru ditambahkan sebagai ganti baris komentar ... komentar akan menghasilkan Hunk GAGAL saat menambal seperti yang dapat kita lihat di bawah.

File test1:
  text
  #comment
  other text
File test2:
  text
  new line here
  #comment changed
  other text changed

uji sekarang perintah kami

$ echo -e "#!/usr/bin/sed -f\ns/^[[:blank:]]*#.*$/ /" > outcom.sed
$ echo "diff -u -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ chmod +x mydiff.sh outcom.sed
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
--- /dev/fd/63  2014-08-23 10:05:08.000000000 +0200
+++ /dev/fd/62  2014-08-23 10:05:08.000000000 +0200
@@ -1,2 +1,3 @@
 text
+new line

-other text
+other text changed

/ dev / fd / 62 & / dev / fd / 63 adalah file yang diproduksi oleh substitusi proses. Baris antara "+ baris baru" dan "-lain teks" adalah karakter spasi default yang ditentukan dalam ekspresi sed kami untuk mengganti komentar.

Dan sekarang, apa yang terjadi ketika kita menerapkan tambalan ini:

$ patch -p0 file1 < file.dif 
patching file file1
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file file1.rej

Solusinya adalah jangan menggunakan format unified diff tanpa -u

$ echo "diff -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
1a2
> new line
3c4
< other text
---
> other text changed
$ patch -p0 file1 < file.dif 
patching file file1
$ cat file1
text
new line
#comment
other text changed

sekarang menambal file yang berfungsi (tanpa jaminan hasil dalam proses diff yang sangat kompleks).

syjust
sumber
Diff terpadu Anda gagal diterapkan karena perbedaan konteks. Anda dapat menggunakannya diff -U0 one twountuk menonaktifkan konteks. Untuk menambal, ada banyak alat yang mungkin lebih cocok seperti kdiff3.
Lekensteyn
Terima kasih atas -U0opsi untuk menonaktifkan konteks. Catatan: kdiff3 adalah alat grafis. Saya memerlukan alat otomatis untuk mengelola atribut git merge.
syjust
vimdiffmendukung penggabungan tiga arah, mungkin layak untuk dilihat.
Lekensteyn
lebih tepatnya, saya memerlukan alat skrip untuk mengotomatisasi proses git merge dengan mengecualikannya dalam skrip sql. kdiff3 dan vimdiff adalah alat interaktif, tidak dapat digunakan dalam kasus saya.
syjust
1

Saya biasanya mengabaikan kekacauan ini oleh:

  • Menghasilkan versi yang tidak dikomentari menggunakan grep -v "^#" | cat -sdan ...
  • Menggunakan vim -duntuk melihat file. Penyorotan sintaks menangani membuat perbedaan komentar vs non-komentar cukup jelas. Sorotan perbedaan perbedaan in-line sehingga Anda dapat melihat nilai atau bagian nilai apa yang telah diubah sekilas menjadikan ini favorit saya.
Caleb
sumber
0

Inilah yang saya gunakan untuk menghapus semua baris yang dikomentari -bahkan yang dimulai dengan tab atau spasi- dan yang kosong:

egrep -v "^$|^[[:space:]]*#" /path/to/file

atau kamu bisa melakukannya

sed -e '/^#.*/d' -e 's/#.*//g' | cat -s
Philomath
sumber