【问题标题】:Use an array created using awk as a variable in another awk script在另一个 awk 脚本中使用使用 awk 创建的数组作为变量
【发布时间】:2022-10-14 21:18:01
【问题描述】:

我正在尝试使用 awk 使用包含使用另一个 awk 脚本创建的数组的条件语句来提取数据。

我用于创建数组的 awk 脚本如下:

array=($(awk 'NR>1 { print $1 }' < file.tsv))

然后,在另一个 awk 脚本中使用这个数组

awk var="${array[@]}"  'FNR==1{ for(i=1;i<=NF;i++){ heading[i]=$i } next } { for(i=2;i<=NF;i++){ if($i=="1" && heading[i] in var){ close(outFile); outFile=heading[i]".txt"; print ">kmer"NR-1"\n"$1 >> (outFile) }}}' < input.txt

但是,当我运行它时,会发生以下错误。

awk: fatal: cannot open file 'foo' for reading (No such file or directory)

我已经查看了多篇关于为什么会发生此错误以及如何正确implement a shell variable in awk 的帖子,但到目前为止,这些都没有奏效。但是,当删除 shell 变量并运行脚本时,它确实可以工作。

awk 'FNR==1{ for(i=1;i<=NF;i++){ heading[i]=$i } next } { for(i=2;i<=NF;i++){ if($i=="1"){ close(outFile); outFile=heading[i]".txt"; print ">kmer"NR-1"\n"$1 >> (outFile) }}}' < input.txt

我真的需要那个条件语句,但不知道我在 awk 中实现 bash 变量时做错了什么,希望能得到一些帮助。

提前谢谢。

【问题讨论】:

  • 您不能将数组传递给子进程。您只能传递单个字符串。这是 Linux(以及大多数(如果不是全部)其他操作系统)的设计限制,其中唯一可以被所有进程“理解”的值是字符串。要模拟传递数组,您必须首先将其序列化为字符串,然后在接收端对其进行反序列化。

标签: bash shell awk


【解决方案1】:

只需使用单个 awk 读取两个文件

awk '
    FNR == NR && FNR > 1 {
        var[$1]
        next
    }
    FNR == 1 {
        for (i = 1; i <= NF; i++)
            heading[i] = $i
        next
    }
    {
        for (i = 2; i <= NF; i++)
            if ( $i == "1" && heading[i] in var) {
                close(outFile)
                outFile = heading[i] ".txt"
                print ">kmer" (NR-1) "
" $1 >> (outFile)
            }
    }
' file.tsv input.txt

【讨论】:

  • 您应该在写入之后而不是之前执行close(outfile),这样您就不会在打开最后一个输出文件的情况下离开循环。我认为第一个块可能应该是FNR == NR { if ( FNR &gt; 1 ) { var[$1] } next },所以next 不会在第一个文件的第一行被跳过,但是如果没有样本输入/输出就很难说。
【解决方案2】:

该特定错误消息是因为您在var= 前面忘记了-v(应该是awk -v var=,而不仅仅是awk var=)但正如其他人指出的那样,您不能在awk 命令上设置数组变量线。另请注意,array数组,而不是awkarray,shell 和 awk 是两个完全不同的工具,每个都有自己的语法、语义、范围等。

以下是如何真正做你想做的事情:

array=( "$(awk 'BEGIN{FS=OFS="	"} NR>1 { print $1 }' < file.tsv)" )

awk -v xyz="${array[*]}" '
    BEGIN{ split(xyz,tmp,RS); for (i in tmp) var[tmp[i]] }
    ... now use `var` as you were trying to ...
'

例如:

$ cat file.tsv
col1    col2
a b     c d e
f g h   i j

$ cat -T file.tsv
col1^Icol2
a b^Ic d e
f g h^Ii j

$ awk 'BEGIN{FS=OFS="	"} NR>1 { print $1 }' < file.tsv
a b
f g h

$ array=( "$(awk 'BEGIN{FS=OFS="	"} NR>1 { print $1 }' < file.tsv)" )

$ awk -v xyz="${array[*]}" '
    BEGIN {
        split(xyz,tmp,RS)
        for (i in tmp) {
            var[tmp[i]]
        }
        for (idx in var) {
            print "<" idx ">"
        }
    }
'
<f g h>
<a b>

【讨论】:

    【解决方案3】:

    您可以将字符串存储在变量中,然后使用split function 将其转换为数组,考虑以下简单示例,让file1.txt 内容为

    A B C
    D E F
    G H I
    

    file2.txt 内容是

    1
    3
    2
    

    然后

    var1=$(awk '{print $1}' file1.txt)
    awk -v var1="$var1" 'BEGIN{split(var1,arr)}{print "First column value in line number",$1,"is",arr[$1]}' file2.txt
    

    给出输出

    First column value in line number 1 is A
    First column value in line number 3 is G
    First column value in line number 2 is D
    

    说明:我存储第一个awk 命令的输出,然后将其用作第二个awk 命令中split 函数的第一个参数。免责声明:此解决方案假定所有涉及的文件都具有符合默认 GNU AWK 行为的分隔符,即一个或多个空格始终是分隔符。

    (在 gawk 4.2.1 中测试)

    【讨论】:

      猜你喜欢
      • 2023-03-30
      • 2011-03-07
      • 1970-01-01
      • 1970-01-01
      • 2017-11-01
      • 2021-08-13
      • 2014-04-29
      • 2022-01-18
      相关资源
      最近更新 更多