データストリームの文字列をフィルタリングして画面に出力するためのシェルスクリプト

データストリームの文字列をフィルタリングして画面に出力するためのシェルスクリプト

ログファイルを簡単に追跡し、単一フィールドの出力を画面に印刷しようとしています。

私のLinuxシステムのログファイルは次のとおりです。

2022-10-21 16:00:08;areq_in=0;areq_qavg=0;areq_qmax=0;areq_sent=0;ares_out=0;ares_out_err=0;ares_out_ok=0;ares_qavg=0;ares_qmax=0;ares
_recv=0;ares_tavg=0;ares_tmax=0;creq_out=0;creq_qavg=0;creq_qmax=0;creq_recv=0;cres_in=0;cres_qavg=0;cres_qmax=0;cres_sent=0;lic_rej=0
;lic_use=0;mbr_from=0;mbr_to=0;mreject=0;mreject_conn=0;msys_recv=0;msys_sent=0;

ログの各行は;区切りフィールドで構成され、ほとんどは「key = value」形式です。

だから私がしたいのは、creq_recv画面に「キー」名のフィールドを印刷することです。

誰かの修正からこのスクリプトをインポートしましたが、何かが欠けているようです。

#!/bin/bash
creq_recv=$1
my_variable=`tail -f stats_2022102116.log | awk -F: -v creq_recv="$1" '$1 == creq_recv {$1=$1; print}' stats_2022102116.log`
echo "$my_variable"

出力が空なので

[root@priti ]$ ./chk_conn_log.sh
    
[root@priti ]$ 

値が何であれ、出力は画面に印刷する必要がありますcreq_recv。たとえば、次のように

creq_recv=12
creq_recv=34
creq_recv=65

答え1

行および/またはPCREまたは拡張正規表現の一致部分のみを印刷するオプションを使用して、grep必要な操作を実行できます。これにより、「1つ以上の雨;文字」を使用できます。-o-P-E[^;]+

tail -f stats_2022102116.log | grep -oP '(?<=^|;)creq_recv=[^;]+'

または、より安全にするために、次に終わる別のフィールド名を使用することもできますcreq_recv

tail -f stats_2022102116.log | grep -oP '(^|;)\Kcreq_recv=[^;]+'

「awk変数の値をスクリプトに渡された最初の引数の値に設定する」という意味でawk失敗しました。ただし、起動したスクリプトにはパラメータ()がないため空白なので、空白です。-v creq_recv="$1"creq_recv./chk_conn_log.sh$1creq_recv

とにかく変数が設定されていても動作しません。スクリプトがawk間違ってtail -f停止しないと、絶対に終了しないためecho実行されません。あなたが望むものは次のとおりです。

#!/bin/bash

tail -f stats_2022102116.log | 
    awk -F';'  '{ for(i=1; i<=NF; i++){ if ($i ~ /^creq_recv=/) { print $i } } }'

いいえechotail変更されたawkスクリプトのみが適用されます。しかし、上記のコマンドを使用すると、grepはるかに簡単です。

答え2

試している回避策には、望ましくない動作を引き起こすいくつかの問題が含まれています。

  1. スクリプトを$1呼び出すときに指定されていないようなコマンドライン引数(「bash」レベル)が必要です。./chk_conn_log.sh
  2. スクリプトは、対応する(未指定= null)値を次にコピーします。シェル変えるcreq_recv。だから、creq_recv シェルに関する限りは空の変数です。
  3. フィルタリングしたいですか?前進プロセスの出力を印刷しtailますが、後で印刷に使用するシェル変数に結果を割り当てますecho。変数は両方のプロセスが終了したときにのみ入力されるため、機能しません。ただし、ログファイルに書き込まれなくなった場合は、tail新しい入力を待ちます。したがって、ログファイルは決してシャットダウンされず、スクリプトは会議実際にはそこに閉じ込められているが、
  4. tailの出力をにパイプしたいが、awkログファイルをパラメータとして明示的に宣言するので、awkファイルの内容が簡単に実行されます。今スクリプトを呼び出すように、まだ行が含まれていない可能性があり、終了します。したがって、変数はmy_variable空で、スクリプトの出力も空です。

たとえそれでも、

  1. ログファイルが区切られていると言いましたが(例を表示)、区切り文字を使用するようにマークし;ました。awk:
  2. あなたが設定したawk変数 creq_recv(指定されていない)コマンドライン引数に追加し、awkプログラム内で最初 :- コンテンツと同じ別のフィールドアッ変えるcreq_recv。ただし、変数は空で(上記参照)、「フィールド」には文字列2022-10-21 16(最初の日付/時刻まで:)が含まれます。したがって、いくつかの理由で条件が満たされない。その行を含むスクリプトを呼び出すと、出力は空です。
  3. 表示されたフィールドだけでなく、行全体を印刷しますcreq_recv=xx

