Мне нужно извлечь все строки из файла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.