it-swarm-id.com

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

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 diff pada file konfigurasi, melewatkan komentar? Baris yang dikomentari ditentukan oleh:

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

Ekspresi reguler (paling sederhana) yang melewatkan persyaratan pertama adalah #.*. Saya mencoba --ignore-matching-lines=RE (-I RE) pilihan GNU diff 3.0, tapi saya tidak bisa membuatnya bekerja dengan RE itu. Saya juga mencoba .*#.* dan .*\#.* tanpa keberuntungan. Menempatkan garis secara harfiah (Port 631) karena RE tidak 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 bekerja untuk diff -I '#.*' file1 file2.

Jadi, bagaimana seharusnya opsi ini digunakan? Bagaimana saya bisa membuat diff melewati baris tertentu (dalam kasus saya, komentar)? Tolong jangan menyarankan greping file dan membandingkan file-file sementara.

56
Lekensteyn

Menurut Gilles, opsi -I 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 sejauh ini tidak ada jawaban yang menjelaskan cara menggunakan opsi -I Dengan benar, saya akan memberikan alternatif yang berfungsi dalam bash shell:

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

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 Shell yang meluas dan untuk menghindari karakter yang dicadangkan oleh regex (mis. Tanda kurung).

Kita dapat membaca diffutils manual:

Namun, -I hanya 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, diff mencetak set lengkap perubahan di sekitarnya, termasuk yang diabaikan. Anda dapat menentukan lebih dari satu ekspresi reguler untuk diabaikan baris dengan menggunakan lebih dari satu -I pilihan. diff mencoba untuk 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?

7
kenorb

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).

3
syjust

Saya biasanya mengabaikan kekacauan ini oleh:

  • Membuat versi yang tidak dikomentari menggunakan grep -v "^#" | cat -s dan membedakannya atau ...
  • Menggunakan vim -d untuk 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.
1
Caleb

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
0
Philomath