我需要在表格的最后一列之后添加一个带有(序号)数字的新列。
输入和输出文件都是.CSV表。
传入表有超过500 000行(行)数据和7列,例如https://www.dropbox.com/s/g2u68fxrkttv4gq/incoming_data.csv?dl=0
传入的CSV表格(这只是一个例子,所以" |"和" - "为了清楚起见,这里是:)
| id | Name |
-----------------
| 1 | Foo |
| 1 | Foo |
| 1 | Foo |
| 4242 | Baz |
| 4242 | Baz |
| 4242 | Baz |
| 4242 | Baz |
| 702131 | Xyz |
| 702131 | Xyz |
| 702131 | Xyz |
| 702131 | Xyz |
结果CSV(这只是一个例子,所以" |"和" - "为了清楚起见在这里):
| id | Name | |
--------------------------
| 1 | Foo | 1 |
| 1 | Foo | 2 |
| 1 | Foo | 3 |
| 4242 | Baz | 1 |
| 4242 | Baz | 2 |
| 4242 | Baz | 3 |
| 4242 | Baz | 4 |
| 702131 | Xyz | 1 |
| 702131 | Xyz | 2 |
| 702131 | Xyz | 3 |
| 702131 | Xyz | 4 |
第一列是ID,因此我尝试将所有具有相同ID的行分组并迭代它们。脚本(说实话,我不知道bash脚本):
FILE=$PWD/$1
# Delete header and extract IDs and delete non-unique values. Also change \n to ♥, because awk doesn't properly work with it.
IDS_ARRAY=$(awk -v FS="|" '{for (i=1;i<=NF;i++) if ($i=="\"") inQ=!inQ; ORS=(inQ?"♥":"\n") }1' $FILE | awk -F'|' '{if (NR!=1) {print $1}}' | awk '!seen[$0]++')
for id in $IDS_ARRAY; do
# Group $FILE by $id from $IDS_ARRAY.
cat $FILE | grep $id >> temp_mail_group.csv
ROW_GROUP=$PWD/temp_mail_group.csv
# Add a number after each row.
# NF+1 — add a column after last existing.
awk -F'|' '{$(NF+1)=++i;}1' OFS="|", $ROW_GROUP >> "numbered_mails_$(date +%Y-%m-%d).csv"
rm -f $PWD/temp_mail_group.csv
done
现在这个脚本几乎像我想要的那样工作,除了它认为(例如)ID 2834和772834是相同的。
UPD:虽然我将一个答案标记为已批准,但它没有为具有相同ID的某些记录组分配正确的值(现在我没有看到模式)。
答案 0 :(得分:3)
您可以在一个脚本中执行所有操作:
gawk 'BEGIN { FS="|"; OFS="|";}
/^-/ {print; next;}
$2 ~ /\s*id\s*/ {print $0,""; next;}
{print "", $2, $3, ++a[$2];}
'
$1
是输入中第一个|
之前的空字段。我使用空输出列""
来获取前导|
。
技巧是++a[$2]
,它接受每一行中的第二个字段(= ID列)并在关联数组a
中查找它。如果没有条目,则结果为0
。通过预先递增,我们从1
开始,每次ID重新出现时添加1
。
答案 1 :(得分:2)
一种awk方式
不考虑延长的虚线。
awk 'NR>2{$0=$0 (++a[$2])"|"}1' file
| id | Name |
-------------
| 1 | Foo |1|
| 1 | Foo |2|
| 1 | Foo |3|
| 42 | Baz |1|
| 42 | Baz |2|
| 42 | Baz |3|
| 42 | Baz |4|
| 70 | Xyz |1|
| 70 | Xyz |2|
| 70 | Xyz |3|
| 70 | Xyz |4|
答案 2 :(得分:2)
每次在shell中编写循环只是为了操作文本时,你的方法都是错误的。发明shell的人也发明了awk for shell来调用操作文本 - 不要让他们失望: - )。
$ awk '
BEGIN{ w = 8 }
{
if (NR==1) {
val = sprintf("%*s|",w,"")
}
else if (NR==2) {
val = sprintf("%*s",w+1,"")
gsub(/ /,"-",val)
}
else {
val = sprintf(" %-*s|",w-1,++cnt[$2])
}
print $0 val
}
' file
| id | Name | |
----------------------
| 1 | Foo | 1 |
| 1 | Foo | 2 |
| 1 | Foo | 3 |
| 42 | Baz | 1 |
| 42 | Baz | 2 |
| 42 | Baz | 3 |
| 42 | Baz | 4 |
| 70 | Xyz | 1 |
| 70 | Xyz | 2 |
| 70 | Xyz | 3 |
| 70 | Xyz | 4 |
答案 3 :(得分:0)
这是使用纯Bash的方法:
inputfile=$1
prev_id=
while IFS= read -r line ; do
printf '%s' "$line"
IFS=$'| \t\n' read t1 id name t2 <<<"$line"
if [[ $line == -* ]] ; then
printf '%s\n' '---------'
elif [[ $id == 'id' ]] ; then
printf ' Number |\n'
else
if [[ $id != "$prev_id" ]] ; then
id_count=0
prev_id=$id
fi
printf '%2d |\n' "$(( ++id_count ))"
fi
done <"$inputfile"