Назад | Перейти на главную страницу

Извлечение строк из файла по префиксу

Мне нужно извлечь все строки из файла2, которые начинаются с префикса id #, содержащегося в файле1.

Файл 1 представляет собой один столбец, например:

324
399
408
135236
321590

Файл 2 многоколоночный, например:

1 [tab] 108 [tab] Anarchist [tab] 103985
...
324 [tab] 309 [tab] Melodies [tab] 230498

Какой самый быстрый и простой способ извлечь только эти строки из File2?

bash код для этого:

for i in $(cat file1); do egrep "^$i\s" file2; done
$ while read p; do awk '$1 == "'$p'"' file2; done < file1

или:

$ awk -F'\t' 'FNR==NR { a[$0]; next } $1 in a' file1 file2
  • FNR: количество записей, прочитанных из текущего обрабатываемого файла
  • NR: общее количество входных записей
  • FNR==NR: верно только тогда, когда awk читает file1
  • a[$0]: создать элемент массива, проиндексированный $0 (из file1)
  • $1 in a: проверить, читается ли каждая строка из file2 существует как индекс в массиве a

Это, наверное, самый быстрый:

grep -f <( sed 's/.*/^&\t/' file1) file2

Ответы с использованием for и while петли будут очень медленными.

В awk ответ квантами должен работать. Я не знаю, почему это не так, если ваши окончания строк не относятся к Unix или file1 очень большой.

1) Мы можем использовать логику ИЛИ grep. Например

$> grep -P "^(324|399|408|135236|321590).*" file2
324 [tab] 309 [tab] Melodies [tab] 230498

Итак, вопрос в том, как мы можем передать эту переменную в grep?

2) Мы можем вывести файл file1 в одну строку и заменить разделители на |, чем добавить скобки.

$> echo `cat file1` | sed -r -e 's/([0-9])\ ([0-9])/\1,\2/g'
324,399,408,135236,321590

Итак, наконец, у нас есть вариант без циклов for-while.

grep -P "^($( echo `cat file1` | sed -r -e 's/([0-9])\ ([0-9])/\1|\2/g'  )).*" file2

В join Команда GNU coreutils server предназначена именно для этого, но она требовательна к вводу.

$ sort file1 > sorted1
$ sort file2 > sorted2
$ join -t"      " sorted1 sorted2 | sort -n

В join команда требует, чтобы ее входные файлы были отсортированы лексикографически, а не численно. Таким образом, вся эта сортировка входов и выходов.

Чтобы указать, что вывод из join должны быть разделены табуляцией, используйте -t"символ табуляции", который вы должны ввести как Ctrl-V Tab в командной строке Bash.