awk может рассматривать значения в записях по-другому на основе первого значения?

Я относительно плохо знаком с awk. Я создаю awk сценарий, который считает файл, который имеет общий формат:

NAME firstName lastName
PAY cost numberOfPayments
END

Мой файл будет выглядеть примерно так:

NAME Jane Doe
PAY 5.00 2
PAY 2.00 10
END
NAME John Doe
PAY 10.00 5
PAY 4.00 3
PAY 1.00 20
END

И сумма платежей между NAME кому: END может варьироваться, и может быть несколько имен (это - просто образец).

Это - мой awk сценарий:

# !/bin/awk

BEGIN { total=0; RS = "END"; }
{
    if (match($1, "NAME")) {
        print $2;
    }
    if (match($1, "SAVE")) {
        total = total + ($2 * $3);
        print total;
    }
}

Первое значение должно распознать, какое действие мы формуем (PAY по сравнению с. NAME). На основе этого я должен или распечатать NAME или найдите общую сумму, которая найдена путем умножения стоимости количеством платежей. END то, что я использую, чтобы показать, что это - конец записи для того определенного клиента.

Вывод этого конкретного файла должен быть:

Jane 30
John 82

Я попробовал несколько путей, но я, может казаться, не получаю желаемый вывод. Любая справка для получения этой работы очень ценилась бы!

1
задан 26 September 2017 в 07:42

1 ответ

Во-первых, код:

#!/usr/bin/awk -f

$1 == "NAME" { printf "%s ", $2 }
$1 == "PAY" { total += $2 * $3 }
$1 == "END" { print total; total = 0 }

Если Вы называете сценарий tally, отметьте его исполняемый файл с chmod +x tally, и Вы находитесь в каталоге, который содержит его, можно выполнить его на входном файле file с:

./tally file

На входном тексте Вы показали, он дает вывод, который Вы хотели:

Jane 30
John 82

Вы не заявили, на что Вы хотите, чтобы вывод был похож, когда существует несколько имен без END промежуточный, но я предполагаю, что Вы хотите произвести имя для каждого. Рассмотрите этот входной файл:

NAME Jane Doe
NAME Clark Kent
PAY 5.77 9
END
NAME John Doe
PAY 14.22 6
NAME Linda Lee Danvers
PAY .25 4
END

Это производит этот вывод:

Jane Clark 51.93
John Linda 86.32

Что это делает, и почему:

В проблеме Вы пытаетесь решить, каждое из того, что должно концептуально думаться, поскольку запись является "строкой файла конфигурации" нескольких строк, где строка может состоять из нескольких полей. Каждая данная величина поэтому имеет три "координаты": ⟨stanza, строка, поле ⟩

Но фундаментальная абстракция AWK вместо этого ⟨record, поле ⟩. AWK является все еще хорошим выбором для этой проблемы, но необходимо будет решить, как Вы хотите отобразить естественную абстракцию проблемы к абстракции Ваши поддержки инструмента непосредственно. В Вашем коде похоже, что можно пытаться рассматривать каждую строку файла конфигурации как единственную запись, так как Вы сделали END входной разделитель записей (RS = "END"). Это может быть сделано работать, и я надеюсь, что другие ответы отправляются то шоу как. Но я предлагаю иметь awk рассматривайте каждую строку как запись вместо этого.

Причина состоит в том, что уже существует другой способ думать о Ваших входных данных: как список команд, один на строку, где:

  1. Ваш NAME команда производит слово, которое следует за нею. Концептуально это - имя.
  2. Ваш PAY команда накапливает продукты в переменную total. А именно, это умножает два значения, которые следуют за ним и увеличения total той суммой.
  3. Ваш END печать команды total, заканчивает строку и сброс total назад обнулять.

Как это работает, линию за линией:

#!/usr/bin/awk -f

В Ubuntu, awk расположен в /usr/bin и нет /bin. -f флаг требуется (на любой ОС) сказать AWK, что следующий аргумент, который является именем файла самого сценария, должен быть интерпретирован как сценарий, а не как название входного файла для обработки.

Нет BEGIN правило

Вы могли сделать один и установить tally = 0 в нем, но Вам не нужно к тому, потому что AWK позволяет арифметику на неинициализированных переменных и рассматривает их как нуль. (Если Вы работали gawk --lint -f tally file затем Вы могли бы хотеть включать присвоение явно для предотвращения "ссылки на неинициализированную переменную" предупреждение.) Я поместил пустую строку здесь, но Вы не имеете к.

$1 == "NAME" { printf "%s ", $2 }

Когда первое поле NAME, распечатайте второе поле $2 как строка (%s) сопровождаемый пространством.

$1 == "PAY" { total += $2 * $3 }

Когда первое поле PAY, увеличьте значение total продуктом вторых и третьих полей.

$1 == "END" { print total; total = 0 }

Когда первое поле END, распечатайте значение total. print оператор автоматически добавляет выходной разделитель записей, который является новой строкой, так как Вы не установили ORS иначе. Затем набор total назад обнулять для подготовки к следующей строке файла конфигурации (если таковые имеются).

1
ответ дан 7 December 2019 в 15:29

Другие вопросы по тегам:

Похожие вопросы: