メールキューを定期的にクリーンアップするbashスクリプトがあります。何らかの理由で、私たちは@mms.att.netやその他のemail2SMSゲートウェイに送信され、9時間以上待ち行列にあったがまだ配信されていないすべての電子メールを削除することにしました。
簡単に言えば、このスクリプトは次のことを行います。
domains=`cat /etc/mail/email2textdomains.txt`
egrep $domains /var/log/maillog | .... other tasks
内容は/etc/mail/email2textdomains.txt
正しい
"mms.att.net|vtxt.com|vtext.com|vzwpix.com"
したがって、egrep 行は次のようになります。これは私がコマンドラインに入力したのとまったく同じです。
egrep "mms.att.net|vtxt.com|vtext.com|vzwpix.com" file | ...
これを実行すると、5つ以上のステップコマンドパイプラインになり、各コマンドは古いstdoutからstdinを読み込みます。これは明らかに私が望む検索ではありません。
egrep mms.att.net|vtxt.com|vtext.com|vzwpix.com file | ...
ただし、実行時に2つの二重引用符は異なる方法で処理されます。つまり、文字列の一部になるので、本質的に次を検索します。
- 「mms.att.net
- vtxt.com
- vtext.com
- vzwpix.com」
明らかに私は引用がどのように機能するか誤解しました。解決策は、含まれている行を変更して二重引用符を削除することでした。
パイプでテストしようとしましたが、od -a
印刷されていない文字は表示されません。
コンテンツが/etc/mail/email2textdomains.txt
正しく機能するように機能する理由
mms.att.net|vtxt.com|vtext.com|vzwpix.com
作成したとおり、長い間失敗するパイプラインはいつ必要ですか?
答え1
この種のタスクをデバッグするのに役立つツールはですset -x
。これにより、コマンドが実行する操作を正確に確認できます。
$ set -x
$ domains=$(cat domains.txt)
++ cat domains.txt
+ domains='"mms.att.net|vtxt.com|vtext.com|vzwpix.com"'
ご覧のとおり、引用$domains
符が含まれています。したがって、次のように使用するとgrep
:
$ grep -E -- "$domains" file
+ grep --color -E -- '"mms.att.net|vtxt.com|vtext.com|vzwpix.com"' file
あなたが望むのは、シェルレベルで引用符を使用することです。今後データはgrep
コマンドに渡されますが、引用符は変数データの一部であるため、他の文字のように扱われます。最も簡単な解決策は、ファイルから引用符を削除し、変数のみを引用することです。これはとにかくベストプラクティスです。
domains=$(tr -d \" < domains.txt) &&
grep -E -- "$domains" file
ところで、usingは前者がより明確でより多くのネストを可能にし、もはや使用されないのでvar=$(command)
usingよりも優先されます。var=`command`
egrep
grep -E
また、これは単一文字に一致する正規表現演算子であるため、実際には単一文字の後に単一文字の後に続く行を.
探します。grep mms.att.net
たとえば、を含むものとも一致します。mms
att
net
hammstattinet.com
したがって、E
これらのドメインを含む行に一致する拡張正規表現を作成するには、sを削除するだけでなく、"
ドメイン名から正規表現演算子であるすべての文字をエスケープする必要があります。有効なドメイン名の場合.
。
また、実装によっては空の正規表現に対して異なる動作がありgrep
ますが、ほとんどはすべての行を報告するため、特に処理する必要があります。
だから:
regex=$(
sed 's/"//g; # remove all "s like with tr
s/\./\\./g; # substitute .s with \.s
' domains.txt
) &&
[ -n "$regex" ] && # check it's not empty
grep -E -- "$regex" file
あるいは、|
s を改行文字に置き換え、(以前)-F
オプションを使用して固定文字列を検索することもできます。grep
fgrep
F
domains=$(<domains.txt tr -d '"' | tr '|' '\n') &&
[ -n "$domains" ] &&
grep -F -- "$domains" file
答え2
@Kazは自分の意見を書いて、答えが許可されるようにする必要があります。
避けたい場合は、eval
コードを書き換えて入れる必要があると思います。追加引用符。私の過度に単純化されたルールは、よりよく知らない限り、すべてのドル記号を二重引用符の中に入れなければならないということです。
/etc/mail/email2textdomains.txt
grepが代替を表現する方法で改行を許可するという事実を活用するために1行に1つのフィールドに変更します。
mms.att.net
vtxt.com
vtext.com
vzwpix.com
そして言う
domains="$(cat /etc/mail/email2textdomains.txt)"
grep -- "$domains" /var/log/maillog | .... other tasks
引用符は私のルールを満たすために最初の行にのみあり、必要ありません。これは、テキストフィールドファイルに先行文字が表示されるのを--
防ぐためです。携帯性を向上させるには、または代わりに-
直線を使用してください。実はあなたを書いています。grep
egrep
grep -E
grep -- "mms.att.net
vtxt.com
vtext.com
vzwpix.com" /var/log/maillog | .... other tasks