Як відрізняти файли від ігнорування коментарів (рядки, що починаються з #)?


55

У мене два файли конфігурації, оригінал від менеджера пакунків та індивідуальний модифікований я. Я додав кілька коментарів, щоб описати поведінку.

Як я можу запускати diffфайли конфігурації, пропускаючи коментарі? Коментований рядок визначається:

  • необов'язковий провідний пробіл (вкладки та пробіли)
  • хеш-знак ( #)
  • будь-який інший персонаж

Найпростіший регулярний вираз, який пропускає першу вимогу, був би #.*. Я спробував --ignore-matching-lines=RE( -I RE) варіант GNU diff 3.0, але я не міг змусити його працювати з цим RE. Я також спробував .*#.*і .*\#.*без везіння. Буквально ставлячи рядок ( Port 631) так, що REнічого не відповідає, а також не допомагає поставити RE між косими рисами.

Як запропоновано в інструменті "diff", аромат регексу здається відсутнім? , Я спробував grep -G:

grep -G '#.*' file

Здається, це відповідає коментарям, але це не працює diff -I '#.*' file1 file2.

Отже, як слід використовувати цей варіант? Як я можу змусити diffпропустити певні рядки (у моєму випадку коментарі)? Будь ласка, не grepпропонуйте використовувати файл та порівнювати тимчасові файли.


12
Цей -Iпараметр спричиняє ігнорування блоку лише у тому випадку, якщо всі його рядки відповідають регулярному вираженню. Таким чином, ви можете ігнорувати зміни лише для коментарів таким чином, але не зміни коментарів, що знаходяться поруч із зміною без коментарів.
Жил "ТАК - перестань бути злим"

@Gilles: Дякую, зараз я зрозумів, чому diff -Iвін не веде себе так, як я очікував. Я оновив свою відповідь прикладом, який пояснив цю поведінку для мене.
Лекенштейн

Відповіді:


49

За словами Жиля, -Iопція ігнорує рядок лише в тому випадку, якщо всередині цього набору нічого іншого, крім матчу -I. Я не отримав його повністю, поки не перевірив.

Тест

У моєму тесті задіяно три файли:
Файл test1:

    text

Файл test2:

    text
    #comment

Файл test3:

    changed text
    #comment

Команди:

$ # 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

Альтернативний спосіб

Оскільки поки що немає відповіді, яка пояснює, як правильно скористатися -Iпараметром, я запропону альтернативу, яка працює в bash shell:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
  • diff -u - уніфікований розл
    • -B - ігнорувати порожні рядки
  • <(command)- функція bash, яка називається заміщенням процесу, яка відкриває дескриптор файлу для команди, це знімає необхідність у тимчасовому файлі
  • grep - команда для друку рядків (не), що відповідають шаблону
    • -v - показати невідповідні лінії
    • E - використовувати розширені регулярні вирази
    • '^\s*(#|$)' - регулярний вираз, що відповідає коментарям та порожнім рядкам
      • ^ - відповідність початку рядка
      • \s* - збігайте пробіли (вкладки та пробіли), якщо такі є
      • (#|$) збігаються з хеш-позначкою або, як варіант, кінцем рядка

6

Спробуйте:

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

Зверніть увагу, що регулярний вираз повинен відповідати відповідному рядку в обох файлах, і він відповідає кожній зміненій рядку, щоб працювати, інакше він все одно покаже різницю.

Використовуйте одинарні лапки, щоб захистити візерунок від розширення оболонки та уникнути символів, зарезервованих регулярними виразами (наприклад, дужки).

Ми можемо читати в diffutilsпосібнику:

Однак -Iвставляти або видаляти рядки, які містять регулярний вираз , лише ігнорується, якщо кожна змінена лінія в тексті (кожна вставка та кожне видалення) відповідає регулярному виразу.

Іншими словами, для кожної неігнорованої зміни diffдрукується повний набір змін у її околицях, включаючи ігнорування. Ви можете вказати більше, ніж один регулярний вираз для рядків, які слід ігнорувати, використовуючи більше одного -Iпараметра. diffнамагається співставити кожен рядок з кожним регулярним виразом, починаючи з останнього заданого.

Така поведінка також добре пояснюється арсеном .

Пов’язано: Як я можу виконати різницю, яка ігнорує всі коментарі?


2

Після пошуку в Інтернеті, альтернативний спосіб Лекенштейна є кращим, який я знайшов.

Але я хочу використовувати висновок dif як патч ... і є проблема, тому що номер рядка відмічено через "grep -v".

Тому я маю намір вдосконалити цей командний рядок:

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

Це не ідеально, але номер рядка зберігається у патч-файлі.

Однак якщо замість рядка коментарів буде доданий новий рядок ... коментар буде вироблятися Hunk FAILED під час виправлення, як ми бачимо нижче.

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

перевірити зараз нашу команду

$ 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 - це файл, створений шляхом заміни процесу. Рядок між "+ новим рядком" та "іншим текстом" є символом пробілу за замовчуванням, визначеним у нашому виразі sed, щоб замінити коментарі.

А тепер, що відбувається, коли ми застосовуємо цей патч:

$ 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

Рішення полягає в тому, щоб не використовувати уніфікований формат diff без -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

тепер робочий файл виправлення файлів (без гарантії результату в дуже складному різному процесі).


Ваша уніфікована різниця не застосовується через різниці в контексті. Ви можете використовувати diff -U0 one twoдля відключення контексту. Для виправлення, є купа інструментів, які можуть бути краще підходять, наприклад, kdiff3.
Лекенштейн

Дякуємо за -U0можливість відключення контексту. Примітка: kdiff3 - це графічний інструмент. Мені потрібен автоматичний інструмент для управління атрибутами об'єднання git.
syjust

vimdiffпідтримує тристоронні злиття, можливо, варто переглянути.
Лекенштейн

Якщо бути точнішим, мені потрібен інструмент сценарію для автоматизації процесу злиття git з виключеннями в sql-скрипт. kdiff3 та vimdiff - це інтерактивні інструменти, які не використовуються в моєму випадку.
syjust

1

Я зазвичай ігнорую цю суцільність будь-яким:

  • Генерування версій, що не коментуються, використовуючи grep -v "^#" | cat -sта відрізняючи ті чи ...
  • Використання vim -dдля перегляду файлів. Підсвічування синтаксису забезпечує огляд різниць коментарів проти не коментарів. Різне підсвічування лінійної різниці, щоб ви могли побачити, які значення або частини значень були змінені з першого погляду, робить це моїм улюбленим.

0

Ось що я використовую для видалення всіх коментованих рядків (навіть тих, що починаються з вкладки або пробілу), і порожніх:

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

або ти можеш зробити

sed -e '/^#.*/d' -e 's/#.*//g' | cat -s
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.