如何在awk中编写循环而不会相互影响?

时间:2017-09-28 06:47:34

标签: awk

我的脚本输出一个价格(col $ 3)的购物清单(col $ 1) 然后它按类别(第2列)组合它们并添加每个类别的成本。

问题是,当我运行for循环时,它总是与原始杂货清单输出的第一行混淆。 我尝试在不同的开始端分离它们​​,但它不起作用。

这是我当前的代码,子类别的循环被注释掉了。

    #!/bin/awk
    BEGIN { FS="\t+"; OFS=" "; printf("%-30s %s\n", "Item","Cost") 
printf("%-30s %s\n", "====","====")}
{ printf("%-30s %s\n", $2, $3, 30) }


END {
}

#BEGIN {
#}

#NR==1{print;next}
#{a[$1]+=substr($3, 2)}


#END { for(i in a)printf("%-30s %s\n", i, a[i], 30)  
#}

输出样本:

Item                          Cost
====                          ======
Air freshener                 $10.60
Antiperspirant / Deodorant    $03.80
Apples                        $10.80
Asparagus                     $01.05
Avocados                      $08.25
BBQ sauce                     $08.55
Baby food                     $08.60
Baby wash                     $05.40

Subtotal by Category       
---------------------------
Alcohol             $ 76.10
Baby/Pet            $ 81.55
Baking              $ 54.15
Bread               $ 50.20
Canned goods        $ 55.60
Chips/Crackers      $ 53.65
Cleaner/Detergent   $ 46.75
Condiments          $ 92.40
Dairy               $ 46.30
Produce             $195.45
Soap/Sundry         $113.65
Spices              $ 89.40
===========================
Total               $955.20

输入样本:

Cleaner/Detergent   Air freshener   $10.60
Soap/Sundry Antiperspirant / Deodorant  $03.80
Produce Apples  $10.80
Produce Asparagus   $01.05
Produce Avocados    $08.25
Condiments  BBQ sauce   $08.55
Baby/Pet    Baby food   $08.60
Baby/Pet    Baby wash   $05.40
Bread   Bagels / Croissants $10.35
Baking  Baking powder / Soda    $07.85
Produce Bananas $02.90
Spices  Basil   $01.70
Soap/Sundry Bath soap / Hand soap   $06.75
Cleaner/Detergent   Bathroom cleaner    $04.55

2 个答案:

答案 0 :(得分:2)

GNU awk 解决方案,可以一气呵成:

$ cat tst.awk
#!/bin/awk
BEGIN {
    FS="\t+";OFS="   "
}
NF==3{                                               # check if there are fields
    amount=substr($3,2)
    l1=length($1); l2=length($2)                     # save max length of 1st col
    col1=(l1<l2)?(col1<l2?l2:col1):(col1<l1?l1:col1) # in col1     

    a[$2]=($2 in a? a[$2]+amount : amount)           # item as key, amount as value
    total=total+amount                               # save total, for col2 width
    c[$1]=c[$1]+amount                               # save sum per category
}
END {
    col2=length(total)                               # determine col2 width

    printf "%-*s%s%s\n", col1, "Item", OFS, "Cost"   # print header item table
    printf "%s%s%s\n", separator("=",col1), OFS, separator("=",col2+2) 

    PROCINFO["sorted_in"]="@ind_str_asc"             # define sorting order

    for(i in a)                                      # print Item table
        printf "%-*s%s$ %*.2f\n", col1, i, OFS, col2, a[i]    

    printf "\n%s\n", "Subtotal by Category"          # print header category table
    print separator("-", col1+col2+length(OFS)+2)    # "+2" because of "$ "

    for (j in c)                                     # print category table
        printf "%-*s%s$ %*.2f\n", col1, j, OFS, col2, c[j]    

    print separator("=", col1+col2+length(OFS)+2)    # "+2" because of "$ "
    printf "%-*s%s$ %*.2f\n", col1, "Total", OFS, col2, total # print total
}
func separator(sep,n){                               # function for generating
    s=sprintf("%*s",n,""); gsub(/ /,sep,s); return s # separator line
}

使用提供的输入生成:

$ awk -f tst.awk input.txt
Item                         Cost
==========================   =======
Air freshener                $ 10.60
Antiperspirant / Deodorant   $  3.80
Apples                       $ 10.80
Asparagus                    $  1.05
Avocados                     $  8.25
BBQ sauce                    $  8.55
Baby food                    $  8.60
Baby wash                    $  5.40
Bagel / Croissants           $ 10.35
Baking powder / Soda         $  7.85
Bananas                      $  2.90
Basil                        $  1.70
Bath soap / Hand soap        $  6.75
Bathroom cleaner             $  4.55

Subtotal by Category
------------------------------------
Baby/Pet                     $ 14.00
Baking                       $  7.85
Bread                        $ 10.35
Cleaner/Detergent            $ 15.15
Condiments                   $  8.55
Produce                      $ 23.00
Soap/Sundry                  $ 10.55
Spices                       $  1.70
====================================
Total                        $ 91.15

答案 1 :(得分:1)

我强烈建议你先获取数据(使用GNU awk for gensub()和\ S / \ s-,其他awks使用变量+ [g] sub()+ [:space:]):

$ cat tst.awk
BEGIN { OFS="\t" }
{
    cat = $1
    item = gensub(/^\S+\s+|\s+\S+$/,"","g")
    cost = gensub(/\$/,"",1,$NF)
    print "Item", item, cost
    subTot[cat] += cost
    tot += cost
}
END {
    for (cat in subTot) {
        print "Category", cat, subTot[cat]
    }
    print "Total", "Total", tot
}

$ awk -f tst.awk file
Item    Air freshener   10.60
Item    Antiperspirant / Deodorant      03.80
Item    Apples  10.80
Item    Asparagus       01.05
Item    Avocados        08.25
Item    BBQ sauce       08.55
Item    Baby food       08.60
Item    Baby wash       05.40
Item    Bagels / Croissants     10.35
Item    Baking powder / Soda    07.85
Item    Bananas 02.90
Item    Basil   01.70
Item    Bath soap / Hand soap   06.75
Item    Bathroom cleaner        04.55
Category        Spices  1.7
Category        Baby/Pet        14
Category        Condiments      8.55
Category        Produce 23
Category        Soap/Sundry     10.55
Category        Baking  7.85
Category        Bread   10.35
Category        Cleaner/Detergent       15.15
Total   Total   91.15

然后您可以将其读入电子表格和/或编写您喜欢的任何格式化脚本以提供布局,例如:

$ awk -f tst.awk file |
  awk 'BEGIN{FS=OFS="\t"} $1!=p{print "_" ORS $1, "Cost" ORS "====", "===="; p=$1} {print $2, "$"$3}' |
  column -s$'\t' -t
_
Item                        Cost
====                        ====
Air freshener               $10.60
Antiperspirant / Deodorant  $03.80
Apples                      $10.80
Asparagus                   $01.05
Avocados                    $08.25
BBQ sauce                   $08.55
Baby food                   $08.60
Baby wash                   $05.40
Bagels / Croissants         $10.35
Baking powder / Soda        $07.85
Bananas                     $02.90
Basil                       $01.70
Bath soap / Hand soap       $06.75
Bathroom cleaner            $04.55
_
Category                    Cost
====                        ====
Spices                      $1.7
Baby/Pet                    $14
Condiments                  $8.55
Produce                     $23
Soap/Sundry                 $10.55
Baking                      $7.85
Bread                       $10.35
Cleaner/Detergent           $15.15
_
Total                       Cost
====                        ====
Total                       $91.15