Ви можете домогтися цього, керуючи форматуванням старих / нових / незмінних рядків у 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