Выберите строки из текстового файла с идентификаторами, указанными в другом файле

Я использую много сортировки grep awk в своей оболочке unix для работы с текстовыми файлами с разделителями разделов с разделителями разделов с разделителями разделов с разделителями-разделителями (размером около 10 М-100 М). В этом отношении оболочка unix является моей таблицей.

Но у меня есть одна огромная проблема, которая выбирает записи, учитывая список идентификаторов.

Имея файл table.csv с id\tfoo\tbar... формата id\tfoo\tbar... и ids.csv со списком идентификаторов, выберите только записи из table.csv с идентификатором, присутствующим в ids.csv .

вид https://stackoverflow.com/questions/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids, но с оболочкой, а не perl.

grep -F очевидно, создает ложные срабатывания, если ids – переменная ширина. join – это утилита, которую я никогда не мог понять. Прежде всего, для этого требуется сортировка по алфавиту (мои файлы обычно сортируются численно), но даже тогда я не могу заставить его работать, не жалуясь на неправильный порядок и пропуская некоторые записи. Поэтому мне это не нравится. grep -f против файла с ^id\t -s очень медленный, когда число идентификаторов велико. awk громоздкий.

Есть ли хорошие решения для этого? Какие-либо конкретные инструменты для разделенных табуляцией файлов? Также будут приветствоваться дополнительные функции.

UPD: Исправлена sort -> join

  • awk попарные разности
  • Как поменять столбцы в таком файле?
  • Обертывание длинных ячеек в ЦС, чтобы держать их в одной колонке
  • Удалить столбец из CSV
  • Экспорт данных netstat из терминала в файл csv
  • два файла .csv сравниваются с помощью awk
  • linux при чтении для переключения столбцов, если столбец 3 меньше столбца 2
  • конвертировать xml в csv
  • 4 Solutions collect form web for “Выберите строки из текстового файла с идентификаторами, указанными в другом файле”

    Я предполагаю, что вы имели в виду grep -f не grep -F но вам действительно нужна комбинация обоих и -w :

     grep -Fwf table.csv ids.csv 

    Причина, по которой вы получали ложные срабатывания, (я думаю, вы не объяснили), потому что если идентификатор может содержаться в другом, то оба будут напечатаны. -w устраняет эту проблему, и -F гарантирует, что ваши шаблоны рассматриваются как строки, а не регулярные выражения. От man grep :

      -F, --fixed-strings Interpret PATTERN as a list of fixed strings, separated by newlines, any of which is to be matched. (-F is specified by POSIX.) -w, --word-regexp Select only those lines containing matches that form whole words. The test is that the matching substring must either be at the beginning of the line, or preceded by a non-word constituent character. Similarly, it must be either at the end of the line or followed by a non-word constituent character. Word-constituent characters are letters, digits, and the underscore. -f FILE, --file=FILE Obtain patterns from FILE, one per line. The empty file contains zero patterns, and therefore matches nothing. (-f is specified by POSIX.) 

    Если ваши ложные срабатывания связаны с тем, что идентификатор может присутствовать в поле, отличном от идентификатора, вместо этого прокрутите свой файл:

     while read pat; do grep -w "^$pat" table.csv; done < ids.csv 

    или, быстрее:

     xargs -I {} grep "^{}" table.csv < ids.csv 

    Лично я сделал бы это в perl хотя:

     perl -lane 'BEGIN{open(A,"ids.csv"); while(<A>){chomp; $k{$_}++}} print $_ if defined($k{$F[0]}); ' table.csv в perl -lane 'BEGIN{open(A,"ids.csv"); while(<A>){chomp; $k{$_}++}} print $_ if defined($k{$F[0]}); ' table.csv 

    Утилита join – это то, что вы хотите. Это требует, чтобы входные файлы были лексически отсортированы.

    Предполагая, что ваша оболочка bash или ksh:

     join -t $'\t' <(sort ids.csv) <(sort table.csv) 

    Обычное awk-решение не требует сортировки.

     awk -F '\t' 'NR==FNR {id[$1]; next} $1 in id' ids.csv table.csv 

    Ответы на этот вопрос SO помогли мне обойти ничтожество с присоединением. По сути, когда вы сортируете файл, готовый отправить его, чтобы присоединиться, вам нужно убедиться, что вы сортируете на основе столбца, к которому вы присоединяетесь. Итак, если это первый, вам нужно сказать ему, что символ разделителя в файле, и что вы хотите, чтобы он сортировался в первом поле (и только в первом поле). В противном случае, если первое поле имеет переменную ширину (например), ваши разделители и, возможно, другие поля могут начать влиять на порядок сортировки.

    Итак, используйте опцию -t для сортировки, чтобы указать разделительный символ, и используйте параметр -k, чтобы указать поле (помня, что вам нужно поле начала и окончания – даже если это то же самое – или он будет сортировать из этого символа до конца строки).

    Итак, для файла с разделителями табуляции, как в этом вопросе, должно работать следующее (с благодарностью за ответ Гленна за структуру):

    join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv

    (Для справки, флаг -d означает сортировку слова. Вы также можете использовать флаг -b, чтобы игнорировать ведущие пробелы, см. man sort and man join ).

    В качестве более общего примера предположим, что вы соединяете два разделенных запятыми файлов – input1.csv на третьем столбце и input2.csv на четвертом. Вы можете использовать

    join -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv

    Здесь параметры -1 и -2 определяют, какие поля должны объединяться в первом и втором входных файлах соответственно.

    Вы также можете использовать ruby, чтобы сделать что-то подобное:

     ruby -pe 'File.open("id.csv").each { |i| puts i if i =~ /\$\_/ }' table.csv 
    Linux и Unix - лучшая ОС в мире.