つまり、私の考えでは、あなたが達成したいと思います。これは次のように実行できます。

tail -f stats_2022102116.log | awk -F';' '{for (i=1;i<=NF;i++) {if (index($i,"creq_recv=")==1) print $i}}'
  • これにより、フィールド区切り記号がに設定されます;
  • 次に、各(入力)入力行に対してすべてのフィールドを繰り返し、creq_recv=文字列で始まる場合はフィールドを印刷します。

正規表現マッチングの代わりにリテラル文字列比較を使用することを選択したのは、通常の場合、正規表現で特別な意味のある文字を含む文字列をエスケープせずに見つけることができるためです。正規表現の一致が必要な場合(あなたの投稿によるとここでは該当しません)、変更できます。

if (index($i,"creq_recv=")==1)

到着

if ($i ~ /your regular expression/)

答え3

使用幸せ(以前のPerl_6)

#recover all key/value pairs:

~$ cat file | raku -e 'my @a = lines>>.split(";", :skip-empty)>>.[1..*].flat;  \
                       my %hash;  for @a>>.split("=") {%hash.=append: $_};  \
                      .say for %hash.sort;'  
#recover values for specific key:

~$ cat file | raku -e 'my @a = lines>>.split(";", :skip-empty)>>.[1..*].flat; \
                       my %hash;  for @a>>.split("=") {%hash.=append: $_}; \
                       say %hash<creq_recv>;' 

上記は、Perlプログラミング言語ファミリーであるRakuでコーディングされた答えです。この問題には実際にはKey-Valueソリューションが必要であり、Rakuは失望しません。

上記の2つのコード例は、最後のステートメントを除いて同じです。つまり、lines読み取ったファイルはデフォルトで末尾の改行を切り取る効果があります\n。その後、この入力はセミコロンに分割され、最初の(時間/日付)要素が削除され、維持され、配列に格納されるインデックス要素が;生成されます。それからの要素は等号に別々に分けられます。この結果要素は編集されます。[1..*]flat@a@a>>=appendみんなこのように入力が与えられると、%hashRakuは2つの連続要素ごとにキー/値のペアを生成します。

最初のコード例では、すべてのキー/値のペアが返されます。 2番目のコード例では、creq_recvキーに関連付けられた値のみが返されます。

入力例:

2022-10-21 16:00:08;areq_in=0;areq_qavg=0;areq_qmax=0;areq_sent=0;ares_out=0;ares_out_err=0;ares_out_ok=0;ares_qavg=0;ares_qmax=0;ares_recv=0;ares_tavg=0;ares_tmax=0;creq_out=0;creq_qavg=0;creq_qmax=0;creq_recv=0;cres_in=0;cres_qavg=0;cres_qmax=0;cres_sent=0;lic_rej=0;lic_use=0;mbr_from=0;mbr_to=0;mreject=0;mreject_conn=0;msys_recv=0;msys_sent=0;

最初のコード例のサンプル出力(2番目のコード例から返されます0):

areq_in => 0
areq_qavg => 0
areq_qmax => 0
areq_sent => 0
ares_out => 0
ares_out_err => 0
ares_out_ok => 0
ares_qavg => 0
ares_qmax => 0
ares_recv => 0
ares_tavg => 0
ares_tmax => 0
creq_out => 0
creq_qavg => 0
creq_qmax => 0
creq_recv => 0
cres_in => 0
cres_qavg => 0
cres_qmax => 0
cres_sent => 0
lic_rej => 0
lic_use => 0
mbr_from => 0
mbr_to => 0
mreject => 0
mreject_conn => 0
msys_recv => 0
msys_sent => 0

より豊富な戻り情報を取得するには、Rakuの=>ペアコンストラクタを使用して、最後のステートメントを次のように変更します。

$_ = "creq_recv" andthen say $_ => %hash{$_};

...返品:

creq_recv => 0

最後に、Rakuは受信(StdINを介して受信されたライン)と、受信したラインでコードを実行するようにreact設計できるブロックを実装します。このように:Supply$*IN.lineswhenever

~$ tail -f test.log | raku -e 'react {whenever Supply( $*IN.lines ) {   \
                               my @a = $_.map( *.split(";", :skip-empty).[1..*] ).flat;  \
                               my %hash; for @a.map: *.split("=") { %hash.=append: $_ };  \
                               $_ = "creq_recv" andthen say $_ => %hash{$_} // Nil; } };'
creq_recv => 0
creq_recv => 0
^C

https://docs.raku.org/言語/concurrency#react
https://docs.raku.org/言語/hashmap
https://docs.raku.org/routine/=%3E
https://raku.org

PS:brian d foyの本には、「関連」(ハッシュやマップなど)に関する非常に素晴らしい章があります。Perl6を学ぶ

関連情報