各ファイルのテキストをフィルタリングし、コンマで区切られた値のリストに変換します。

各ファイルのテキストをフィルタリングし、コンマで区切られた値のリストに変換します。

複数のファイルからいくつかの情報を抽出し、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:

出力ファイルに1行が生成されます。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入力を 1 行ずつ読み込み、余白の各行-F(デフォルトで変更可能)をフィールドに分割するスクリプト言語です。最初のフィールドはで$1、2番目のフィールドは$2このようになります。

  • if($1~/sometext|someothertext|somedifferenttext/){:最初のフィールドがsometextorsomeothertextまたはと一致する場合somedifferenttext。この項目も一致しますfoosometext。正確な一致を制限するには、次のように変更してください。

    if($1=="sometext:" || $1=="someothertext:" || $1=="somedifferenttext:"){
    
  • printf "%s,",$2:上記の条件が満たされたら、2番目のフィールドを印刷し、その後にカンマを入力します。

  • if(FNR==1 && NR>1){ print "" }NR現在の入力行番号とFNR現在のファイルの行番号。したがって、ファイルに行番号1があるたびに改行が印刷されます(printawk呼び出しはデフォルトで改行を追加するため、何も印刷しないのは改行を印刷するのと同じです)。ただし、処理された合計行数が1の場合は必要です。ティー。つまり、新しいファイルを読み始めるたびに改行文字が印刷されます。

  • END{ print "" }':すべてのファイルを処理した後、改行文字も印刷します。

これは、1行に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。これにより、先頭に余分なスペースが印刷されます(空白はまだフィールドと見なされるため)、それを削除するために渡します$1sed


または、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ように動作します。次に、配列の最初の要素が必須文字列の1つである場合、残りのフィールド()が配列に追加されます。ファイルの終わり()に達すると、配列の内容をコンマで連結し、結果の文字列を印刷します。awk@F@F[1..$#F]@kif(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一致する行がないため、出力から欠落しています。パターン*
仕組み:各ファイルを個別に処理して、一致する行から不要な部分を-s削除します。s/.*:[[:blank:]]*//パターン*結果をH前のバッファに追加します。バッファを変更する$と、la tを除くすべての行が削除されます。パターン空間に ewline が 1 つしかxない場合は\n、ファイルに一致する行がないことを意味します。パターン*したがって、パターン空間が除去される。それ以外の場合は、前の\n改行を削除し、残りの部分をコンマに置き換え、末尾のコンマを追加します。

それ以外のsed場合は、以下を繰り返す必要があります。

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

関連情報