awk: найти минимум и максимум в столбце

Я использую awk иметь дело с простым .файл dat, который содержит несколько строк данных и каждая строка имеет 4 столбца, разделенных одним пробелом. Я хочу найти минимум и максимум первой колонки.

файл данных выглядит так:

9 30 8.58939 167.759
9 38 1.3709 164.318
10 30 6.69505 169.529
10 31 7.05698 169.425
11 30 6.03872 169.095
11 31 5.5398 167.902
12 30 3.66257 168.689
12 31 9.6747 167.049
4 30 10.7602 169.611
4 31 8.25869 169.637
5 30 7.08504 170.212
5 31 11.5508 168.409
6 31 5.57599 168.903
6 32 6.37579 168.283
7 30 11.8416 168.538
7 31 -2.70843 167.116
8 30 47.1137 126.085
8 31 4.73017 169.496

команды, которые я использовал следующим образом.

min=`awk 'BEGIN{a=1000}{if (<a) a= fi} END{print a}' mydata.dat`
max=`awk 'BEGIN{a=   0}{if (>a) a= fi} END{print a}' mydata.dat`

тем не менее, выход мин=10 и max=9.

(аналогичные команды могут возвращать мне правильный минимум и максимум второго столбца.)

может ли кто-нибудь сказать мне, где я ошибался? Спасибо!

3 ответов


Awk угадывает тип.

строка " 10 "меньше строки" 4", потому что символ" 1 "предшествует"4". Принудительно введите разговор, используя сложение нуля:

min=`awk 'BEGIN{a=1000}{if (<0+a) a=} END{print a}' mydata.dat`
max=`awk 'BEGIN{a=   0}{if (>0+a) a=} END{print a}' mydata.dat`

ваша проблема заключалась в том, что в вашем скрипте у вас было:

if (<a) a= fi

и заключительный fi не является частью синтаксиса awk, поэтому он рассматривается как переменная so a= fi является конкатенацией строк, и поэтому вы говорите awk, что a содержит строку, а не число и, следовательно, сравнение строк вместо числовых в <a.

что еще более важно в целом, никогда не начинайте с некоторого предполагаемого значения для max / min, просто используйте первое значение, прочитанное как семя. Вот правильный способ написания скрипта:

$ cat tst.awk
BEGIN { min = max = "NaN" }
{
    min = (NR==1 || <min ?  : min)
    max = (NR==1 || >max ?  : max)
}
END { print min, max }

$ awk -f tst.awk file
4 12

$ awk -f tst.awk /dev/null
NaN NaN

$ a=( $( awk -f tst.awk file ) )
$ echo "${a[0]}"
4
$ echo "${a[1]}"
12

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


не-awk ответ:

cut -d" " -f1 file |
sort -n |
tee >(echo "min=$(head -1)") \
  > >(echo "max=$(tail -1)")

эта команда tee -немного слишком умны. tee дублирует поток stdin в имена файлов в качестве аргументов, а также передает те же данные в stdout. Я использую подстановки процессов для фильтрации потоков.

тот же эффект можно использовать (с меньшим размахом) для извлечения первой и последней строк потока данных:

cut -d" " -f1 file | sort -n | sed -n '1s/^/min=/p; $s/^/max=/p'

или

cut -d" " -f1 file | sort -n | { 
    read line
    echo "min=$line"
    while read line; do max=$line; done
    echo "max=$max"
}