使用awk处理多个文件需要统计模式后变量的出现次数。如何在每个文件后停止阵列重置?

使用awk处理多个文件需要统计模式后变量的出现次数。如何在每个文件后停止阵列重置?

对于目录中具有特定扩展名的文件,我想计算模式后括号中任何变量的所有出现次数。每个文件可能包含多个记录/行上的模式。

到目前为止,我可以处理文件并将结果存储在数组中,但似乎数组会被我处理的每个新文件覆盖。如何保留数组值?

#!/bin/bash
for x in `find . $PROGFILES -name "*.fgl"`
do

    awk -f <(cat -  <<-'EOF'
        / envget | env-get | \"envget\" | \"env-get\" /  
        {
        gsub( /get-env/, "envget")              ;# removes hypens
        gsub( /.*envget/, " envget")
        gsub( "\\concat" ,"")       ;# removes concat
        gsub( "\\substring" , "")   ;# removes substring
        for (i = 1; i<= NF; i++) {
            if ( substr( $i, 1, 6) == "envget" ) {
                    lenofget = 8;
                } else {
                    lenofget = 0;
                }
                if ( lenofget != 0 ) {
                    gsub("\\envget" , "",$i)    ;#removes envget
                    gsub ( /\)\.*/, "",$i)      ;#removes everything after a closing parenthesis
                    gsub ( /\47/, "",$i)        ;#so used octal instead
                    gsub ( /\(/, "",$i)         ;#removes paraentheses
                    gsub ( /\"/, "",$i)         ;#removes double quotes
                    gsub ( /\,.*/, "",$i)       ;#removes everything after a , This is for any concat syntax
                    gsub ( /[\/].*/, "",$i)     ;#removes everything after a forward slash
                    narr[$i]++
                }
            }
        }
        END {
            for (y in narr) {
                printf("%s - %d\n",y, narr[y])

            }   
        }   
EOF
) $x
done

具有模式的文件中的典型记录/行是:

if envget("SYPSDATA") in {SPACES "."}
    set lf-path = "envget"('SYPSCTRL')
if env-get(concat("LOG_PRINTER",service-centre)) != spaces
trconcat(env-get("TMPDIR"),"/ps_xxx_temp.psv")
envget(substring(ws-envprinter1,1,strlen(ws-envprinter1)))
      set lf-path = "envget"('SYPSCTRL')
            display bitmap concat(envget('BTS')'/images/repedge.gif') @19,44

鉴于有多个文件具有多个模式匹配行,我希望得到这样的输出(其中数字是每个文件中找到的总数)。

BTS - 15
LOG_PRINTER - 7
ws-envprinter1 - 3
SYPSDATA - 120
TMPDIR - 130
SYPSCTRL - 200

答案1

你正在做的

对于“查找”中的 x。 $PROGFILES -名称“*.fgl”`
    awk(awk_程序)$x
完毕
awk它为每个文件启动一个新进程。为什么?做就是了

awk(awk_程序)*.fgl "$PROGFILES"/*.fgl

除非您需要搜索子目录。如果您确实需要搜索子目录,则只会稍微复杂一些:

寻找 。 “$PROGFILES”-名称“*.fgl”-exec awk(awk_程序){} +

笔记:

  • 您应该始终引用 shell 变量(例如"$PROGFILES""$x"),除非您有充分的理由不这样做,并且您确定您知道自己在做什么。
  • 你不需要使用cat这样的。您可以将 awk 程序放在引号中:

    awk '
            / envget | env-get | \"envget\" | \"env-get\" /  
            {
                gsub( /get-env/, "envget")
            }
        ' "$x"
    

    或者你可以将它放入一个文件中并说 .awk -f (awk_program_file)

  • 上述两种方法都不能保证获得总计数,因为命令行的大小有(非常大的)限制。如果您有如此多的文件,其名称的组合长度超过了该限制, find将调用多个awk进程来覆盖所有名称,并且您将返回到获得不完整的计数。处理这个问题的一种方法是收集各个awk运行的输出并将它们组合起来。

答案2

您的尝试中有几个错误(不是想居高临下,我们都是来学习的!)。

每次调用都是一个不同的进程,具有自己的内存空间,因此一旦处理了文件,下一次调用不保留数组的值awk是正常的。awk您需要在每次迭代时输出计数for,并在最后执行一个额外的步骤来总结所有内容。最简单的方法是将所有这些添加到文件中:

#!/bin/bash

echo "" > "$HOME/tmp_count.txt"

for x in `find . $PROGFILES -name "*.fgl"`
do

    awk '
        /env-?get/  {
          for (i = 1; i<= NF; i++) {
            if ($i ~ /env-?get/) {
              a = gensub(/.*env-?get\"?\((concat\(|substring\()?(\"|\47)?([a-zA-Z0-9\-_]*)(\"|\47)? *(\)|,)?.*/, "\\3", $i)
              arr[a]++
            }
          }
        }
        END {
            for (y in arr) {
                printf("%s %d\n",y, arr[y])
            }   
        }
        ' "$x" >> "$HOME/tmp_count.txt"
done

awk '{arr[$1] += $2}END{for (key in arr) {printf("%s - %d\n", key, arr[key])}}' < "$HOME/tmp_count.txt"

另外,您的方法似乎并不总是有效,所以我尝试在 a 中使用一个大的正则表达式gensub,它至少适用于您提供的示例。我不是正则表达式向导,所以它可能会在某些情况下中断。尝试一下,让我知道它是否适合您!

相关内容