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"
}