![使用 grep、awk 和 csv 从文本文件中提取信息](https://linux22.com/image/1542388/%E4%BD%BF%E7%94%A8%20grep%E3%80%81awk%20%E5%92%8C%20csv%20%E4%BB%8E%E6%96%87%E6%9C%AC%E6%96%87%E4%BB%B6%E4%B8%AD%E6%8F%90%E5%8F%96%E4%BF%A1%E6%81%AF.png)
我正在使用以下代码从一堆文本文件(foo*.txt)中提取信息。
for file in foo*.txt; do
grep "some_text" $file | tail -n5 | awk '{print $2}' >> bar.csv
done
此注释从一组文件 (foo*.txt) 中打印我想要的数字。当我尝试打印文件名(在 csv 文件的一列中)和数字(在 csv 文件的下一列中)时,我尝试在终端上进行以下操作。
for file in foo*.txt; do
echo $file
grep "some_text" $file | tail -n5 | awk '{print $2}' >> bar2.csv
done
这会在终端上打印文件的名称。csv 文件包含我想要的数字。如何修改此代码,以便将文件的名称打印在 csv 文件的一列上,并将提取的数字打印在下一列中?
此代码中的另一个问题是排序问题。例如,考虑文件名 foo_01_s.txt、foo_02_s.txt、foo_03_s.txt.....foo_100_s.txt。如果我想提取信息(使用上述注释),最后一个文件(foo_100_s.txt)不会位于 foo_99_s.txt 之后。
使用 Python/Perl 的解决方案也会有帮助。
答案1
您必须了解您的>>
只会重定向当前命令的一部分 - 基本上只是以 开头的命令的结果数字grep
,并通过管道传输几次。echo $file
是一个单独的命令(您使用;
),因此通常会直接发送到 stdout。 您需要做的就是在整个循环之后重定向:
for file in foo*.txt; do
echo $file
grep "some_text" $file | tail -n5 | awk '{print $2}'
done > bar2.csv
如果您想要按“版本”对文件进行排序(这是合适的名称),您可以在排序后列出它们:
for file in $(ls foo*.txt | sort -V); do
对于快速运行一些小文件(运行~1000个文件大概需要几分钟)来说这应该没问题。
编辑
根据您的评论,有几个解决方案。我猜你想要:
file1 1
2
3
等等。只需删除echo
并修改 echo 行:
for file in foo*.txt; do
grep "some_text" $file | tail -n5 | awk -v f=$file '{if(NR==1) {printf("%-20s %-5s\n",f,\$2)} else {printf("%-20s %-5s\n","",$2)}}'
done > bar2.csv
我让awk
帮我打印。使用-v
允许我传入一个变量f
。对于打印,请熟悉printf
语法(您可以在 shell 中使用man printf
。基本上,我假设两个字段,一个字段为 20,另一个字段为 5,中间有一个空格。负号左对齐。你可以玩它。这将解决您的初始问题,因为现在您可以管道传输该单行。
如果您希望文件只是:
file1,1
file1,2
...
file2,1
您可以删除if
我的语句中的awk
,或者保留初始解决方案并使用 echo,但使用,
echo -n "$file,"
确保-n
不打印换行符。