我正在尝试从多个文件中提取一些信息并创建一个 csv 类型的文件。到目前为止,我已经完成了提取和写入文件部分的工作,但不知道如何在每个输出之间添加逗号或在末尾删除换行符。
#!/bin/bash
for file in folder/*.txt do
grep 'sometext:' $file | sed '/^.*:\s*//' >> list.txt
#doing simliar stuff with other lines in the current file
done
我尝试使用echo -n
删除换行符,但这没有返回任何有用的东西。
代码应该做什么:
对于文件夹中的每个文件,找到以某些模式(例如 等)开头的行,sometext:
并将someothertext:
该行的其余部分和 a 附加到,
与list.txt
.
文件夹中文件内容的示例:
randomtext: ...
sometext: Hello
randomtext: ...
someothertext: World
somedifferenttext: !
randomtext:
将导致输出文件中的单行Hello,World,!,
答案1
好吧,首先不要使用循环for
!这是非常低效的。只需一次给出grep
所有文件名:
grep 'sometext:' folder/*.txt
然而,在这种情况下,我会使用awk
而不是grep
.我将你的输入文件复制了 10 份来测试:
$ awk '{
if($1~/sometext|someothertext|somedifferenttext/){
printf "%s,",$2
}
if(FNR==1 && NR>1){
print ""
}
}
END{ print "" }' folder/*txt
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
解释
awk
是一种脚本语言,它逐行读取输入,并将空白处的每一行(默认情况下,您可以使用 更改-F
)拆分为字段。第一个字段将是$1
,第二个字段$2
等等。
if($1~/sometext|someothertext|somedifferenttext/){
:如果第一个字段匹配sometext
或someothertext
或somedifferenttext
。请注意,这也将匹配foosometext
.如果您想限制精确匹配,请将其更改为:if($1=="sometext:" || $1=="someothertext:" || $1=="somedifferenttext:"){
printf "%s,",$2
:如果满足上述条件,则打印第二个字段,后跟逗号。if(FNR==1 && NR>1){ print "" }
:NR
是当前输入行号,也是FNR
当前文件的行号。因此,每次文件的行号为 1 时,都会打印一个换行符(awk 的print
调用默认添加一个换行符,因此不打印任何内容就像打印一个换行符),但如果处理的总行数也是 1,则不会。换句话说,每次我们开始读取新文件时,都会打印换行符。END{ print "" }'
:处理完所有文件后还打印换行符。
请注意,这假设每行只有 2 个字段。如果您需要打印整行,则可以使用(使用仅打印完全匹配的版本来说明):
awk '{
if($1=="sometext:" ||
$1=="someothertext:" ||
$1=="somedifferenttext:"){
$1="";
printf "%s,",$0
}
if(FNR==1 && NR>1){print ""}
}END{print ""}' folder/*txt | sed 's/^ //'
不同之处在于我们在打印之前使用$0
(整行)代替$2
并设置$1
为空字符串。这会导致在开头打印一个额外的空格(因为空$1
仍然被视为一个字段),因此我们将其传递sed
以将其删除。
或者,您也可以在 Perl 中完成整个操作:
$ perl -lane '
if($F[0]=~/(sometext|someothertext|somedifferenttext):/){
push @k,@F[1..$#F]
}
if(eof){
print join ",", @k; @k=();
}' folder/file*
Hello,World,!
Hello,World,!
Hello,World,!
Hello,World,!
Hello,World,!
Hello,World,!
Hello,World,!
Hello,World,!
Hello,World,!
Hello,World,!
Hello,World,!
或者,也可以有尾随,
:
$ perl -lane '
if($F[0]=~/^(sometext|someothertext|somedifferenttext):$/){
push @k,@F[1..$#F]
}
if(eof){
print join ",", @k , ""; @k=();
}' folder/file*
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
解释
这里的基本思想是相同的。 Perl 的-a
switch 使其行为类似于awk
将每个输入行拆分到数组中@F
。然后,如果数组的第一个元素是所需的字符串之一,则其余字段 ( @F[1..$#F]
) 将添加到数组中@k
。如果到达文件末尾 ( if(eof)
),我们将用逗号连接数组的内容@k
并打印结果字符串。
最后,这是一种按照您尝试的方式执行此操作的方法(假设是 GNU grep
):
$ for f in folder/*; do
grep -hoP '^(sometext|someothertext|somedifferenttext): \K.*' "$f" |
perl -pe 's/\n/,/; END{print "\n"}';
done
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
Hello,World,!,
答案2
和gnu sed
:
sed -Es '/pattern1|pattern2|pattern3/{
s/.*:[[:blank:]]*//;H}
$!d;x;/^\n$/d;s/\n(.*)/\1,/;s/\n/,/g' folder/*.txt > list.txt
其中list.txt
内容将类似于:
file1match1,file1match2,
file2match1,
file4match1,file4match2,file4match3,
file3
由于没有行匹配,因此输出中缺少so图案*。
它是如何工作的:它分别处理每个文件-s
,删除(通过s/.*:[[:blank:]]*//
)匹配行上不需要的部分图案*并将结果附加到H
旧缓冲区。$
当它x
更改缓冲区时,它会删除除 la t 之外的每一行。如果\n
模式空间中只有一条ewline,则意味着该文件中没有匹配的行图案*所以它删除了模式空间。否则,它会删除前导的\n
ewline,用逗号替换其余的,并添加尾随的逗号。
对于其他sed
s 你必须循环:
for file in folder/*.txt do
sed '/pattern1\|pattern2\|pattern3/{
s/.*:[[:blank:]]*//
H
}
$!d
x
/^\n$/d
s/\n\(.*\)/\1,/
s/\n/,/g' "$file"
done > list.txt