Ви можете домогтися цього, керуючи форматуванням старих / нових / незмінних рядків у diff
висновку GNU :
diff --new-line-format="" --unchanged-line-format="" file1 file2
Вхідні файли повинні бути відсортовані, щоб це працювало. За допомогою bash
(і zsh
) ви можете сортувати на місці із заміною процесу <( )
:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
У вищезазначеному придушуються нові та незмінні рядки, тому виводяться лише змінені (тобто видалені лінії у вашому випадку). Ви також можете використовувати кілька diff
варіантів , які інші рішення не пропонують, наприклад , як -i
ігнорувати випадок, або різні варіанти пробільні ( -E
, -b
, і -v
т.д.) для менш суворого відповідності.
Пояснення
Параметри --new-line-format
, --old-line-format
і --unchanged-line-format
дозволяють контролювати шлях diff
форматує відмінності, аналогічні printf
специфікатор формату. Ці параметри форматують відповідно нові (додані), старі (видалені) та незмінні рядки. Установлення порожнього "" запобігає виведенню такого рядка.
Якщо ви знайомі з уніфікованим форматом diff , ви можете частково відтворити його за допомогою:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
Специфікатор %L
- це відповідний рядок, і ми префіксуємо кожне з "+" "-" або "", наприклад diff -u
(зауважте, що він видає лише відмінності, йому не вистачає рядків ---
+++
і @@
у верхній частині кожної групової зміни). Ви також можете використовувати це , щоб робити інші корисні речі , як числа кожного рядка з %dn
.
diff
Метод (поряд з іншими пропозиціями comm
і join
) проводити тільки очікуваний результат з відсортованих введенням, хоча ви можете використовувати <(sort ...)
для сортування на місці. Ось простий awk
(nawk) скрипт (натхненний сценаріями, пов’язаними з відповіддю у відповіді Консолебокса), який приймає довільно впорядковані файли вводу та виводить пропущені рядки у порядку, який вони мають у файлі1.
# output lines in file1 that are not in file2
BEGIN { FS="" } # preserve whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; } # file1, index by lineno
(NR!=FNR) { ss2[$0]++; } # file2, index by string
END {
for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}
Це зберігає весь вміст file1 рядок за рядком в масиві ll1[]
, індексованому рядком-номером , а весь вміст file2 рядок за рядком в асоційованому масиві, індексованому вмістом рядка ss2[]
. Після зчитування обох файлів повторіть ll1
і переконайтесь за допомогою in
оператора, щоб визначити, чи є рядок у file1 у file2. ( diff
Якщо у нього є дублікати, це буде мати різний вихід до методу.)
Якщо файли є достатньо великими, що їх зберігання викликає проблеми з пам'яттю, ви можете торгувати процесором для пам'яті, зберігаючи лише файл1 і видаляючи відповідність по ходу зчитування файлу2.
BEGIN { FS="" }
(NR==FNR) { # file1, index by lineno and string
ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) { # file2
if ($0 in ss1) { delete ll1[ss1[$0]]; delete ss1[$0]; }
}
END {
for (ll=1; ll<=nl1; ll++) if (ll in ll1) print ll1[ll]
}
Вищезазначене зберігає весь вміст file1 у двох масивах, один індексований номером рядка ll1[]
, один індексований вмістом рядка ss1[]
. Потім, коли file2 зчитується, кожен рядок, що відповідає, видаляється з ll1[]
та ss1[]
. В кінці виводяться решта рядків з файлу1, зберігаючи початковий порядок.
У цьому випадку, із вказаною проблемою, ви також можете розділити та перемогти за допомогою GNU split
(фільтрація - це розширення GNU), повторювані запуски з кусками file1 та зчитування файлу2 повністю кожного разу:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
Зверніть увагу на використання та розміщення -
значень stdin
у gawk
командному рядку. Це забезпечується split
з file1 шматками 20000 рядків за викликом.
Для користувачів в системах , НЕ GNU, є майже напевно GNU Coreutils пакет можна отримати, в тому числі на OSX в рамках компанії Apple Xcode інструментів , який забезпечує GNU diff
, awk
хоча тільки POSIX / BSD , split
а не версія GNU.
awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt