![在 awk 中使用用户定义的 bash 函数](https://linux22.com/image/31278/%E5%9C%A8%20awk%20%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%94%A8%E6%88%B7%E5%AE%9A%E4%B9%89%E7%9A%84%20bash%20%E5%87%BD%E6%95%B0.png)
是否可以在 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
执行一个bash
shell,它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