10.11.2016

Як використовувати AWK

Original: http://sparky.rice.edu/~hartigan/awk.html

AWK

Awk є потужною мовою команд, яка дозволяє користувачеві управляти файлами, що містять стовпці даних і рядки. Awk є надзвичайно корисним, як для спільної роботи Unix команд, а також для обробки даних (наприклад, IRAF). Ви також можете дізнатися, як використовувати редактор потоку sed. Багато додатків awk нагадують зроблено на електронних таблиць для ПК.

Цей файл містить ряд прикладів того, як використовувати AWK. Я склав цю таблицю поступово протягом декількох років, як я навчився робити нові речі. Кожен, хто скорочує дані з IRAF повинні вивчити основи AWK навчання, щоб зробити навіть прості речі, заощадить вам багато часу в довгостроковій перспективі. Це займе у вас менше години, щоб прочитати цей файл і вивчити основи.

Є два способи запустити awk. Проста команда awk можна запустити з однієї командного рядка. Інші сценарії складні awk повинні бути записані в командний файл. Я представляю приклади обох типів введення нижче.

Awk бере кожен рядок введення і намагається відповідати “шаблону” (див. нижче), і якщо йому це вдасться, він буде робити все, що ви скажете в межах {} (називається дією). Awk найкраще працює з файлами, які мають стовпчики цифр або рядків, розділених пробілами (табуляції або пропусків), хоча на більшості машин ви можете використовувати опцію -F, якщо ваші стовпчики відділив іншим персонажем. Awk звертається до першого стовпця як $1, до другого – як $2 тощо, а до цілого рядка – як $0. Якщо у вас є файл (наприклад, каталог), який завжди має номера в певних шпальтах, ви також можете виконати команду “colrm” та об’єднати її з awk. Існує довідкова сторінка з colrm. Існує також дуже неповний довідкова сторінка man про awk.

Я буду вести вас через два приклади. По-перше, припустимо, що у вас є файл з ім’ям “file1”, який має 2 стовпці цифр, і ви хочете зробити новий файл з ім’ям “file2”, який має стовпці 1 та 2, як раніше, і додає третій стовпець, який являє собою відношення числа в колонках 1 і 2. Припустимо, ви хочете новий файл із 3 стовпцями (file2), щоб утримувати тільки ті рядки, з колонки 1 менше колонці 2. Кожна з наступних двох команд робить те, що ви хочете:

awk ‘$1 < $2 {print $0, $1/$2}’ file1 > file2

— або —

cat file1 | awk ‘$1 < $2 {print $0, $1/$2}’ > file2

Розглянемо другий приклад. Усім відомо, що “cat file1” друкує зміст файлу1 на екрані. | (символ “вертикальна лінія”) спрямовує відображення “cat file1”, яке зазвичай іде на екран, до команди awk. Awk розглядає вхідний сигнал від “cat file1” по одному рядку за раз, і намагається наблизитися до “шаблона”. Шаблон – це те, що між першою ‘ і {, у цьому випадку шаблоном є $1 < $2. Якщо шаблон є хибним, awk переходить до наступного рядка. Якщо зразок істинно, awk робить те, що в {}. У цьому випадку ми awk попросили перевірити, якщо перший стовпець менше, ніж другий. Якщо немає шаблон, awk передбачає, що зразок істинно, і переходить до дії, що міститься в {}.

Що таке дія? У більшості випадків це оператор друку якийсь. В цьому випадку ми хочемо, щоб awk друкувати весь рядок, тобто $0, а потім надрукував показники стовпців 1 і 2, тобто $1/$2. Ми закриваємо дію } і закриваємо команду awk символом ‘. Зрештою, щоб зберегти остаточний результат 3 стовпців у file2 (інакше його буде надруковано на екрані), додаємо ‘> file2’.

Як другий приклад, припустимо, що у вас є кілька тисяч файлів, які ви хочете перемістити в новий каталог і перейменувати використовуючи .dat із назвами файлів. Ви можете зробити це одне за іншим (кілька годин), або використати vi, щоб це зробив відповідний командний файл (кілька хвилин), або використати awk (кілька секунд). Скажімо, імена файлів: junk* (* – підстановка для будь-якої послідовності символів), їх потрібно перемістити до ../iraf і до назви додати ‘.dat’. Для цього введіть

