在 awk 中使用 bc

Using bc in awk

我正在尝试在 awk 脚本中使用 bc。在下面的代码中,我试图将十六进制数转换为二进制数并将其存储在一个变量中。

#!/bin/awk -f

{
  binary_vector = $(bc <<< "ibase=16;obase=2;FF") 
}

我哪里出错了?

不是说这是个好主意但是:

$ awk 'BEGIN {
    cmd = "bc <<< \"ibase=16;obase=2;FF\""
    rslt = ((cmd | getline line) > 0 ? line : -1)
    close(cmd)
    print rslt
}'
11111111

另见 http://gnu.org/software/gawk/manual/gawk.html#Bitwise-Functions and http://gnu.org/software/gawk/manual/gawk.html#Nondecimal-Data

下面的一行 Awk 脚本应该做你想做的事:

awk -vVAR=$(read -p "Enter number: " -u 0 num; echo $num) \
 'BEGIN{system("echo \"ibase=16;obase=2;"VAR"\"|bc");}'

解释:

-vVAR 将变量 VAR 传递给 Awk

-vVAR=$(read -p ... ) 从 shell 给用户输入。

system("echo ... |bc") 使用 Awk 系统内置命令执行 shell 命令。请注意引用是如何在变量 VAR 处停止然后在它之后继续的,这样 Awk 将 VAR 解释为 Awk 变量而不是作为放入系统调用的字符串的一部分。

更新 - 在 Awk 变量中使用它:

awk -vVAR=$(read -p "Enter number: " -u 0 num; echo $num) \
'BEGIN{s="echo \"ibase=16;obase=2;"VAR"\"|bc"; s | getline awk_var;\ 
close(s); print awk_var}'

s | getline awk_var 会将命令 s 的输出放入 Awk 变量 awk_var 中。请注意,该字符串是在将其发送到 getline 之前构建的 - 如果不是(除非您将字符串连接括起来)Awk 将尝试将它发送到 getline 中的单独部分 %s VAR %s。

close(s) 关闭管道——尽管对于 bc 来说这无关紧要,Awk 在退出时自动关闭管道——如果你把它放到一个更复杂的 Awk 脚本中,最好明确地关闭管道。根据 Awk 文档,某些命令(例如 mail 将在完成之前等待管道关闭。

http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_39.html

根据您编写示例的方式,您似乎想要将 awk 记录( line )转换为关联数组。这是一个 awk 可执行脚本,它允许通过 运行 对 split 类型数组中的值执行 bc 命令:

#!/usr/bin/awk -f

{
    # initialize the a array
    cnt = split([=10=], a, FS)

    if( convertArrayBase(10, 2, a, cnt) > -1 ) {

        # use the array here
        for(i=1; i<=cnt; i++) {
            print a[i]
        }
    }
}

    # Destructively updates input array, converting numbers from ibase to obase
    #
    # @ibase:  ibase value for bc
    # @obase:  obase value for bc
    # @a:      a split() type associative array where keys are numeric
    # @cnt:    size of a ( number of fields )
    #
    # @return: -1 if there's a getline error, else cnt
    #
function convertArrayBase(ibase, obase, a, cnt,         i, b, cmd) {

    cmd = sprintf("echo \"ibase=%d;obase=%d", ibase, obase)
    for(i=1; i<=cnt; i++ ) {
        cmd = cmd ";" a[i]
    }
    cmd = cmd "\" | bc"

    i = 0 # reset i
    while( (cmd | getline b) > 0 ) {
        a[++i] = b
    }
    close( cmd )
    return i==cnt ? cnt : -1
}

当与输入一起使用时:

1 2 3
4 s 1234567

此脚本输出以下内容:

1
10
11
100
0
100101101011010000111

convertArrayBase 函数对 split 类型数组进行操作。因此,在调用它之前,您必须使用整行(如图所示)或字段的子字段(未显示)初始化输入数组(此处为 a)。它破坏性地更新数组。

您可以使用一些辅助文件直接调用 bc 以获得类似的输出。我没有发现 bc 支持 -stdin 作为文件名)所以 比我想要的多一点。

制作这样的 start_cmds 文件:

ibase=10;obase=2;

quit_cmd 喜欢:

;quit

给定一个输入文件(称为 data.semi),其中数据由 ; 分隔,如下所示:

1;2;3
4;s;1234567

你可以运行bc喜欢:

$ bc -q start_cmds data.semi quit_cmd
1
10
11
100
0
100101101011010000111

这与 awk 脚本输出的数据相同,但只调用 bc 一次所有输入。现在,虽然该数据不在脚本中的 awk 关联数组中,但 bc 输出可以作为 awk 的标准输入输入写入并重新组装到数组中,例如:

bc -q start_cmds data.semi quit_cmd | awk 'FNR==NR {a[FNR]=; next} END { for( k in a ) print k, a[k] }' -
1 1
2 10
3 11
4 100
5 0
6 100101101011010000111

最后一个破折号告诉 awk 将 stdin 视为输入文件,并允许您稍后添加其他文件进行处理。