过滤每个文件中的文本并将其转换为逗号分隔值的列表

过滤每个文件中的文本并将其转换为逗号分隔值的列表

我正在尝试从多个文件中提取一些信息并创建一个 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/){:如果第一个字段匹配sometextsomeothertextsomedifferenttext。请注意,这也将匹配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 的-aswitch 使其行为类似于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,则意味着该文件中没有匹配的行图案*所以它删除了模式空间。否则,它会删除前导的\newline,用逗号替换其余的,并添加尾随的逗号。

对于其他seds 你必须循环:

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

相关内容