次の内容を含むlistlogというログファイルがあります。
2021-08-12 16:09:17 textsp sdgg
reponse:success
prams:invalid
line 3
2021-08-12 16:10:17 textdfdfdlfs sfdfs
reponse: failed
prams:valid
line 3
t1とt2の間に記録されたログをフィルタリングして2つの連続したログ行を印刷したいと思います。私はawkとif条件とgrepを使って書いています。しかし、エラーが発生しました。
これが t1 < (16:09:17, 16:19:17) < t2 と仮定してフィルタリングして新しいファイルに書きたい内容です。
2021-08-12 16:09:17 textsp sdgg
reponse:success
prams:invalid
2021-08-12 16:10:17 textdfdfdlfs sfdfs
reponse: failed
prams:valid
これは私が書いたawkスクリプトです。
t1=$(date -d "$min_change minutes ago" +"%F %T")
t2=$(date +"%F %T")
cat $listlog | awk -v t1="$t1" -v t2="$t2" -v listlog="$listlog" '{
if (match($0 ~ /^[0-9]{4}(-[0-9]{2}){2}))
{
if ( $0 > t1 && $0 < t2 | $0 ~ t2 )
{
grep -A 2 $0 $listlog >> /tmp/temp.txt
}
}
}'
私が得るエラーは次のとおりです。
awk: cmd. line:2: if (match($0 ~ /^[0-9]{4}(-[0-9]{2}){2}))
awk: cmd. line:2: ^ unterminated regexp
awk: cmd. line:3: if (match($0 ~ /^[0-9]{4}(-[0-9]{2}){2}))
awk: cmd. line:3: ^ unexpected newline or end of string
awk: cmd. line:4: if ( $0 > t1 && $0 < t2 | $0 ~ t2 )
awk: cmd. line:4: ^ syntax error.
awk: cmd. line:5: if ( $0 > t1 && $0 < t2 | $0 ~ t2 )
awk: cmd. line:5: ^ unexpected newline or end of string
awk: cmd. line:6: grep -A 2 $0 $listlog >> /temp/temp.txt
awk: cmd. line:6: ^ syntax error
awk: cmd. line:7: grep -A 2 $0 $listlog >> /temp/temp.txt.
awk: cmd. line:7: ^ unexpected newline or end of string
答え1
スクリプトの問題を説明し始めるのは難しいですが、以下は最も重要なことをまとめたものです。
awk
あなたが書いたのは台本ではありません。awk
シェルスクリプトに含まれているスクリプトでもありません(通常は完璧です)。これは、いくつかのシェルコードとawk
その中にシェルコードを含めようとする試みの奇妙なブレンドです(awk
シェルコマンドを解釈または実行せずに)。とにかく、外部プロセスにフォークしないと、内部的に
grep
このように実行することはできません。そして正規表現のパターンマッチングが可能なので、awk
これを行う必要はありません。awk
コードにインデントがないので、読みにくいです。
アップデート:質問を編集してフォーマットを変更したところ、コードにインデントがあることがわかりました。ただし、
<br/>
Markdownエディタに組み込まれているテキスト書式設定機能を使用する代わりに、HTMLタグを使用して書式設定します。1番と2番のエントリは、おそらく質問を投稿してから7時間以内に回答やコメントを受け取らなかった理由でしょう。すべての読者(少なくとも私の読者)の初期反応は「WTF?これは言えない。どこから始めるべきかさえ分からない…通過」です。
シェルまたは日付
awk
比較を試して日付比較を実行することはできません。より大きいまたはより小さいなどの数値比較を実行するには、日付を数値に変換する必要があります(たとえば、time_t
1970年1月1日の真夜中(UTC)である「epoch」以降の秒を表すunix形式)。で好きなようにできますが、日付変換機能が組み込まれていない
awk
ため、必要以上に難しいでしょう。日付と時刻のライブラリ機能が良い場合は、はるかawk
に簡単です。perl
あなたが書くスクリプトはperl
コードよりもコードスタイルに近いので、これはあなたにとって良いことですawk
。
これはPerlで欲しいものを達成する非常に簡単で直接的な方法です。コマンドライン引数が必要です(perlGetSelect::標準ライブラリモジュール)。 Getopt::Std
Perlに含まれるコアPerlライブラリモジュール。
パラメータは順序に関係なく提供できます。すべてのオプションパラメータ(、、および)-m
は-n
必須ではなく、-s
コマンドラインに指定されていない場合はデフォルト値を持ちます。未処理の引数はGetopt::Std
入力ファイル名として扱われます。スクリプトは標準入力と同様にすべての入力ファイル名を処理できるため、ログファイルをスクリプトにリンクできます。
スクリプトも日付::分析Perlのモジュール時間::日付日付文字列をepoch以降の秒コレクションに変換します。
Date::Parse
Perlにはこれが含まれていないため、インストールする必要があります。 Debian(またはUbuntu、Mint、または他のDebian関連のディストリビューション)を使用している場合は、他のほとんどsudo apt-get install libtimedate-perl
のディストリビューションでもパッケージを提供します。セントースパッケージはPerl-TimeDate-2.30-2Centos 7の場合、またはPerl-TimeDate-2.30-15セントース8の場合。それ以外の場合は使用できますcpan
。
#!/usr/bin/perl
use strict;
use Getopt::Std;
use Date::Parse;
my %opts;
getopt('m:s:n:', \%opts); # -m minutes, -s start time, -n number of lines
my $st = $opts{s} || -1; # start time, default now.
my $min = $opts{m} || 5; # number of minutes after $st to match, default 5.
my $nl = $opts{n} || 2; # No. of extra lines to print after match, default 2.
if ($st == -1) {
$st = time(); # now
} else {
$st = str2time($st); # convert time string to seconds since epoch
};
my $et = $st + $min * 60; # calculate ending time
my $p = -1; # input lines are printed only when $p > -1
# main loop, read and process all input file(s).
while (<>) {
if (m/^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)/) {
my $t = str2time($1);
$p = $nl if ($t >= $st && $t <= $et);
};
if ($p > -1) { print; $p-- };
};
たとえば、別の名前で保存してextract.pl
実行可能にし、chmod +x extract.pl
次のように実行します。
$ ./extract.pl -s "2021-08-12 16:09:00" -n 2 -m 5 listlog
2021-08-12 16:09:17 textsp sdgg
reponse:success
prams:invalid
2021-08-12 16:10:17 textdfdfdlfs sfdfs
reponse: failed
prams:valid
>>/tmp/temp.txt
必要に応じて、コマンドラインの最後に追加して出力をリダイレクトできます。