summaryrefslogtreecommitdiffhomepage
path: root/uk-ua/awk-uk.html.markdown
blob: a99dca174e8dcf5f5bfb0162d0ab61fc22f0452b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
---
category: tool
tool: awk
filename: learnawk-ua.awk
contributors:
     - ["Marshall Mason", "http://github.com/marshallmason"]
translators:
     - ["Ihor Sokorchuk", "https://github.com/IhorSokorchuk"]
lang: uk-ua
---

AWK є стандартним інструментом у кожній POSIX-сумісній системі UNIX.
Він схожий на flex/lex із командного рядка й чудово підходить для завдань
обробки тексту та інших скриптових задач. Він має подібний до C синтаксис,
але без обов’язкових крапок з комою (хоча їх все одно потрібно використовувати,
якщо ви пишете однорядковий код, для якого AWK відмінно підходить),
у ньому немає ручного керування пам’яттю та статичної типізації.
Він відмінно обробляє текст. Ви можете викликати його зі сценарію оболонки або
використовувати як окрему мову сценаріїв.

Навіщо використовувати AWK замість Perl? Це читабельність коду.
Адже AWK легше читати, ніж Perl. Для простих сценаріїв обробки тексту,
особливо тих, які читають файли рядок за рядком і розбивають їх
за роздільниками, AWK є чи не найкращим інструментом.

