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

<p>у меня есть два файла конфигурации, оригинальный от менеджера пакетов и один измененный мной. я добавил несколько комментариев для описания поведения.</p><p>как я могу запустить <code>diff</code> для файлов конфигурации, пропуская комментарии? комментируемая строка определяется как:</p><ul> <li>необязательный начальный пробел (табуляции и пробелы)</li> <li>знак хеша (<code>#</code>)</li> <li>ничего другого персонажа</li> </ul><p>(самое простое) регулярное выражение, пропускающее первое требование, будет <code>#.*</code>. я попробовал <code>--ignore-matching-lines=re</code> (<code>-i re</code>) опция gnu diff 3.0, но я не смог заставить ее работать с этим re. я также попробовал <code>.*#.*</code> а также <code>.*\#.*</code> без удачи. буквально ставить строку (<code>port 631</code>) поскольку <code>re</code> ничего не соответствует, и при этом это не помогает помещать re между слешами.</p><p>как указано в <a href="https://stackoverflow.com/questions/2072167/diff-tools-flavor-of-regex-seems-lacking/2072649#2072649">похоже, что в "diff" отсутствует регулярное выражение?</a> , я пробовал <code>grep -g</code>:</p><pre><code>grep -g '#.*' file </code></pre><p>это похоже на комментарии, но не работает для <code>diff -i '#.*' file1 file2</code>.</p><p>итак, как использовать эту опцию? как я могу заставить <code>diff</code> пропускать определенные строки (в моем случае, комментарии)? пожалуйста, не предлагайте <code>grep</code>ing файл и сравнивать временные файлы.</p>

<p>по словам жиля, опция \_<code>-i</code>\_ игнорирует только строку, если ничего внутри этого набора не совпадает, за исключением совпадения \_<code>-i</code>\_. я не получил его полностью, пока не проверил.</p><h2>тест</h2><p>в моем тесте участвуют три файла:<br> файл <strong> \_<code>test1</code>\_ </strong>:</p><pre>\_<code> text </code>\_</pre><p>файл <strong> \_<code>test2</code>\_ </strong>:</p><pre>\_<code> text #comment </code>\_</pre><p>файл <strong> \_<code>test3</code>\_ </strong>:</p><pre>\_<code> changed text #comment </code>\_</pre><p>команды:</p><pre>\_<code>$ # 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 </code>\_</pre><h3>альтернативный способ</h3><p>поскольку пока нет ответа, объясняющего, как правильно использовать параметр \_<code>-i</code>\_, я предоставлю альтернативу, которая работает в оболочках bash:</p><pre>\_<code>diff -u -b <(grep -ve '^\s*(#|$)' test1) <(grep -ve '^s(#|$)' test2) </code>\_</pre><ul> <li>\_<code>diff -u</code>\_ - унифицированный дифференциал <ul> <li>\_<code>-b</code>\_ - игнорировать пустые строки</li> </ul> </li> <li><code><(command)</code> - функция bash, называемая <a href="http://tldp.org/ldp/abs/html/process-sub.html">подстановка процесса</a> , которая открывает дескриптор файла для команды, что устраняет необходимость во временном файле</li> <li><code>grep</code> - команда для печати строк (не), соответствующих шаблону <ul> <li>\_<code>-v</code>\_ - показывать несовпадающие строки</li> <li><code>e</code> - использовать расширенные регулярные выражения</li> <li><code>'^\s*(#|$)' - регулярное выражение, соответствующее комментариям и пустым строкам  _^_ - соответствует началу строки _s_ - совпадать с пробелами (табуляцией и пробелами), если есть _(#|$)</code>\_ соответствует хеш-метке или, альтернативно, концу строки</li> </ul> </li> </ul> </li> </ul>

<p>пытаться:</p><pre><code>diff -b -i '^#' -i '^ #' file1 file2 </code></pre><p>обратите внимание, что регулярное выражение должно совпадать с соответствующей строкой в обоих файлах, и оно соответствует каждой измененной строке в блоке, чтобы работать, иначе оно все равно покажет разницу.</p><p>используйте одинарные кавычки, чтобы защитить шаблон от расширения shell и экранировать символы, зарезервированные для регулярных выражений (например, скобки).</p><p>мы можем прочитать в <a href="http://www.gnu.org/software/diffutils/manual/html\_node/specified-lines.html#specified-lines"><code>diffutils</code></a> руководстве:</p><blockquote> <p>однако, <code>-i</code> игнорирует только вставку или удаление строк, которые содержат регулярное выражение, если каждая измененная строка в блоке (каждая вставка и каждое удаление) соответствует регулярному выражению.</p> <p>другими словами, для каждого невосполнимого изменения <code>diff</code> печатает полный набор изменений в его окрестности, включая игнорируемые. вы можете указать более одного регулярного выражения для игнорируемых строк, используя более одного <code>-i</code> вариант. <code>diff</code> пытается сопоставить каждую строку с каждым регулярным выражением, начиная с последнего заданного.</p> </blockquote><p>это поведение также хорошо объясняется <a href="https://stackoverflow.com/a/15923606/55075">armel здесь</a> .</p><p>связанный: <a href="https://stackoverflow.com/q/7504059/55075">как я могу выполнить diff, который игнорирует все комментарии?</a> </p>

<p>после поиска по сети, альтернативный способ лекенштейна - лучший, который я нашел.</p><p>но я хочу использовать вывод diff как патч ... и есть проблема, потому что номер строки записывается из-за "grep -v".</p><p>поэтому я намерен улучшить эту командную строку:</p><pre><code>diff -u -b <(sed 's/^[[:blank:]]*#.*$/ /' file1) <(sed 's/^[[:blank:]]#.$/ /' file2) </code></pre><p>это не идеально, но номер строки хранится в файле патча.</p><p>однако, если вместо строки комментария будет добавлена новая строка ... комментарий будет производить hunk failed при исправлении, как мы видим ниже.</p><pre><code>file test1: text #comment other text file test2: text new line here #comment changed other text changed </code></pre><p>проверить сейчас нашу команду</p><pre><code>$ echo -e "#!/usr/bin/sed -fns/^[[: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 </code></pre><p>/ dev/fd/62 &/dev/fd/63 - это файл, созданный путем подстановки процесса. строка между "+ новая строка" и "-other text" является символом пробела по умолчанию, определенным в нашем выражении sed для замены комментариев.</p><p>а теперь, что будет, когда мы применим этот патч:</p><pre><code>$ 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 </code></pre><p>теперь рабочий файл патча (без гарантии результата в очень сложном процессе сравнения).</p>

<p>я обычно игнорирую этот беспорядок:</p><ul> <li>генерация некомментированных версий с использованием <code>grep -v "^#" | cat -s</code> и различие тех или ...</li> <li>с помощью <code>vim -d</code> чтобы посмотреть файлы. подсветка синтаксиса позволяет сделать различия между комментариями и комментариями, не являющимися комментариями, совершенно очевидными. подсветка diff различий в строке, чтобы вы могли сразу увидеть, какие значения или части значений были изменены, делает это моим любимым.</li> </ul>

<p>вот что я использую, чтобы удалить все закомментированные строки, даже те, которые начинаются с табуляции или пробела, и пустые:</p><pre><code>egrep -v "^$|^[[:space:]]*#" /path/to/file </code></pre><p>или вы можете сделать</p><pre><code>sed -e '/^#.*/d' -e 's/#.*//g' | cat -s </code></pre>