ls junk* | awk ‘{print “mv “$0″ ../iraf/”$0″.dat”}’ | csh

ls junk* перераховує імена файлів, і цей вихід передається в awk замість того, щоб іти на екран. Нмає ніякої закономірності (нічого між ‘ і {), тому awk продовжує друкувати щось для кожного рядка. Так, якщо перші два рядки з ‘ls junk*’ надали junk1 та junk2, відповідно, awk надрукує:

mv junk1 ../iraf/junk1.dat
mv junk2 ../iraf/junk2.dat

На цьому етапі команди mv просто виводяться на екран. Для виконання команди ми беремо висновок awk і труби його назад в операційну систему (С-оболонки). Отже, щоб закінчити заяву ми додамо ‘ | csh’.

Інші сценарії складні awk повинні бути запущені з файлу. Синтаксис для таких випадків:

cat file1 | awk -f a.awk > file2

де file1 – це файл вводу, file2 – файл виводу, а .awk –файл із командами awk. Наведені нижче приклади, які містять більше одного рядка awk повинні бути запущені з файлів.

Деякі корисні змінні awk, визначені для вас: NF (кількість стовпців), NR (поточний рядок, який обробляє awk), END (істинний, якщо awk досягає EOF), BEGIN (істинний перед прочитанням awk будь чого), and length (кількість символів у рядку). Існує також можливість зациклення, команда search (/), substring (надзвичайно корисна), і доступний форматований друк. Є логічні змінні || (або) та && (і), які можуть вживатися в ‘pattern’. Ви можете визначити і маніпулювати ваші власні змінні. Приклади наведені нижче. Єдина помилка, я не знаю, що версія Sun по awk не робитиме тригонометричні функції, хоча це робити журнали. Там дещо назване gawk (продукт Gnu), який робить дещо більше речей, ніж awk від Sun, однак, в основному, те саме. Зверніть увагу на вживання команди ‘yes’ нижче. Разом із ‘head’ та ‘awk’ ви економите годину друку, якщо у вас є багато файлів для аналізу або перейменування.

Успіхів!

ПРИКЛАДИ      # символ коментаря для awk.  'field' означає 'column'

# Надрукувати перші 2 поля у зворотному порядку:
  awk '{ print $2, $1 }' file


# Надрукувати рядки, довші за 72 символи:
  awk 'length > 72' file
    

# Надрукувати довжину рядка у другому стовпці
  awk '{print length($2)}' file


# Додати вгорі перший стовпець, надрукувати суму та середній показник:
       { s += $1 }
  END  { print "sum is", s, " average is", s/NR }


# Надрукувати поля у зворотному порядку:
  awk '{ for (i = NF; i > 0; --i) print $i }' file


# Надрукувати останній рядок
      {line = $0}
  END {print line}


# Надрукувати загальну кількість рядків зі словом Pat
  /Pat/ {nlines = nlines + 1}
  END {print nlines}


# Надрукувати всі рядки між парами start/stop:
  awk '/start/, /stop/' file


# Надрукувати всі рядки, перше поле яких відрізняється від попереднього:
  awk '$1 != prev { print; prev = $1 }' file


# Надрукувати стовпець 3, якщо column 1 > column 2:
  awk '$1 > $2 {print $3}' file
     

# Надрукувати рядок, якщо column 3 > column 2:
  awk '$3 > $2' file


# Порахувати кількість рядків, де col 3 > col 1
  awk '$3 > $1 {print i + "1"; i++}' file


# Надрукувати порядковий номер, потім column 1 файлу:
  awk '{print NR, $1}' file


# Надрукувати кожен рядок після стирання другого поля
  awk '{$2 = ""; print}' file


# Надрукувати hi 28 разів
  yes | head -28 | awk '{ print "hi" }'


# Надрукувати hi.0010 до hi.0099 (ОБЕРЕЖНО, КОРИСТУВАЧІ IRAF!)
  yes | head -90 | awk '{printf("hi00%2.0f \n", NR+9)}'

# Надрукувати 4 довільні числа в межах 0 та 1
yes | head -4 | awk '{print rand()}'

# Надрукувати 40 довільних цілих чисел за модулем 5
yes | head -40 | awk '{print int(100*rand()) % 5}'


# Замінити кожне поле на його абсолютне значення
  { for (i = 1; i <= NF; i=i+1) if ($i < 0) $i = -$i print}

# За наявності іншого символа, що обмежує поля, використайте параметр -F
# Так, щоб надрукувати номер телефону для Джоунз у наступному файлі,
# 000902|Beavis|Theodore|333-242-2222|149092
# 000901|Jones|Bill|532-382-0342|234023
# ...
# введіть
  awk -F"|" '$2=="Jones"{print $4}' filename



# Деякі команди зациклення
# Видалити купу завдань друку з черги
  BEGIN{
	for (i=875;i>833;i--){
		printf "lprm -Plw %d\n", i
	} exit
       }


 Відформатовані роздруківки мають вигляд printf( "format\n", value1, value2, ... valueN)
		e.g. printf("howdy %-8s What it is bro. %.2f\n", $1, $2*$3)
	%s = string
	%-8s = 8 character string left justified
 	%.2f = number with 2 places after .
	%6.2f = field 6 chars with 2 chars after .
	\n is newline
	\t is a tab


# Частота друку гістограма стовпчика чисел
$2 <= 0.1 {na=na+1}
($2 > 0.1) && ($2 <= 0.2) {nb = nb+1}
($2 > 0.2) && ($2 <= 0.3) {nc = nc+1}
($2 > 0.3) && ($2 <= 0.4) {nd = nd+1}
($2 > 0.4) && ($2 <= 0.5) {ne = ne+1}
($2 > 0.5) && ($2 <= 0.6) {nf = nf+1}
($2 > 0.6) && ($2 <= 0.7) {ng = ng+1}
($2 > 0.7) && ($2 <= 0.8) {nh = nh+1}
($2 > 0.8) && ($2 <= 0.9) {ni = ni+1}
($2 > 0.9) {nj = nj+1}
END {print na, nb, nc, nd, ne, nf, ng, nh, ni, nj, NR}


# Знайти максимальне і мінімальне значення, присутні в стовпці 1
NR == 1 {m=$1 ; p=$1}
$1 >= m {m = $1}
$1 <= p {p = $1}
END { print "Max = " m, "   Min = " p }

# Приклад визначення змінних, кілька команд в одному рядку
NR == 1 {prev=$4; preva = $1; prevb = $2; n=0; sum=0}
$4 != prev {print preva, prevb, prev, sum/n; n=0; sum=0; prev = $4; preva = $1; prevb = $2}
$4 == prev {n++; sum=sum+$5/$6}
END {print preva, prevb, prev, sum/n}

# Приклад визначення і за допомогою функції, вставки значень в масив
# і цілочисельних арифметичних розрахунків mod(n). Цей скрипт визначає кількість днів
# з 1 січ 1901 р. (from http://www.netlib.org/research/awkbookcode/ch3)
function daynum(y, m, d,    days, i, n)
{   # 1 == Jan 1, 1901
    split("31 28 31 30 31 30 31 31 30 31 30 31", days)
    # 365 days a year, plus one for each leap year
    n = (y-1901) * 365 + int((y-1901)/4)
    if (y % 4 == 0) # leap year from 1901 to 2099
        days[2]++
    for (i = 1; i < m; i++)
        n += days[i]
    return n + d
}
    { print daynum($1, $2, $3) }

# Приклад використання підрядків
# substr($2,9,7) вибирає символи від 9 до 15 у стовпці 2
{print "imarith", substr($2,1,7) " - " $3, "out."substr($2,5,3)}
{print "imarith", substr($2,9,7) " - " $3, "out."substr($2,13,3)}
{print "imarith", substr($2,17,7) " - " $3, "out."substr($2,21,3)}
{print "imarith", substr($2,25,7) " - " $3, "out."substr($2,29,3)}


Назад до  Головної сторінки Гертігена
Патрік Гертіген  
 hartigan@sparky.rice.edu

About The Author

admin

Comments are closed.