```awk
#!/usr/bin/awk -f

# Коментарі у AWK схожі на цей коментар.

# Програми AWK складаються з набору шаблонів і дій.
pattern1 { action; } # зовсім як lex
pattern2 { action; }

# Скрипт виконує неявний цикл, у якому AWK автоматично читає та аналізує кожен
# запис із кожного наданого йому файла. Кожен такий запис розділяється на поля
# роздільником FS, який за замовчуванням має значення пробіл (кілька пробілів
# або символів табуляції вважаються одним роздільником).
# Можна встановити значення FS в командному рядку (-FC) або у шаблоні BEGIN.

# Одним із спеціальних шаблонів є шаблон BEGIN. Цей шаблон є дійсним
# ДО прочитання будь-якого з вхідних файлів.
# Ще один спеціальний шаблон END є дійсним після кінця останнього з вказаних
# вхідних файлів, або після стандартного введення, якщо вхідні файли не вказано.
# Також використовується роздільник між полями виводу (OFS), значення якого
# ви також можете призначити, і для якого за замовчуванням встановлено
# значення один пробіл.


BEGIN {

    # BEGIN запускатиметься на початку програми. Тут розміщують весь код
    # попереднього налаштування перед обробкою будь-яких текстових файлів.
    # Якщо ваш скрипт не читає текстові файли, розглядайте BEGIN як
    # головну точку входу.

    # Змінні є глобальними. Просто встановіть їх і використовуйте.
    # Оголошувати змінні не потрібно.
    count = 0;

    # Оператори є такими ж як у мові C та схожих на неї мовах програмування.
    a = count + 1;
    b = count - 1;
    c = count * 1;
    d = count / 1; # цілочислене ділення
    e = count % 1; # модуль
    f = count ^ 1; # піднесення до степеня

    a += 1;
    b -= 1;
    c *= 1;
    d /= 1;
    e %= 1;
    f ^= 1;

    # Постфіксний оператор збільшення та зменшення на одиницю
    a++;
    b--;

    # Префіксний оператор, який повертає збільшене на одиницю значення
    ++a;
    --b;

    # Зверніть також увагу, що в кінці рядка не обов'язково вказувати
    # розділовий знак крапка з комою.

    # Оператор галудження
    if (count == 0)
        print "Starting with count of 0";
    else
        print "Huh?";

    # Можна використовувати тернарний оператор
    print (count == 0) ? "Starting with count of 0" : "Huh?";

    # Для блоків, що складаються з кількох рядків, використовують дужки
    while (a < 10) {
        print "String concatenation is done" " with a series" " of"
            " space-separated strings";
        print a;

        a++;
    }

    for (i = 0; i < 10; i++)
        print "Good ol' for loop";

    # Порівняння є стандартними:
    # a < b  # менше ніж
    # a <= b # менше або дорівнює
    # a != b # не дорівнює
    # a == b # дорівнює
    # a > b  # більше ніж
    # a >= b # більше або дорівнює

    # Логічні оператори також стандартні:
    # a && b # AND - І
    # a || b # OR  - АБО

    # Крім того, є перевірка на збіг із регулярним виразом:
    if ("foo" ~ "^fo+$")
        print "Fooey!";
    if ("boo" !~ "^fo+$")
        print "Boo!";

    # Масиви:
    arr[0] = "foo";
    arr[1] = "bar";

    # Також можна ініціалізувати масив за допомогою вбудованої функції split():
    n = split("foo:bar:baz", arr, ":");

    # Підтримуються асоціативні масиви (насправді, всі масиви асоціативні):
    assoc["foo"] = "bar";
    assoc["bar"] = "baz";

    # Та багатовимірні масиви (з деякими обмеженнями, які описуються далі):
    multidim[0,0] = "foo";
    multidim[0,1] = "bar";
    multidim[1,0] = "baz";
    multidim[1,1] = "boo";

    # Можна перевірити членство в масиві:
    if ("foo" in assoc)
        print "Fooey!";

    # Оператор 'in' також можна використовувати для обходу ключів масиву:
    for (key in assoc)
        print assoc[key];

    # Командний рядок знаходиться у спеціальному масиві з ім'ям ARGV
    for (argnum in ARGV)
        print ARGV[argnum];

    # Можна видаляти елементи масиву. Це особливо корисно для того, щоб AWK
    # не розглядав аргументи як імена файлів для обробки
    delete ARGV[1];

    # Кількість аргументів командного рядка міститься у змінній з іменем ARGC
    print ARGC;

    # AWK має багато вбудованих функцій. Вони діляться на три категорії, які
    # буде розглянуто далі.

    return_value = arithmetic_functions(a, b, c);
    string_functions();
    io_functions();
}

# Функції декларуються так:
function arithmetic_functions(a, b, c,     d) {

    # Однією з дратівливих особливостей AWK є те, що в цій мові немає
    # локальних змінних. Усе є глобальним.
    # Для коротких сценаріїв це добре, навіть корисно, але для довших
    # сценаріїв це може бути проблемою.

    # Проте, є обхідний шлях (huk). Вказані у функції аргументи є локальними
    # у функції. Крім того, AWK дозволяє вказувати більше аргументів функції,
    # ніж потрібно. Тому, просто вставте локальну змінну в оголошення
    # функції, як це зроблено вище. Можна додати також додаткові пробіли,
    # щоб відрізнити фактичні параметри функції від створених у такий спосіб
    # локальних змінних.
    # У наведеному вище прикладі, змінні a, b, c є фактичними параметрами,
    # тоді як d є лише локальною змінною.

    # Тепер, розглянемо математичні функції

    # Більшість реалізацій AWK підтримують стандартні тригонометричні функції:
    localvar = sin(a);
    localvar = cos(a);
    localvar = atan2(b, a); # арктангенс b / a

    # та логаритмічні обчислення:
    localvar = exp(a);
    localvar = log(a);

    # квадратний корінь:
    localvar = sqrt(a);

    # округлення до цілого числа:
    localvar = int(5.34); # localvar => 5

    # випадкові числа:
    srand(); # встановити т.зв. "сіль" (без аргумента використовується час)
    localvar = rand(); # випадкове число від 0 до 1.

    # Повернути значення можна так:
    return localvar;
}

# Функції для обробки рядків тексту
function string_functions(    localvar, arr) {

    # AWK, як мова обробки тексту, підтримує функції для обробки рядків.
    # У багатьох з цих функцій використовуються регулярні вирази.

    # Пошук і заміна, першої (sub) або всіх відповідностей (gsub).
    # Обидві вони повертають кількість замінених відповідностей.
    localvar = "fooooobar";
    sub("fo+", "Meet me at the ", localvar); # localvar => "Meet me at the bar"
    gsub("e", ".", localvar); # localvar => "m..t m. at th. bar"

    # Пошук підрядка, який збігається з регулярним виразом:
    # index() робить те ж саме, але не використовує регулярний вираз.
    match(localvar, "t"); # => 4, оскільки 't' є четвертим символом

    # Розбити рядок за роздільником
    n = split("foo-bar-baz", arr, "-"); # a[1] = "foo"; a[2] = "bar"; a[3] = "baz"; n = 3

    # Інші корисні речі
    sprintf("%s %d %d %d", "Testing", 1, 2, 3); # => "Testing 1 2 3"
    substr("foobar", 2, 3); # => "oob"
    substr("foobar", 4); # => "bar"
    length("foo"); # => 3
    tolower("FOO"); # => "foo"
    toupper("foo"); # => "FOO"
}

# Функції введення-виведення
function io_functions(    localvar) {

    # Ви вже бачили print
    print "Hello world";

    # Є також printf
    printf("%s %d %d %d\n", "Testing", 1, 2, 3);

    # AWK сам по собі не має дескрипторів файлів. Він автоматично відкриває
    # дескриптор файлу, коли виконується дія, яка потребує дискриптора.
    # Рядок, який використано для цього, можна розглядати як дескриптор файлу
    # для введення-виводу. Це схоже на сценарії оболонки.
    # Рядок, при цьому, повинен точно збігатися. Тому використовуйте змінну:
    outfile = "/tmp/foobar.txt";
    print "foobar" > outfile;

    # Тепер, рядок вихідного файлу є дескриптором файлу.
    # Ви можете закрити його:
    close(outfile);

    # Ось як запустити команду в оболонці
    system("echo foobar"); # => друкує foobar

    # Читає рядок із потоку стандартного введення та зберігає в localvar
    getline localvar;

    # Читає рядок з каналу (використовуйте рядок, щоб правильно його закрити)
    cmd = "echo foobar";
    cmd | getline localvar; # localvar => "foobar"
    close(cmd);

    # Читає рядок з файлу та зберігає в localvar
    infile = "/tmp/foobar.txt";
    getline localvar < infile;
    close(infile);
}

# Як вже вказувалося, програми AWK складаються з набору шаблонів та дій.
# Було розглянуто шаблони BEGIN та END . Інші шаблони використовуються тоді,
# коли скрипт обробляє рядки з файлів або стандартного потоку введення.
#

# Вказані у командному рядку аргументи розглядаються як імена файлів для
# обробки. Усі ці файли обробляються послідовно один за одним.
# Виконується неявний цикл, в якому обробляються усі рядки з цих файлів.
# Шаблони та дії AWK схожі на оператор switch всередині циклу.

/^fo+bar$/ {
    # Ця дія буде виконуватися для кожного рядка, який збігається з регулярним
    # виразом: /^fo+bar$/, і буде пропущена для усіх рядків,
    # які не збігаються з ним. Давайте просто надрукуємо рядок:

    print;

    # Без аргументів! Це тому, що print має аргумент за замовчуванням: $0.
    # $0 - ім'я змінної, яка містить увесь поточний рядок. Ця змінна
    # створюється автоматично.

    # Також існують інші змінні зі знаком $. Кожен рядок неявно розбивається
    # перед викликом кожної дії так, як це робить оболонка. І так само, як і
    # в оболонці, до кожного поля можна отримати доступ із відповідної змінної
    # зі знаком долара.

    # Тут буде виведене друге та четверте поля з рядка
    print $2, $4;

    # AWK автоматично визначає багато інших змінних, щоб допомогти перевірити
    # та обробити кожен рядок. Однією з них є NF.

    # Друкує кількість полів у цьому рядку
    print NF;

    # Друк останнього поля у цьому рядку
    print $NF;
}

# Кожен шаблон є логічним виразом. Регулярний вираз у наведеному шаблоні
# також є перевіркою на істине/хибне, але частина виразу прихована.
# Шаблон обробляє кожен вхідний рядок і його повна версія є такою:
$0 ~ /^fo+bar$/ {
    print "Equivalent to the last pattern";
}

a > 0 {
    # цей блок виконуватиметься для кожного рядка, допоки змінна a буде
    # більшою від 0
}

# Ідея AWK зрозуміла. Обробка текстових файлів, порядкове зчитування і
# обробка, зокрема розбиття за роздільником, настільки поширені в UNIX,
# що AWK — це мова сценаріїв, яка виконує все це сама, без ваших вказівок.
# Все, що вам потрібно зробити, це написати шаблони та дії на основі того,
# що ви очікуєте від введення, і що ви хочете з ним зробити.

# Ось короткий приклад простого сценарію, для якого чудово підходить AWK.
# Цей сценарій прочитає ім’я зі стандартного потоку введення, а потім надрукує
# середній вік для людей з цим іменем.
# Нехай, як аргумент скрипта вказано ім’я файла з такими даними:
#
# Bob Jones 32
# Jane Doe 22
# Steve Stevens 83
# Bob Smith 29
# Bob Barker 72
#

# Ось сценарій:

BEGIN {

    # Спершу попросимо користувача ввести ім'я
    print "What name would you like the average age for?";

    # Отримати рядок зі стандартного потоку введення, а не з файлів
    # командного рядка
    getline name < "/dev/stdin";
}

# Тепер обробимо кожен рядок, першим полем якого є вказане ім'я
$1 == name {

    # Тут усередині ми маємо доступ до низки корисних змінних, які вже
    # встановлені для нас попередньо:
    # $0 - це весь рядок
    # $3 - це третє поле, вік, який нас тут цікавить
    # NF - це кількість полів, яка має бути 3
    # NR - це кількість записів (рядків), переглянутих на поточний момент
    # FILENAME - ім'я файла, який обробляється
    # FS - це роздільник полів, який використовується. Тут це - " "
    # ...  є багато інших задокументованих на сторінці керівництва змінних

    # Обчислимо поточну суму та кількість рядків, які відповідають шаблону
    sum += $3;
    nlines++;
}

# Ще один спеціальний шаблон називається END. Він виконується після обробки
# всіх текстових файлів. На відміну від BEGIN, він запуститься, якщо були
# дані для обробки. Він виконуватиметься після того, як будуть прочитані та
# оброблені, відповідно до вказаних правил та дій, усі файли з вхідними даними.
# Його мета зазвичай полягає в тому, щоб вивести якийсь остаточний звіт або
# зробити щось із накопиченою протягом сценарію сукупністю даних

END {
    if (nlines)
        print "The average age for " name " is " sum / nlines;
}
```

Детальніше дивіться:

* [Awk tutorial](http://www.grymoire.com/Unix/Awk.html)
* [Awk man page](https://linux.die.net/man/1/awk)
* [The GNU Awk User's Guide](https://www.gnu.org/software/gawk/manual/gawk.html) GNU Awk is found on most Linux systems.
* [AWK one-liner collection](http://tuxgraphics.org/~guido/scripts/awk-one-liner.html)
* [Awk alpinelinux wiki](https://wiki.alpinelinux.org/wiki/Awk) a technical summary and list of "gotchas" (places where different implementations may behave in different or unexpected ways).
* [basic libraries for awk](https://github.com/dubiousjim/awkenough)