2

私はLinuxの専門家ではありません。通常、この状況ではPHPの方がはるかに適しています...しかし、状況により、Bashで書いたことがあります:)

現在のフォルダー内のすべての .csv ファイルに対して実行され、一連のコマンドを実行する次の .sh があります。目標: .csv ファイル (実際には .csv ではなく、実際には単なる .txt ファイル) 内のメーリング リストをクリーンアップすること。

for file in $(find . -name "*.csv" ); do
echo "====================================================" >> db_purge_log.txt
echo "$file" >> db_purge_log.txt
echo "----------------------------------------------------" >> db_purge_log.txt
echo "Contacts BEFORE purge:" >> db_purge_log.txt
wc -l $file | cut -d " " -f1 >> db_purge_log.txt
echo " " >> db_purge_log.txt
cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u  > tmp_file
mv tmp_file $file ;
echo "Contacts AFTER purge:" >> db_purge_log.txt
wc -l $file | cut -d " " -f1 >> db_purge_log.txt
done

問題は次のとおりです。

このループの途中で、別の .csv ファイルを抑制リストとして使用するコマンドを追加したいと考えています$file

この時点で頭が固まってしまい、解決策が思いつきません。正直に言うと、 2 つの異なるファイルでsortorgrepを使用して 3 番目のファイルにエクスポートしても、両方のファイルにまたがる重複した行を完全に排除することはできなかったため、最終的にはデータがはるかに少なくなりました。

どんな助けでも大歓迎です!

4

2 に答える 2

4

掃除

スクリプトに機能を追加する前に、既存のスクリプトをクリーンアップする必要があります。

I/O リダイレクション — 繰り返さないでください

このような壁から壁への I/O のリダイレクトを見ると、泣きたくなるのです。それをすべて回避するには、次の 3 つのオプションがあります。

for file in $(find . -name "*.csv" )
do
    echo "===================================================="
    echo "$file"
    echo "----------------------------------------------------"
    echo "Contacts BEFORE purge:"
    wc -l $file | cut -d " " -f1
    echo " "
    cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u  > tmp_file
    mv tmp_file $file ;
    echo "Contacts AFTER purge:"
    wc -l $file | cut -d " " -f1
done  >> db_purge_log.txt

または:

{
for file in $(find . -name "*.csv" )
do
    echo "===================================================="
    echo "$file"
    echo "----------------------------------------------------"
    echo "Contacts BEFORE purge:"
    wc -l $file | cut -d " " -f1
    echo " "
    cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u  > tmp_file
    mv tmp_file $file ;
    echo "Contacts AFTER purge:"
    wc -l $file | cut -d " " -f1
done
}  >> db_purge_log.txt

あるいは:

exec >>db_purge_log.txt   # By default, standard output will go to db_purge_log.txt
for file in $(find . -name "*.csv" )
do
    echo "===================================================="
    echo "$file"
    echo "----------------------------------------------------"
    echo "Contacts BEFORE purge:"
    wc -l $file | cut -d " " -f1
    echo " "
    cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u  > tmp_file
    mv tmp_file $file ;
    echo "Contacts AFTER purge:"
    wc -l $file | cut -d " " -f1
done

最初の形式は、I/O リダイレクトを提供する単一のループを持つこのスクリプトに適しています。2 番目の形式では、{andを使用して、}より一般的なコマンド シーケンスを処理します。を使用する 3 番目の形式execは「永続的」です。元の標準出力を復元することはできませんが、{...}フォームを使用すると、スクリプトのさまざまなセクションをさまざまな場所に書き込むことができます。

これらすべてのバリエーションのもう1つの利点は、必要に応じて、標準出力を送信しているのと同じ場所にエラーを簡単に送信できることです。例えば:

exec >>db_purge_log.txt 2>&1

