如何在不知道电子邮件地址的情况下按 CSV 电子邮件分组?
How to group by email in CSV without knowing the email addresses?
我有一个包含不同列的 CSV,其中一列包含电子邮件。该脚本应根据列生成一个字符串,并将其作为附加列添加到 CSV 中。目前,脚本遍历每一行并形成字符串。但我想要的是,该脚本只构建具有相同电子邮件地址的行字符串。棘手的是,我不知道不同的电子邮件地址,所以我无法对其进行硬编码并对其进行动态分组。
构建它的方法是什么?
这是我的初始 CSV:
42342;home;2020-01-12;2020-01-13;test@test.com
45235;home;2020-04-12;2020-04-13;test@test.com
68787;photo;2020-05-12;2020-05-13;email@test.com
68787;test;2020-05-12;2020-05-13;moritz@test.com
68787;test;2020-05-12;2020-05-13;moritz@test.com
68787;test;2020-05-12;2020-05-13;moritz@test.com
CSV 最后的样子:
42342;home;2020-01-12;2020-01-13;test@test.com;home 2020-01-12_2020-01-13 && home 2020-04-12_2020-04-13
45235;home;2020-04-12;2020-04-13;test@test.com;home 2020-01-12_2020-01-13 && home 2020-04-12_2020-04-13
68787;photo;2020-05-12;2020-05-13;email@test.com;photo 2020-05-12_2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13
这是我的 bash 脚本:
getPhotosCommand(){
com=""
header="ID;DIR;START_DATE;END_DATE"
while read line; do
IFS=';' read -r -a array <<< "$line"
dir=${array[2]}
start_date=${array[3]}
end_date=${array[4]}
newCom="$dir $start_date_$end_date && "
com=$com$newCom
done < $file_new_photos
echo $com
}
为了解析一个文件并输出另一个 post 处理过的文件,我会使用 awk.
Awk 有 associatives array(a.k.a。在大多数语言中都有映射),它可能会帮助您实现这一目标。
简而言之,您的 awk 脚本应该:
- 在开始块中:初始化关联数组,处理一些参数以在结束块中设置输出。
- 在您的“行块”中:将当前行插入关联数组,并将电子邮件作为关键字
- 在你的结束块中:生成循环遍历关联数组的新文件。
玩得开心=)
保留行的原始顺序的两次通过 awk 解决方案。
第一遍用于构建第 6 个字段(针对每封电子邮件),第二遍用于将它们附加到相应的行。
awk '
BEGIN {FS = OFS = ";"}
{
if (NR == FNR) {
str = " " " "
if (arr[]) {
arr[] = arr[] " && " str
} else {
arr[] = str
}
} else {
print [=10=], arr[]
}
}
' file.scsv file.scsv
输出:
42342;home;2020-01-12;2020-01-13;test@test.com;home 2020-01-12 2020-01-13 && home 2020-04-12 2020-04-13
45235;home;2020-04-12;2020-04-13;test@test.com;home 2020-01-12 2020-01-13 && home 2020-04-12 2020-04-13
68787;photo;2020-05-12;2020-05-13;email@test.com;photo 2020-05-12 2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13
假设这些行按电子邮件地址分组并且文件没有空行,这个 bash
脚本可能就是您要执行的操作。
#!/bin/bash
cnt=0
while IFS= read -r line; do
email=${line##*;}
if [ "$email" = "$prev_email" ]; then
grpline[cnt++]=$line
else
if ((cnt)); then
tail=
for ((i = 0; i < cnt; ++i)); do
IFS=';' read -ra fld <<< "${grpline[i]}"
tail+=" && ${fld[1]} ${fld[2]}_${fld[3]}"
done
tail=${tail:4}
for ((i = 0; i < cnt; ++i)); do
printf '%s;%s\n' "${grpline[i]}" "$tail"
done
fi
prev_email=$email
grpline[0]=$line
cnt=1
fi
done < <(cat file; echo)
如果文件未按电子邮件地址分组,则将 cat file
替换为 sort -t \; -k5 file
。
我有一个包含不同列的 CSV,其中一列包含电子邮件。该脚本应根据列生成一个字符串,并将其作为附加列添加到 CSV 中。目前,脚本遍历每一行并形成字符串。但我想要的是,该脚本只构建具有相同电子邮件地址的行字符串。棘手的是,我不知道不同的电子邮件地址,所以我无法对其进行硬编码并对其进行动态分组。
构建它的方法是什么?
这是我的初始 CSV:
42342;home;2020-01-12;2020-01-13;test@test.com
45235;home;2020-04-12;2020-04-13;test@test.com
68787;photo;2020-05-12;2020-05-13;email@test.com
68787;test;2020-05-12;2020-05-13;moritz@test.com
68787;test;2020-05-12;2020-05-13;moritz@test.com
68787;test;2020-05-12;2020-05-13;moritz@test.com
CSV 最后的样子:
42342;home;2020-01-12;2020-01-13;test@test.com;home 2020-01-12_2020-01-13 && home 2020-04-12_2020-04-13
45235;home;2020-04-12;2020-04-13;test@test.com;home 2020-01-12_2020-01-13 && home 2020-04-12_2020-04-13
68787;photo;2020-05-12;2020-05-13;email@test.com;photo 2020-05-12_2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13 && test 2020-05-12_2020-05-13
这是我的 bash 脚本:
getPhotosCommand(){
com=""
header="ID;DIR;START_DATE;END_DATE"
while read line; do
IFS=';' read -r -a array <<< "$line"
dir=${array[2]}
start_date=${array[3]}
end_date=${array[4]}
newCom="$dir $start_date_$end_date && "
com=$com$newCom
done < $file_new_photos
echo $com
}
为了解析一个文件并输出另一个 post 处理过的文件,我会使用 awk.
Awk 有 associatives array(a.k.a。在大多数语言中都有映射),它可能会帮助您实现这一目标。
简而言之,您的 awk 脚本应该:
- 在开始块中:初始化关联数组,处理一些参数以在结束块中设置输出。
- 在您的“行块”中:将当前行插入关联数组,并将电子邮件作为关键字
- 在你的结束块中:生成循环遍历关联数组的新文件。
玩得开心=)
保留行的原始顺序的两次通过 awk 解决方案。 第一遍用于构建第 6 个字段(针对每封电子邮件),第二遍用于将它们附加到相应的行。
awk '
BEGIN {FS = OFS = ";"}
{
if (NR == FNR) {
str = " " " "
if (arr[]) {
arr[] = arr[] " && " str
} else {
arr[] = str
}
} else {
print [=10=], arr[]
}
}
' file.scsv file.scsv
输出:
42342;home;2020-01-12;2020-01-13;test@test.com;home 2020-01-12 2020-01-13 && home 2020-04-12 2020-04-13
45235;home;2020-04-12;2020-04-13;test@test.com;home 2020-01-12 2020-01-13 && home 2020-04-12 2020-04-13
68787;photo;2020-05-12;2020-05-13;email@test.com;photo 2020-05-12 2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13
68787;test;2020-05-12;2020-05-13;moritz@test.com;test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13 && test 2020-05-12 2020-05-13
假设这些行按电子邮件地址分组并且文件没有空行,这个 bash
脚本可能就是您要执行的操作。
#!/bin/bash
cnt=0
while IFS= read -r line; do
email=${line##*;}
if [ "$email" = "$prev_email" ]; then
grpline[cnt++]=$line
else
if ((cnt)); then
tail=
for ((i = 0; i < cnt; ++i)); do
IFS=';' read -ra fld <<< "${grpline[i]}"
tail+=" && ${fld[1]} ${fld[2]}_${fld[3]}"
done
tail=${tail:4}
for ((i = 0; i < cnt; ++i)); do
printf '%s;%s\n' "${grpline[i]}" "$tail"
done
fi
prev_email=$email
grpline[0]=$line
cnt=1
fi
done < <(cat file; echo)
如果文件未按电子邮件地址分组,则将 cat file
替换为 sort -t \; -k5 file
。