使用 AWK 将 FILENAME 打印为 CSV
Use AWK to print FILENAME to CSV
我有一个小脚本来比较一堆 CSV 文件中的某些列。
它工作正常,但有些事情让我很烦恼。
代码如下:
FILES=./*
for f in $FILES
do
cat -v $f | sed "s/\^A/,/g" > op_tmp.csv
awk -F, -vOFS=, 'NR == 1{next} =="T"{t[]+=;n[]} =="A"{a[]+=;n[]} =="C"{c[]+=;n[]} =="R"{r[]+=;n[]} =="P"{p[]+=;n[]} END{ for (i in n){print i "|" "A" "|" a[i]; print i "|" "C" "|" c[i]; print i "|" "R" "|" r[i]; print i "|" "P" "|" p[i]; print i "|" "T" "|" t[i] "|" (t[i]==a[i]+c[i]+r[i]+p[i] ? "ERROR" : "MATCHED")} }' op_tmp.csv >> output.csv
rm op_tmp.csv
done
简单说明一下:
我得到了目录中的所有文件,然后我使用 CAT 替换管道 | 的除数 ^A。
然后我使用 awk onliner 比较我需要的列并将结果打印到 output.csv.
但现在我想在每个循环之前打印文件名。
我尝试在同一行中使用 cat sed 和 awk 并打印 $FILENAME,但它不起作用:
cat -v $f | sed "s/\^A/,/g" | awk -F, -vOFS=, 'NR == 1{next} =="T"{t[]+=;n[]} =="A"{a[]+=;n[]} =="C"{c[]+=;n[]} =="R"{r[]+=;n[]} =="P"{p[]+=;n[]} END{ for (i in n){print i "|" "A" "|" a[i]; print i "|" "C" "|" c[i]; print i "|" "R" "|" r[i]; print i "|" "P" "|" p[i]; print i "|" "T" "|" t[i] "|" (t[i]==a[i]+c[i]+r[i]+p[i] ? "ERROR" : "MATCHED")} }' > output.csv
有人可以帮忙吗?
您可以更好地重写整个脚本,但假设它能满足您的要求,现在只需添加
echo $f >> output.csv
awk 调用之前。
如果你想在每个 awk 输出行中添加文件名,你必须将它作为参数传递,即
awk ... -v fname="$f" '{...; print fname... etc
重写:
for f in ./*; do
awk -F '\x01' -v OFS="|" '
BEGIN {
letter[1]="A"; letter[2]="C"; letter[3]="R"; letter[4]="P"; letter[5]="T"
letters["A"] = letters["C"] = letters["R"] = letters["P"] = letters["T"] = 1
}
NR == 1 {next}
in letters {
count[,] +=
seen[]
}
END {
print FILENAME
for (i in seen) {
sum = 0
for (j=1; j<=4; j++) {
print i, letter[j], count[letter[j],i]
sum += count[letter[j],i]
}
print i, "T", count["T",i], (count["T",i] == sum ? "ERROR" : "MATCHED")
}
}
' "$f"
done > output.csv
备注:
- 只要文件名中包含 space,您迭代文件的方法就会失效
- 尽量减少重复。
- 换行是免费的,使用它们来提高可读性
- 改进您的变量名称
i
、n
等 -- 这里 "letter" 和 "letters" 可以使用改进来保留一些 含义关于那些符号。
- awk 有一个
FILENAME
变量(这是您问题的实际答案)
- awk 将
\x01
理解为 Ctrl-A——我假设这是输入文件中的字段分隔符
- 定义您将实际使用的输出字段分隔符
如果您有 GNU awk(版本 ???),您可以使用 ENDFILE
块并完全取消 shell for
循环:
gawk -F '\x01' -v OFS="|" '
BEGIN {...}
FNR == 1 {next}
in letters {...}
ENDFILE {
print FILENAME
for ...
# clean up the counters for the next file
delete count
delete seen
}
' ./* > output.csv
我有一个小脚本来比较一堆 CSV 文件中的某些列。 它工作正常,但有些事情让我很烦恼。
代码如下:
FILES=./*
for f in $FILES
do
cat -v $f | sed "s/\^A/,/g" > op_tmp.csv
awk -F, -vOFS=, 'NR == 1{next} =="T"{t[]+=;n[]} =="A"{a[]+=;n[]} =="C"{c[]+=;n[]} =="R"{r[]+=;n[]} =="P"{p[]+=;n[]} END{ for (i in n){print i "|" "A" "|" a[i]; print i "|" "C" "|" c[i]; print i "|" "R" "|" r[i]; print i "|" "P" "|" p[i]; print i "|" "T" "|" t[i] "|" (t[i]==a[i]+c[i]+r[i]+p[i] ? "ERROR" : "MATCHED")} }' op_tmp.csv >> output.csv
rm op_tmp.csv
done
简单说明一下: 我得到了目录中的所有文件,然后我使用 CAT 替换管道 | 的除数 ^A。 然后我使用 awk onliner 比较我需要的列并将结果打印到 output.csv.
但现在我想在每个循环之前打印文件名。 我尝试在同一行中使用 cat sed 和 awk 并打印 $FILENAME,但它不起作用:
cat -v $f | sed "s/\^A/,/g" | awk -F, -vOFS=, 'NR == 1{next} =="T"{t[]+=;n[]} =="A"{a[]+=;n[]} =="C"{c[]+=;n[]} =="R"{r[]+=;n[]} =="P"{p[]+=;n[]} END{ for (i in n){print i "|" "A" "|" a[i]; print i "|" "C" "|" c[i]; print i "|" "R" "|" r[i]; print i "|" "P" "|" p[i]; print i "|" "T" "|" t[i] "|" (t[i]==a[i]+c[i]+r[i]+p[i] ? "ERROR" : "MATCHED")} }' > output.csv
有人可以帮忙吗?
您可以更好地重写整个脚本,但假设它能满足您的要求,现在只需添加
echo $f >> output.csv
awk 调用之前。
如果你想在每个 awk 输出行中添加文件名,你必须将它作为参数传递,即
awk ... -v fname="$f" '{...; print fname... etc
重写:
for f in ./*; do
awk -F '\x01' -v OFS="|" '
BEGIN {
letter[1]="A"; letter[2]="C"; letter[3]="R"; letter[4]="P"; letter[5]="T"
letters["A"] = letters["C"] = letters["R"] = letters["P"] = letters["T"] = 1
}
NR == 1 {next}
in letters {
count[,] +=
seen[]
}
END {
print FILENAME
for (i in seen) {
sum = 0
for (j=1; j<=4; j++) {
print i, letter[j], count[letter[j],i]
sum += count[letter[j],i]
}
print i, "T", count["T",i], (count["T",i] == sum ? "ERROR" : "MATCHED")
}
}
' "$f"
done > output.csv
备注:
- 只要文件名中包含 space,您迭代文件的方法就会失效
- 尽量减少重复。
- 换行是免费的,使用它们来提高可读性
- 改进您的变量名称
i
、n
等 -- 这里 "letter" 和 "letters" 可以使用改进来保留一些 含义关于那些符号。 - awk 有一个
FILENAME
变量(这是您问题的实际答案) - awk 将
\x01
理解为 Ctrl-A——我假设这是输入文件中的字段分隔符 - 定义您将实际使用的输出字段分隔符
如果您有 GNU awk(版本 ???),您可以使用 ENDFILE
块并完全取消 shell for
循环:
gawk -F '\x01' -v OFS="|" '
BEGIN {...}
FNR == 1 {next}
in letters {...}
ENDFILE {
print FILENAME
for ...
# clean up the counters for the next file
delete count
delete seen
}
' ./* > output.csv