その他の問題

  • wc次の代わりに —からファイル名を抑制:

    wc -l $file | cut -d " " -f1
    

    使用する:

    wc -l < $file
    
  • UUOCcat — 以下の代わりに— を無用に使用:

    cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u  > tmp_file
    

    使用する:

    egrep -v "xxx|yyy|zzz" $file | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u  > tmp_file
    
  • UUOU — の無駄な使用uniq

    uniqなぜandが必要なのかはまったく明らかではありませんsort -u。でsort -u十分なので、次のようになります。

    egrep -v "xxx|yyy|zzz" $file | grep -v -E -i '([0-z])\1{2,}' | sort -u  > tmp_file
    
  • UUOG — の無駄な使用grep

    egrepは同等でgrep -Eあり、両方とも複数の正規表現を処理できます。2 番目の式は、括弧内の式が 3 回以上一致するものと一致します (実際には 3 回一致するだけで済みます)。したがって、実際には 2 番目の式が一致します。最初の仕事。そして[0-z]試合は怪しい。おそらく、大文字と小文字の数字だけでなく-i、さまざまな句読点にも一致しますが、 .

    grep -Eiv '([0-9a-z]){3}' $file | sort -u > tmp_file
    
  • スペースを含むファイル名

    このコードは、for file in $(find ...)表記のため、スペース、タブ、または改行を含むファイル名を処理しません。おそらく、今それを扱う必要はありません — 問題に注意してください。

最後のクリーンアップ

for file in $(find . -name "*.csv" )
do
    echo "===================================================="
    echo "$file"
    echo "----------------------------------------------------"
    echo "Contacts BEFORE purge:"
    wc -l < $file
    echo " "
    grep -Evi '([0-9a-z]){3}' | sort -u  > tmp_file
    mv tmp_file $file
    echo "Contacts AFTER purge:"
    wc -l <$file
done >> db_purge_log.txt

余分な機能を追加する

このループの途中のどこかに、別のファイルを抑制リストとして使用するコマンドを追加したいと思います。.csvつまり、その抑制リストで完全一致として見つかったすべての行を から削除する必要があります$file

既に入力ファイル ( $file) を並べ替えているので、抑制ファイルを並べ替えることができます (suppfile='suppressions.txt'まだ並べ替えられていない場合は、それも呼び出します。そのため、 を使用して、 とcommの両方に表示される行を削除し$fileます$suppfile。 (または、この場合のように、ファイルの編集およびソートされたバージョンにのみ表示される行$file)、したがって、共通のエントリを抑制し、そこからのエントリ$suppfileは表示されません$filecomm -23 - "$suppfile"、標準入力からファイルをソート-し、からのエントリを除外します"$suppfile"

suppfile='suppressions.txt'   # Must be in sorted order

for file in $(find . -name "*.csv" )
do
    echo "===================================================="
    echo "$file"
    echo "----------------------------------------------------"
    echo "Contacts BEFORE purge:"
    wc -l < "$file"
    echo " "
    grep -Evi '([0-9a-z]){3}' | sort -u | comm -23 - "$suppfile" > tmp_file
    mv tmp_file "$file"
    echo "Contacts AFTER purge:"
    wc -l < "$file"
done >> db_purge_log.txt

抑制ファイルがソートされていない場合は、単純に一時ファイルにソートします。.csv現在のディレクトリの抑制ファイルに接尾辞を使用することに注意してください。抑制ファイルのすべての行が抑制ファイルの行と一致するため、ファイルをキャッチして空にします。これは、抑制ファイルの後に処理されるファイルには役立ちません。


おっと —grep正規表現を単純化しすぎました。(おそらく)次のようになります。

grep -Evi '([0-9a-z])\1{2}' $file

その違いはかなりのものです。123私の元の書き直しでは、隣接する 3 つの数字または文字 (たとえば、 またはabz)を探します。リビジョン (実際には元のコマンドの 1 つと非常によく似ています) は[0-9A-Za-z]、同じ文字が 2 回出現する (たとえば111orで、 oraaaではありません) からの文字を探します。123abz

代替案xxx|yyy|zzzが実際に 3 文字の繰り返しではない可能性がある場合は、 を 2 回連続して呼び出す必要があるかもしれませんgrep

于 2013-08-26T00:05:23.383 に答える