Как определить новые команды или макросы в awk

Мне нравится определять новую команду, которая оборачивает существующую команду awk, такую ​​как print, Однако я не хочу использовать функцию:

#wrap command with function
function warn(text) { print text > "/dev/stderr" }
NR%1e6 == 0 {
  warn("processed rows: "NR)
}

Вместо этого мне нравится определять новую команду, которая может быть вызвана без скобок:

#wrap command with new command ???
define warn rest... { print rest... > "/dev/stderr" }
NR%1e6 == 0 {
  warn "processed rows: "NR
}

Одно из решений, которое я могу себе представить, - это использование препроцессора и, может быть, хорошая настройка сценария awk для вызова этого препроцессора, за которым следует awk. Тем не менее, я больше надеялся на чистое решение awk.

Примечание: решение также должно работать в mawk, который я использую, потому что он намного быстрее, чем vanilla GNU / awk.

Обновление: обсуждение показало, что gawk (GNU/awk) может быть довольно быстрым и mawk не требуется.

3 ответа

Решение

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

Странно, что вы называете GNU awk "ванильным", когда он имеет гораздо больше полезных функций, чем любой другой доступный в настоящее время awk, тогда как mawk - это просто урезанный awk, оптимизированный для скорости, которая необходима только в очень редких случаях.

Глядя на источник Mawk, я вижу, что команды являются специальными и не могут быть добавлены во время выполнения. От kw.c:

keywords[] =
{
    { "print",    PRINT },
    { "printf",   PRINTF },
    { "do",       DO },
    { "while",    WHILE },
    { "for",      FOR },
    { "break",    BREAK },
    { "continue", CONTINUE },
    { "if",       IF },
    { "else",     ELSE },
    { "in",       IN },
    { "delete",   DELETE },
    { "split",    SPLIT },
    { "match",    MATCH_FUNC },
    { "BEGIN",    BEGIN },
    { "END",      END },
    { "exit",     EXIT },
    { "next",     NEXT },
    { "nextfile", NEXTFILE },
    { "return",   RETURN },
    { "getline",  GETLINE },
    { "sub",      SUB },
    { "gsub",     GSUB },
    { "function", FUNCTION },
    { (char *) 0, 0 }
};

Вы можете добавить новую команду, исправив код Си Моука.

Я создал скрипт-оболочку под названиемкоторый сочетает в себе препроцессор C (от GCC) с Awk.

Лицензия BSD, поставляется с справочной страницей, регрессионными тестами и простыми инструкциями по установке.

Обычно препроцессор C создает макросы, которые выглядят как функции; но, используя некоторые трюки с потоком управления, которые работают в Awk так же, как и в C, мы можем провернуть маленькие чудеса синтаксического сахара:

      function __warn(x)
{
   print x
   return 0
}

#define warn for (__w = 1; __w; __w = __warn(__x)) __x =

NR % 5 == 0 {
  warn "processed rows: "NR
}

Бежать:

      $ cppawk -f warn.cwk 
a
b
c
d
e
processed rows: 5
f
g
h
i
j
processed rows: 10
k

Потому что весь forхитрость заключается в одной строке кода, мы могли бы использовать __LINE__символ, чтобы сделать скрытые переменные квазиуникальными:

      function __warn(x)
{
   print x
   return 0
}

#define xcat(a, b, c) a ## b ## c
#define cat(a, b, c) xcat(a, b, c)
#define uq(sym) cat(__, __LINE__, sym)
#define warn for (uq(w) = 1; uq(w); uq(w) = __warn(uq(x))) uq(x) =

NR % 5 == 0 {
  warn "processed rows: "NR
}

Расширение это:

      $ cppawk --prepro-only -f warn.cwk 
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "<stdin>"
function __warn(x)
{
   print x
   return 0
}
NR % 5 == 0 {
  for (__13w = 1; __13w; __13w = __warn(__13x)) __13x = "processed rows: "NR
}

В u()интерполированный макрос 13в переменные, потому что warnвызывается в строке 13.

Надеюсь, вам понравится.

PS, возможно, не делайте этого, но найдите менее хакерский способ использования cppawk.

Вы можете использовать вариативные макросы C99/GNUC, например:

      #define warn(...) print __VA_ARGS__ >> "/dev/stderr"

NR % 5 == 0 {
  warn("processed rows:", NR)
}

Мы сделали скромный printоболочка, которая перенаправляет на стандартную ошибку. Кажется, ничего, но вы не можете сделать это с функцией Awk: не без того, чтобы сделать ее функцией с одним аргументом и передать значение выражения, которое связывает все.

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