bash: сортировка по числовому расстоянию - PullRequest
0 голосов
/ 15 апреля 2020

У меня есть файл, содержащий список людей с их полом и возрастом, подобный этому:

name1    M    73.2
name2    M    31.5
name3    F    20.3
name4    F    55.0
...

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

name2    M    31.5
name3    F    20.3
name4    F    55.0
name1    M    73.2

Ответы [ 4 ]

2 голосов
/ 15 апреля 2020

Аналогичным образом, если необходимо сохранить формат строки в оригинале, вместо печати первых трех полей вы можете использовать переменную и усечь после третьего поля результаты из sort, например

awk 'function abs(v) { return v < 0 ? -v : v }
    { print $0"\t"abs($NF-30) }' file | 
sort -k4n |
awk '{ out=$0; print substr(out, 0, match (out,$3)+length($3)) }'

Пример использования / Вывод

С вашим примером файла в файле с именем file вы получите:

$ awk 'function abs(v) { return v < 0 ? -v : v }
>     { print $0"\t"abs($NF-30) }' file |
> sort -k4n |
> awk '{ out=$0; print substr(out, 0, match (out,$3)+length($3)) }'
name2    M    31.5
name3    F    20.3
name4    F    55.0
name1    M    73.2

( примечание: вы можете просто выбрать-скопировать оригинальное выражение awk, а затем в xterm с file в текущем рабочем каталоге, вставить тест средней кнопкой мыши)

1 голос
/ 15 апреля 2020

Любая версия Awk

awk -v ref=30.0 '{ print $1, $2, $3, ($3 < ref) ? ref - $3 : $3 - ref }' |
sort -k4,4n |
awk '{ print $1, $2, $3 }'

Добавьте расстояние от контрольного возраста в качестве дополнительного столбца, отсортируйте его, удалите. Вы можете использовать cut для операции удаления, если хотите. Если вы используете GNU Awk, вы можете сделать все это в awk. Есть способы сохранить интервал, если это важно для вас.

Вы можете написать все это в одну строку, если вы настаиваете; Это ваш выбор.

Многофункциональное устройство с использованием GNU Awk

Проверка руководства GNU Awk показывает, что встроенной функции abs() нет, что немного удивительно GNU Awk имеет функции asort() и asorti(), которые можно использовать для внутренней сортировки данных, что позволяет коду использовать один вызов awk и никаких вызовов sort команда. Это также сохраняет интервал в исходных данных.

Этот вариант использует идею «квадрата расстояния», предложенную zhihao_li в их ответе .

gawk -v ref=48.0 '
function comp_idx(i1, v1, i2, v2) {
    if (i1+0 < i2+0) return -1; else if (i1+0 > i2+0) return +1; else return 0;
}
    { data[($3-ref)^2] = $0 }
END { 
      n = asorti(data, results, "comp_idx")
      for (i = 1; i <= n; i++) print data[results[i]]
    }' "$@"

Операции +0 в функции comp_idx необходимы, чтобы awk обрабатывал значения индекса как числа, а не как строки. Без них порядок сортировки основывался на лексикографическом (не числовом c) порядке квадратов расстояний. Если важна одна строка, вы можете написать это все в одной строке, но вам также понадобится добавить точку с запятой. Я не рекомендую это.

Вы могли бы преобразовать код в более полный сценарий оболочки, который принимает значение age в качестве аргумента, передаваемого Awk (механизм -v ref=30.0). Это сложнее, чем сложнее. В его нынешнем виде он обрабатывает только те файлы, которые ему даны, или стандартный ввод, если файлы не передаются.

С примерами данных для эталонного возраста 48.0:

name4    F    55.0
name2    M    31.5
name1    M    73.2
name3    F    20.3

Измените базовый возраст с 48,0 до 30,0, как в вопросе, и в результате получите:

name2    M    31.5
name3    F    20.3
name4    F    55.0
name1    M    73.2
0 голосов
/ 15 апреля 2020

Другой подход, использующий perl вместо awk:

$ age=30 perl -anE 'push @lines, [@F, abs($ENV{age} - $F[2])];
   END { say join("\t", $_->@[0..2]) for sort { $a->[3] <=> $b->[3] } @lines }' input.txt 
name2   M   31.5
name3   F   20.3
name4   F   55.0
name1   M   73.2
0 голосов
/ 15 апреля 2020

Обсуждение о добавлении еще одного столбца было полезным. Я придумал это решение с $ {ag}, обеспечивающим данный возраст. Квадратная операция проще, чем проверка на отпущение грехов.

awk -v a=${ag} '{print $1,$2,$3,($3-a)^2}' | sort -n -k 4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...