Как я могу найти строки в одном файле, но не другие, используя скрипты bash?

Представьте файл 1:

#include "first.h"
#include "second.h"
#include "third.h"

// more code here
...

Представьте файл 2:

#include "fifth.h"
#include "second.h"
#include "eigth.h"

// more code here
...

Я хочу получить заголовки, которые включены в файл 2, но не в файл 1, только те строки. Таким образом, при запуске diff файла 1 и файла 2 будет выдавать:

#include "fifth.h"
#include "eigth.h"

Я знаю, как это сделать в Perl/Python/Ruby, но я хотел бы сделать это без использования другого языка программирования.

Ответы

Ответ 1

Если вам удобно использовать временный файл, попробуйте следующее:

grep include file1.h > /tmp/x && grep -f /tmp/x -v file2.h | grep include

Это

  • извлекает все из file1.h и записывает их в файл /tmp/x
  • использует этот файл для получения всех строк из file2.h, которые не содержатся в этом списке
  • извлекает все из оставшейся части file2.h

Он, вероятно, неправильно обрабатывает различия в пробелах и т.д.

EDIT: для предотвращения ложных срабатываний используйте другой шаблон для последнего grep (спасибо jw013 за это):

grep include file1.h > /tmp/x && grep -f /tmp/x -v file2.h | grep "^#include"

Ответ 2

Это однострочный, но не сохраняет порядок:

comm -13 <(grep '#include' file1 | sort) <(grep '#include' file2 | sort)

Если вам нужно сохранить заказ:

awk '
  !/#include/ {next} 
  FILENAME == ARGV[1] {include[$2]=1; next} 
  !($2 in include)
' file1 file2

Ответ 3

Этот вариант требует fgrep с опцией -f. GNU grep (то есть любая система Linux, а затем и некоторые) должны работать нормально.

# Find occurrences of '#include' in file1.h
fgrep '#include' file1.h |
# Remove any identical lines from file2.h
fgrep -vxf - file2.h |
# Result is all lines not present in file1.h.  Out of those, extract #includes
fgrep '#include'

Это не требует сортировки или каких-либо явных временных файлов. Теоретически fgrep -f может использовать временный файл за кулисами, но я считаю, что GNU fgrep не делает.

Ответ 4

Если цель не должна выполняться только с Bash (т.е. использование внешних программ приемлемо), используйте combine из moreutils:

combine file1 not file2 > lines_in_file1_not_in_file2

Ответ 5

cat $file1 $file2 | grep '#include' | сортировать | uniq -u