предварительно заполнить ассоциативный ключ массива в awk? - PullRequest
4 голосов
/ 29 сентября 2011

Я написал плагин munin, который использует пакет slurm для мониторинга состояний заданий в кластере HPC.Я написал это на sh + awk (а не на своем обычном инструменте выбора, perl).

Сценарий работает, но мне потребовались целые годы, чтобы выяснить, как предварительно заполнить ассоциативный массив возможных состояний(некоторые / большинство могут не присутствовать в выходных сакральных, и я хочу, чтобы они по умолчанию на ноль).Google не сильно помог, и лучшее, что я мог придумать, - это использовать разбивку на строку для создания временного массива, который я затем повторил.

Я придумал это:

BEGIN {
    num = split("cancelled completed completing failed nodefail pending running suspended timeout",statenames," ");
    for (i=1;i<=num;i++) {
        states[statenames[i]] = 0
    }
  }

Это работает, но кажется неуклюжим по сравнению с тем, как я сделал бы это в Perl, например:

foreach (qw(cancelled completed completing failed nodefail pending running suspended timeout)) {
    $states{$_} = 0;
}

или это

%states = map {$_ => 0} qw(cancelled completed completing failed nodefail pending running suspended timeout);

мой вопрос:Есть ли способ сделать это в awk, который похож на любую из версий perl?

[отредактировано]

, чтобы уточнить, вот пример выходного сакрала, который я добавляю в awk.Обратите внимание, что единственными состояниями в этом выводе являются RUNNING, COMPLETED и CANCELED - другие не существуют (потому что они не произошли сегодня), но я все равно хочу, чтобы они были в выводе моего скрипта (в форме, используемой munin как "statename.value 0 ").

# sacct -X -P -o 'state' -n
RUNNING
RUNNING
RUNNING
RUNNING
COMPLETED
RUNNING
COMPLETED
RUNNING
COMPLETED
COMPLETED
CANCELLED by 1000
COMPLETED

[снова отредактировано]

и вот пример вывода из моего плагина munin:

# ./slurm-sacct
suspended.value 0
pending.value 0
nodefail.value 0
failed.value 0
running.value 6
completing.value 0
completed.value 5
timeout.value 0
cancelled.value 1

Сценарий запускается и делает чтоЯ хочу, я просто хотел узнать, есть ли лучший способ инициализировать ассоциативный массив.

Ответы [ 5 ]

5 голосов
/ 29 сентября 2011

Вам, вероятно, вообще не нужно это делать.Переменные в awk являются динамическими, что означает, что они автоматически инициализируются при первом использовании (либо назначаются на , либо на ), и это также относится к элементам массива.

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

{ states[$1]++ }
END {
     for (state in states) print state, states[state]
}

Каждый раз, когда выполняется выражение states[$1]++, оно проверяет наличие states[$1] и инициализирует его значением 0, если оно еще не существует.


РЕДАКТИРОВАТЬ : Из вашего комментария я предполагаю, что вы хотите распечатать строку для каждого возможного состояния, независимо от того, есть ли какие-либо задания в этом состоянии или нет.В этом случае вам нужно включить все возможные имена состояний, и для этого нет краткой записи, как в Perl.Насколько я знаю, то, что вы уже нашли, настолько чистое, насколько это возможно.(Awk на самом деле не предназначен для такого использования)

Я бы предложил следующее:

{ states[$1]++ }
END {
     split("cancelled completed completing failed nodefail pending running suspended timeout",statenames," ");
     for (state in statenames) print state, states[state]+0
}
4 голосов
/ 24 октября 2012

Возможно, Крэйг может использовать вместо:

print "Timeout states ",states[timeout],".";

это:

print "Timeout states ",int(states[timeout]),".";

В моем случае, если в вводе awk нет состояния тайм-аута, первый отпечаток выдаст:

Время ожидания.

Пока второй даст:

Тайм-аут состояний 0.

1 голос
/ 29 сентября 2011

Я думаю, что более естественным подходом в awk было бы использование отдельного файла ключей.Рассмотрим файл keys.txt с одним ключом на строку.Затем вы могли бы сделать что-то вроде этого:

printf "key1\nkey2\nkey2\nkey5" | 
  awk '
    FILENAME == "keys.txt" {
      counts[$0] = 0
      next
    }

    {
      counts[$0]++
    }

    END {
      for (key in counts) {
        print key, counts[key]
      }
    }' keys.txt -

С пятью ключами в keys.txt это выдает:

key1 1
key2 2
key3 0
key4 0
key5 1

Хотя ключи показаны здесь по порядку, это просто случайное ине следует полагаться.

Для конкретного примера вы также можете вообще пропустить ассоциативный массив.Вместо этого вы можете минимально обработать строки с помощью awk и использовать sort | uniq -c для подсчета количества.Наличие всех ключей может быть гарантировано с помощью join против файла ключей.

0 голосов
/ 24 октября 2012

Одним из вариантов ответа @ DavidZaslavsky может быть печать состояний в порядке, указанном вами в строке split (). Это было бы:

{ states[tolower($1)]++ }
END {
     n = split("cancelled completed completing failed nodefail pending running suspended timeout",statenames)
     for (i=1; i<=n; i++) {
         state = statenames[i]
         print state, states[state]+0
     }
}

Я также преобразовал ввод в нижний регистр, чтобы он соответствовал вашим жестко запрограммированным значениям, избавился от ненужного третьего аргумента для split () и последующего нулевого оператора (конечная точка с запятой).

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

{ states[tolower($1)]++ }
END {
     n = split("cancelled completed completing failed nodefail pending running suspended timeout",statenames)
     for (i=1; i<=n; i++) {
         state = statenames[i]
         print state, states[state]+0
         delete states[state]
     }
     for (state in states) {
         print "WARNING: found new state name %s\n",state | "cat>&2"
         print state, states[state]+0
     }
}
0 голосов
/ 30 сентября 2011

awk несколько неуклюже (я бы сказал «менее кратко»), чем Perl.

Вы можете написать это (аналогично ответу @ Michael):

pipeline of data |
awk '
  NR == FNR {statenames[$1]=0; next}
  { usual processing }
  END { usual output }
' <(printf "%s\n" cancelled completed completing failed nodefail pending running suspended timeout) -
...