如何使用" for"在awk程序?

时间:2014-11-07 21:18:15

标签: bash awk

我有一个任务:编写一个脚本,用于存储在文件中的整数求和。形成一个调用脚本:例如:sum a.txt 3 4

输入文件可以包含多个整数列。各个列由speces或tabs分隔。该脚本应该将适当的列相加并将结果写入stdout。因此,当我们有sum a.txt 3 4时,我们需要添加第三和第四列文件的编号。

所以我这样做:

#!/bin/bash
array1=( "$@" )
let LA=${#array1[@]}-1
awk '{for(i=1;i<=$LA;i++)y+=$'${array1[i]}'; print y}' a.txt

但我有一个错误:awk: : 1unexpected character '.'

请帮助有另一种方法来添加过程调用脚本中给出其编号的列数吗?

在这个论坛上得到了以下答案:

#!/bin/bash
awk -v col1=$2 -v col2=$3 '{sum1 += $col1; sum2 += $col2} END{print sum1,sum2}' $1

但是,如果我们不知道过程示例中将给出的列数量,那该怎么办:./sum a.txt 2 3 ... n(也许我需要使用for但是如何?)

6 个答案:

答案 0 :(得分:0)

你不需要AWK。 Bash就足够了:

$ cat data.txt
1 2 3 4 5 6
7 8 9 10 11 12
13 14 15 16 17 18
$ ./csum.sh $(seq 0 5) < data.txt
21 24 27 30 33 36
$ cat csum.sh
#! /bin/bash
SUM=()
S=0
for N in "$@"; do
  SUM[$S]=0
  ((S++))
done
while IFS=$'\n' read LINE ; do
  COLS=($LINE)
  S=0
  for C in "$@"; do
    SUM[$S]=$(expr ${SUM[S]} + ${COLS[C]})
    ((S++))
  done
done
echo ${SUM[*]}

答案 1 :(得分:0)

好的,我看了你更新的问题,我想你想要:

file: my.awk

#!/bin/bash
eval "awk '{print \$$2+\$$3}' $1"   

这会将您传入的列添加到一起。

E.g. cat num.txt
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1


$>my.awk num.txt 2 3
7
7
7
7
7
7
7
7
7
7

如果你想要更多列,你可以从命令行中选择它们,以添加更多列 - 以下添加列4,5,1,3,1,如下所示:

$> my.awk num.txt 4 5+\$1+\$3+\$1
16
16
16
16
16
16
16
16
16
16

值为16,因为列4,5,1,3,1的相应值是(2 + 1 + 5 + 3 + 5)= 16.您可以按照您想要的任何顺序追加任意数量的列它会将它们全部加在一起。

或者如果你想要这里是一个更简单的版本,你只需输入文件名后跟你想要加的列:

file: my2.awk

#!/bin/bash
eval "awk '{print $(echo "${*:2}" | sed -r 's/\b[0-9]*\b/\$&+/g;s/\+$//')}' $1"

$> my2.awk num.txt 1 5 2 3
13
13
13
13
13
13
13
13
13
13

答案 2 :(得分:0)

Shell变量未在single quoted strings中展开,与awk程序一样,您必须使用option -v后跟var=value设置变量。

#!/bin/sh
if [ "$#" < 2 ]; then
    echo "$0:" not enough arguments
    exit 1;
fi
FILE=$1;
shift 1;
awk -v A="$*" 'BEGIN { N = split(A,a," "); }\
               { y = 0; for(i = 1; i <= N; i++) y += $(a[i]); print y; }' "$FILE"

shift 1改变左边的论点1. ex。 $2 -> $1

BEGIN { N = split(A,a," "); } split将字符串A按空格放入数组a

{y = 0; for(i = 1; i <= N; i++) y += $(a[i]); print y; }y,将每个编号为a[i]的字段添加到y,最后打印y;

但是,您的脚本非常简单,只需使用awk

#!/usr/bin/awk
BEGIN {
    if (ARGC < 2) {
        print $0, ": not enough arguments";
        exit 1;
    }
    for (i = 2; i < ARGC; i++) {
        a[i] = ARGV[i] + 0;
        delete ARGV[i];
    }
}
{
    tmp = 0;
    for (i in a) {
        tmp += $(a[i]);
    }
    print tmp;
}

a[i] = ARGV[i] + 0a[i]设置为数字 ARGV[i] + 0

delete ARGV[i]删除ARGV[i],这可以防止ARGV[i]被视为要打开的文件名。

值得注意的是,awk的大多数版本都比bash小一些。

答案 3 :(得分:0)

这不是完全标记的解决方案。 bash脚本test.sh看起来像这样:

awk '{for(i=2;i<ARGC;++i)s[i]+=$ARGV[i]}ENDFILE{for(i=2;i<ARGC;++i)printf("%d ",s[i]);print"";exit}' "$@"

输入

1 2 3 4 5 6
7 8 9 10 11 12
13 14 15 16 17 18

命令行:

test.sh inputfile 3 4 5

输出:

27 30 33

类似于ARGC [1]的C包含输入文件的名称(在这种情况下,它与FILENAME相同),1以上的其他包含数字。 ENDFILE必须存在,因为没有3这样的文件。但在此之前它会打印相加的列值。

参考ceving的评论,纯粹的版本就足够了(如果输入文件很小。少于cca 100行)。在这种情况下,不需要外部工具。

file="$1"; shift
cols=($@)
while read -r t; do
  arr=($t)
  for((i=0;i<$#;++i)){ ((sum[i]+=arr[cols[i]-1]));}
done < "$file"
echo "${sum[@]}"

命令行和输出是相同的。

答案 4 :(得分:0)

您忘记提供样本输入和预期输出供我们测试,因此这可能适用于您,也可能不适合您,但如果我正确理解您的问题,这是正确的方法:

file="$1"
shift
awk -v fldNrs="$*" '
BEGIN { split(fldNrs,flds) }
{ sum=0; for (idx in flds) sum += $(flds[idx]); print sum }
' "$file"

答案 5 :(得分:0)

声明

我的脚本 在命令行中忽略 列号重复。

如果OP需要不同的行为,可以使用其他优秀的答案来实现其他可能的重复处理。

% cat sum_cols.sh 

file="$1";shift

# next 3 lines, we build, eg, beg="BEGIN{a[3]=0;a[5]=0;a[6]=0;}"
beg="BEGIN{"
for n in "$@"; do beg=$(printf "%sa[%d]=0;" "$beg" "$n") ; done
beg="$beg""}"

# The awk program below  is based on the "for(i in a)" statement
# "for(i in a) is a loop over the indexes of the array "a"
# we loop for every line to accumulate the sum
#         and at the end to print our results

awk "$beg {for(i in a) a[i]+=\$i} END{for(i in a) print i, a[i]}" $file

% cat integers
1 2 3 4 5 6 7 8 9 10 
11 12 13 14 15 16 17 18 19 20 
21 22 23 24 25 26 27 28 29 30 
31 32 33 34 35 36 37 38 39 40 
41 42 43 44 45 46 47 48 49 50 
51 52 53 54 55 56 57 58 59 60 
61 62 63 64 65 66 67 68 69 70 
71 72 73 74 75 76 77 78 79 80 
81 82 83 84 85 86 87 88 89 90 
91 92 93 94 95 96 97 98 99 100 
101 102 103 104 105 106 107 108 109 110 
111 112 113 114 115 116 117 118 119 120 
% sh sum_cols.sh integers 2 5 7
2 684
5 720
7 744
% # EDIT Note that a repeated column number in the command line is ignored 
% sh sum_cols.sh integers 2 5 2 7
2 684
5 720
7 744
% 

修改

如果脚本的用户在命令行中重复列号,原始问题未指定该怎么做,如上面的sh col.sh integers 2 5 2 7所示。

我的看法是忽略重复,而在其他解决方案(例如,Ed Morton's或TrueY&#39;)中,n出现列号j结果n*sum(matrix(i,j), i)

我必须承认,在我的情况下,选择一种特定的行为是偶然的......