我有一个 csv(其中第一列和第二列用“,”分隔)文件,如下所示:
Column1,Column2
4e,info1
4t,info2
45t,info3
3,info4
我想获得 4 个不同的文件,每一行一个,文件名取自 Column1,内容取自 Column2。
我的预期输出是:
文件名1 =4e.smi
info1
文件名2 =4t.smi
info2
文件名3 =45t.smi
info3
文件名4 =3.smi
info4
我认为我可以生成两个不同的变量,一个与第一列相关,另一个与第二列相关,并使用该变量创建新文件,对所有行循环执行此操作。但我尝试了这个命令行,但它不起作用:
while IFS=',' read -r name smile; do write "$smile" "$name".smi; done < InputFIle.txt
有人可以帮我解决这个挑战吗?
谢谢。
答案1
write
除了该命令不是您要使用的命令这一事实之外,您的循环几乎是正确的。相反,请printf
与重定向一起使用。您还需要确保跳过初始行,我们可以通过多种不同的方式来做到这一点。下面,我使用的是tail -n +2
,但您也可以使用sed 1d
.
tail -n +2 InputFIle.txt |
while IFS=, read -r name string; do
printf '%s\n' "$string" >"$name".smi
done
请注意,这假设文件名仅写入一次,就好像两行或多行第一列中具有相同的值一样,后面的行将导致已写入文件的数据被覆盖。因此,您可能想要更改>
为>>
附加到输出文件。如果您想运行代码两次(否则您将在输出中获得重复的数据),则此更改将要求您额外删除这些文件。
awk
一种可能更有效的方法是像这样使用:
awk -F, 'NR > 1 { print $2 >($1 ".smi") }' InputFIle.txt
这会将第二个逗号分隔字段打印到第一个字段给出的文件名。它通过NR
针对 1 进行测试(到目前为止读取的记录数)来跳过第一行。
这不会遇到与 shell 循环相同的问题。输出文件将在第一个文件后被截断(清空或创建)print
,然后附加后续输出。
如果某些行上有更多字段,awk
则需要修改变体以打印除第一个字段之外的所有字段:
awk -F, 'NR > 1 { name = $1; sub("[^,]*,",""); print >(name ".smi") }' InputFIle.txt
这会将第一个字段保存在单独的变量 中,name
然后使用 从原始行中删除该字段sub()
。然后它将剩余的行打印到文件中。
答案2
无论需要生成多少个输出文件,使用任何 tail+sort+awk 都可以有效地工作:
tail -n +2 file | sort | awk -F, '$1!=prev{close(out); out=$1".smi"; prev=$1} {print $2 > out}'
如果您没有随时关闭输出文件,那么大多数 awk 可能会遇到“打开文件过多”错误,或者 GNU awk 会显着减慢速度,因为它会尝试为您管理打开/关闭文件,一旦超过了我见过的低于 20 个输出文件的阈值。我们首先进行排序,这样我们就不必每次在输入中看到重复的 $1 时都打开/关闭输出文件。