【问题标题】:How to define new commands or macros in awk如何在 awk 中定义新的命令或宏
【发布时间】:2014-12-17 16:46:06
【问题描述】:

我喜欢定义一个包装现有 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 脚本的 shebang 以调用此预处理器,然后调用 awk。不过,我更希望有一个纯粹的 awk 解决方案。

注意:该解决方案也应该适用于我使用的mawk,因为它比 vanilla GNU/awk 快得多。

更新:讨论表明gawk (GNU/awk) 可以非常快,并且不需要mawk

【问题讨论】:

  • 鉴于功能路线的简单性以及预处理/等的可能相对复杂性。这真的值得吗?
  • 也许你是对的,但我一直在寻找(简单的)方法来编写更具可读性的代码。我在这里的目标不是一个非常老套的解决方案。

标签: shell macros awk command mawk


【解决方案1】:

您不能在任何 awk 中执行此操作,并且如果不编写 awk 语言解析器,您将无法在 awk 之外稳健地执行此操作,到那时您也可以编写自己的类似 awk 的命令,该命令实际上将不再是真正的 awk in尽管它的行为与该名称的任何其他命令不同。

奇怪的是,你将 GNU awk 称为“vanilla”,因为它比任何其他当前可用的 awk 都具有更多有用的功能,而 mawk 只是为速度优化的精简 awk,仅在极少数情况下才需要。

【讨论】:

  • 对不起香草。我不知道“香草”可能是一个过于消极的词。但是,我使用 AWK 处理非常大的 csv 文件,mawk 通常感觉比gawk 快 10 倍。
  • @Juve 感觉还是
  • @Etan 在我的四核 Macbook (2.5Ghz) 上处理 5M 行,mawk 需要 1.47 秒,awk 需要 9.6 秒(即,mawk 快 6.5 倍)跨度>
  • @Juve 好的。需要说明的是,我并没有怀疑你。我在怀疑“感觉”。
  • 感谢 Ed 的提示!我从 2007 年开始使用一些糟糕的 Mac awk。我刚刚安装了 gawk,它可以在 2.1 秒内处理我的示例文件。
【解决方案2】:

查看 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 }
};

您可以通过修补 Mawk 的 C 代码来添加新命令。

【讨论】:

    【解决方案3】:

    我创建了一个名为 cppawk 的 shell 包装脚本,它结合了 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 插入到变量中,因为在第 13 行调用了 warn

    希望你喜欢。

    PS,也许不要这样做,但找到一些不那么老套的方式来使用cppawk

    您可以使用 C99/GNUC 可变参数宏,例如:

    #define warn(...) print __VA_ARGS__ >> "/dev/stderr"
    
    NR % 5 == 0 {
      warn("processed rows:", NR)
    }
    

    我们制作了一个不起眼的 print 包装器,它重定向到标准错误。看起来没什么,但你不能用 awk 函数做到这一点:不能不让它成为一个单参数函数并传递表达式的值连接一切。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-29
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多