Спочатку обробіть останній рядок, використовуючи awk


11

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

Наступний метод, використовуючи tacдвічі, виконує цю роботу, але, можливо, є складнішим, ніж необхідно.

$ cat file
0 5
1 2
2 3
3 4
$ tac file | awk 'NR==1{norm=$2} {print $1, $2/norm}' | tac
0 1.25
1 0.5
2 0.75
3 1

Моє запитання таке: чи можна отримати вищезазначений результат, використовуючи лише awk?

Я думаю, що відповідь - «Ні, awk сканує файл за рядком», але я відкритий для пропозицій щодо альтернатив.

Відповіді:


5

Ви можете це зробити як пропуск із двома прохадностями:

awk 'FNR == NR { n = $2; next } { print $1, $2/n }' infile infile

Якщо ваша версія awk підтримує блок ENDFILE (наприклад, GNU awk 4+), ви можете зробити це так:

awk 'ENDFILE { n = $2 } FNR != NR { print $1, $2/n }' infile infile

Зауважте, що ефективніше до seekкінця файлу спочатку побачити відповідь Кемха .

Пояснення

Перший приклад працює при запам'ятовуванні попереднього $2, тобто він оцінюється лише тоді, коли локальний лічильник ліній ( FNR) дорівнює глобальному лічильнику ліній ( NR). nextКоманда переходить до наступного рядка, в даному випадку це гарантує , що останній блок оцінюється тільки коли другий аргумент обробляється.

Другий приклад має аналогічну логіку, але користується перевагою блоку ENDFILE, який оцінюється, коли доходить кінець вхідного файлу.


Перший приклад працює добре, другий - ні $ awk --version GNU Awk 3.1.8. Чи можете ви додати зовсім невелике пояснення того, як обробляються два вхідні файли та що nextце робиться?
Бернхард

1
@Bernhard: див. Редагування
Тор

6

Якщо вашим джерелом даних є файл, який можна прочитати кілька разів (тобто це не потік), спочатку слід скористатись, tail(1)щоб отримати потрібні дані з останнього рядка та передати їх, щоб пробудити його для послідовної обробки файлу. tailбуде намагатися до кінця файлу прочитати останній рядок, не потребуючи прочитати всі дані до нього.

awk -v norm=$(tail -n 1 file | cut -d' ' -f2) '{print $1, $2/norm}' file

Це буде великою виграшею для великих файлів, коли весь файл не вміститься в кеш-пам'ять (тобто його потрібно буде прочитати з диска двічі, один раз за кожен пропуск), і допоможе меншою мірою, не потребуючи сканування. вхід для отримання останнього рядка. Менші файли можуть не відрізнятися від двопрохідного підходу.


3

Ви можете завантажити їх у масив і прочитати його назад:

awk '{x[i++]=$0} END{for (j=i-1; j>=0;) print x[j--] }'

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


Я згоден, використання рішення for-loops в awkне є рішенням.
Бернхард
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.