在 awk 中使用用户定义的 bash 函数

在 awk 中使用用户定义的 bash 函数

是否可以在 AWK 中使用 bash 函数?

示例文件(字符串、int、int、int)

Mike 247808 247809 247810

尝试将值从十进制转换为十六进制。

在 shell 脚本中或 shell 脚本中定义的函数.bashrc

$ awk '{print $1 ; d2h($2)}' file

awk: calling undefined function d2h
 input record number 1, file file
 source line number 1

答案1

尝试使用system()函数:

awk '{printf("%s ",$1); system("d2h " $2)}' file

在您的情况下,system将调用d2h 247808该命令的输出,然后将其附加到printf输出:

Mike 3C800

编辑:

作为system使用sh而不是bash我找不到访问的方法.bashrc。但您仍然可以使用当前 bash 脚本中的函数:

#!/bin/bash
d2h() {
    # do some cool conversion here
    echo "$1" # or just output the first parameter
}
export -f d2h
awk '{printf("%s ",$1); system("bash -c '\''d2h "$2"'\''")}' file

注:-f用于导出一个函数而不是一个变量。

编辑2:

我不知道为什么,但这在我的 Ubuntu 16.04 上不起作用。这很奇怪,因为它曾经在 Ubuntu 14.04 上运行。

答案2

您可以从 awk 调用 bash 并使用其输出。从性能角度来看,如果这种情况发生得太频繁,显然是危险的。引用手册页:

command | getline [var]

运行命令将输出传输到 $0 或 var,

命令将是一个 bash 脚本,其中包含函数定义并执行该函数。

答案3

从十进制转换为十六进制awk本身就可以很好地完成。你可以定义一个awk函数来做到这一点:

function d2h(d) {
  return sprintf("%x", d)
}

现在要回答一般情况下的问题,为了awk运行bash函数,您需要awk执行一个bashshell,它bash来解释该函数的定义,并调用该函数,并将提取的值awk作为参数传递。

不是微不足道的。

bash支持通过环境导出函数,因此它可以在 的后续调用中使用bash,因此这是将函数的定义传递给bash调用者的一种方法awk

export -f d2h

awk执行命令(此处)的唯一方法bash是使用其system("cmd"), orprint... | "cmd""cmd" | getline。在所有情况下,awk都会运行 shell 来解释该cmd,但它会是sh,而不是bash。因此,您需要构建一个命令行,sh这是一个bash解释bash命令行以调用该函数的调用,因此您需要小心引用:

export -f d2h
<file awk -v q="'" '
  function shquote(s) {
    gsub(q, q "\\" q q, s)
    return q s q
  }
  {print $1; system("exec bash -c '\''d2h \"$1\"'\'' bash " shquote($2))}'

如果您想将函数的输出返回到 中awk,则需要通过管道将其传回。为此,您可以使用cmd | getline代替system(cmd)(这会使cmd标准输出保持不变)。

cmd | getline line商店一条线(严格来讲一条记录,默认情况下记录为行),因此要在由多行组成的情况下获取整个输出,您需要一个循环,例如:

awk '...
  cmd = "exec bash -c '\''d2h \"$1\"'\'' bash " shquote($2)
  output = ""
  while ((cmd | getline line) > 0) {
    output = output line RS
  }
  sub(RS "$", "", output) # remove the last newline
  ...'

这确实意味着每次调用函数时都要运行一sh又一,因此效率非常低。bash这最终会比使用bash以下命令进行读取和分割的效率低得多while read loop

(unset -v IFS; while read -r a b rest; do
  printf '%s\n' "$a"
  d2h "$b"
 done < file)

另请注意,自从 shellshock 以来,bash现在在名为 的环境变量中导出函数BASH_FUNC_d2h%%。一些sh实现包括mksh和更新版本dash 消除来自环境的那些环境变量:

$ env 'foo%%=bar' dash -c 'printenv foo%%'
$ env 'foo%%=bar' mksh -c 'printenv foo%%'
$ env 'foo%%=bar' zsh  -c 'printenv foo%%'
bar
$ env 'foo%%=bar' bash -c 'printenv foo%%'
bar

因此,您可以通过其他方式传递函数定义,而不是依赖脆弱的函数导出功能。它可以通过具有常用名称的环境变量:

BASH_FUNCTIONS=$(typeset -f d2h) awk '
   ...
   cmd = "exec bash -c '\''eval \"$BASH_FUNCTIONS\";" \
         "d2h \"$1\"'\'' bash " shquote($2)
   ...'

答案4

在 awk 中使用用户定义的 bash 函数

免责声明:我意识到这不是OP想要做的,但谷歌会引导像我这样的其他人找到这个答案。

情况

您有一个bash由函数组织的脚本(因为您不讨厌自己或[大多数]同事),并且这些函数中至少有一个需要从awk.

解决方案

脚本

#!/bin/env bash

# The main function - it's a sound pattern even in BASH
main(){
    # In the awk command I do some tricky things with single quotes. Count carefully...
    # The first $0 is outside the single quotes so it is the name of the current bash script.
    # The second $0 is inside the single quotes so it is awk's current line of input.
    awk '{printf("%s. ", ++c); system("'$0' --do"); print $0}'<<-PRETEND_THIS_IS_AN_INPUT_STREAM
        and
        and
        well
    PRETEND_THIS_IS_AN_INPUT_STREAM
}

# functionized to keep things DRY
doit(){
    echo -n "doin' it "
}


# check for a command switch and call different functionality if it is found
if [[ $# -eq 1 && $1 == "--do" ]];
then
        doit
else
        main
fi

输出

$ ./example.sh
1. doin' it and
2. doin' it and
3. doin' it well

相关内容