GNU sort стабильная сортировка, когда sort не знает порядок сортировки

18

У меня есть файл с двумя столбцами; файл отсортирован так, как я хочу, уже в столбце 1. Я хотел бы отсортировать по столбцу 2, в каждой категории столбца 1. Тем sortне менее, не понимает порядок сортировки столбца 1.

Нормальный способ (из подобных вопросов здесь в стеке) будет такой:

sort --stable -k1,1 -k2,2n

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

Пример ввода:

C 2
C 1
A 2
A 1
B 2 
B 1

и вывод:

C 1
C 2
A 1
A 2
B 1 
B 2
Эван Бенн
источник

Ответы:

20

Вы можете использовать awk, чтобы начать новую сортировку для каждого блока:

% awk -v cmd="sort -k2,2" '$1 != prev {close(cmd); prev=$1} {print | cmd}' foo
C 1
C 2
A 1
A 2
B 1
B 2
  • $1 != prev {close(cmd); prev=$1} - когда сохраненное значение отличается, у нас есть новый блок, поэтому мы закрываем все ранее запущенные sort
  • {print | "sort -k2,2"}'направляет вывод sort, запуская его, если он еще не запущен (awk может отслеживать команды, которые он запускает)
Мур
источник
2
awk действительно невероятно. Мне это нравится намного больше, чем я ожидал, это был awk decorate-sort-undecorate!
Эван Бенн
Я пытался сравнить этот ответ с другим ответом, не уверен, почему этот использует больше ресурсов ... Есть идеи? gist.github.com/EvanTheB/5b64eafb84eeaf51c289295ac06e1b0b
Эван Бенн
Сколько раз вы пробежали в среднем?
Муру
Я не усредняю, но вижу последовательные времена выполнения, когда я повторяю и исследую.
Эван Бенн
Вот файл, аналогичный тому, который я использую, если вы хотите исследовать:seq 30 | xargs -L1 bash -cs 'yes $1 | head -1000000 | paste - <(seq 1000000) | shuf' bash
Эван Бенн
12

Вы могли бы использовать преобразование Шварца (это в основном подход decorate-sort-undecorate, на который вы ссылались в комментарии, но, вероятно, более производительный, чем хороший ответ muru из-за использования одного sortвызова в отличие от нескольких) - с помощью awkдобавления столбца префикса, который увеличивается с изменением значения в первом столбце, сортируется по столбцу префикса, за которым следует «второй» столбец (порядковый номер которого временно смещен 3из-за наличия столбца префикса), и, наконец, избавляется от столбца префикса

awk '{print ($1 in a? c+0: ++c)"\t" $0; a[$1]}' file | sort -k1,1n  -k3,3 | cut -f 2-
Iruvar
источник
Я удивлен, но вы правы, это было быстрее, чем другой ответ! 3 минуты против 2 минут в моем файле с 100 миллионами строк (~ 30 уникальных первых столбцов).
Эван Бенн
1
Не нужно хранить массив уникального ключа из первого столбца. Я думаю, этого должно быть достаточно, чтобы сравнить первый столбец текущей строки с предыдущим.
Кусалананда
Что-то вроде awk -v OFS="\t" '$1 != prev { key++ } { print key, $0; prev = $1 }(не проверено).
Кусалананда