Я использую много сортировки grep awk в моей оболочке Unix для работы с текстовыми файлами столбцов, разделенных табуляцией среднего размера (около 10–100 млн строк). В этом отношении Unix Shell - моя электронная таблица.
Но у меня есть одна огромная проблема - выбор записей по списку идентификаторов.
Имея table.csv
файл с форматом id\tfoo\tbar...
и ids.csv
файл со списком идентификаторов, выберите только записи table.csv
с идентификатором, присутствующим в ids.csv
.
вид /programming/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids, но с оболочкой, а не perl.
grep -F
очевидно, дает ложные срабатывания, если идентификаторы имеют переменную ширину.
join
это утилита, которую я никогда не мог понять. Прежде всего, это требует алфавитной сортировки (мои файлы обычно сортируются по номерам), но даже тогда я не могу заставить его работать, не жалуясь на неправильный порядок и пропуская некоторые записи. Так что мне это не нравится. grep -f для файла с ^id\t
-s очень медленный, когда количество идентификаторов велико.
awk
это громоздко.
Есть ли хорошие решения для этого? Какие-нибудь специальные инструменты для файлов, разделенных табуляцией? Дополнительная функциональность также будет приветствоваться.
UPD: исправлено sort
->join
grep -f
это слишком медленно, поддержание этой стратегии кажется большим количеством проблем, чем оно того стоит - вариации, вероятно, станут жертвами тех же проблем производительности O (N * M). Может быть, ваше время было бы лучше потратить на изучение того, как использовать нормализованную базу данных SQL ...awk
.sort
можно делать все виды сортировки, числовые, алфавитные и другие. Смman sort
.Ответы:
Я думаю, вы
grep -f
не имели в виду,grep -F
но на самом деле вам нужно сочетание обоих и-w
:Причина, по которой вы получили ложные срабатывания (я думаю, вы не объяснили), потому что если идентификатор может содержаться в другом, то оба будут напечатаны.
-w
устраняет эту проблему и-F
гарантирует, что ваши шаблоны обрабатываются как строки, а не как регулярные выражения. Отman grep
:Если вы получили ложные срабатывания из-за того, что идентификатор может присутствовать в поле, не являющемся идентификатором, вместо этого переберите файл:
или быстрее:
Лично я бы сделал это,
perl
хотя:источник
^
с -F, вы не можете нацелиться на первый столбец.^id\t
Бит из OP означает ,id
может произойти в другой колонке. Если нет, это не имеет значения.join
Утилита, что вы хотите. Требуется лексическая сортировка входных файлов.Предполагая, что ваша оболочка bash или ksh:
Без необходимости сортировки, обычное решение awk
источник
join
это не клудж: твои слова были, ты не мог понять это. Открой свой разум и учись. Какой результат вы получили, и как это отличается от того, что вы ожидаете?join
.awk
Решение здесь очень быстро и эффективно для моих целей (я извлечение подмножеств нескольких сот из файлов с ~ 100M линий)Ответы на этот ТАКОЙ вопрос помогли мне обойти ниггеры с помощью join. По сути, когда вы сортируете файл в процессе подготовки к отправке для присоединения, вам нужно убедиться, что вы сортируете по столбцу, к которому вы присоединяетесь. Так что, если это первый, вам нужно указать ему, какой символ разделителя находится в файле и что вы хотите, чтобы он сортировался в первом поле (и только в первом поле). В противном случае, если первое поле имеет переменную ширину (например), ваши разделители и, возможно, другие поля могут начать влиять на порядок сортировки.
Итак, используйте опцию -t sort, чтобы указать разделяющий символ, и опцию -k, чтобы указать поле (помня, что вам нужно поле начала и конца - даже если оно одинаковое - или оно будет отсортировано по этому символу до конца строки).
Поэтому для файла, разделенного табуляцией, как в этом вопросе, должно работать следующее (благодаря ответу Гленна за структуру):
join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv
(Для справки, флаг -d означает сортировку по словарю. Вы также можете использовать флаг -b, чтобы игнорировать начальные пробелы, смотрите
man sort
и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, чтобы сделать нечто подобное:
источник