パラメータを渡そうとするシェルスクリプトがあり、スクリプトに空の出力が表示されますdate
。ARGV[1]
これはコマンドです:
#!/bin/bash
dt=$(date -d "yesterday" '+%m%d%Y')
cat /tmp/log.$AUTOSERVE.$dt \
| perl -ne '/STATUS:\s+(\w+).+MACHINE:\s+(\w+.\w+.\w+)$/ && print join( "\t", $1, $2 ). "\n"' \
| grep -E '(SUCCESS|FAILURE|TERMINATED)' \
| cut -f2 \
| sort \
| uniq -c \
| perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", '$ARGV[1]', $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD \
> /tmp/output.txt
私は何が間違っていましたか?
ここで私がやりたいことを説明します。
毎日次の名前でログファイルを生成します。
log.$AUTOSERVE.mmddyyyy
ログファイルには次のデータが含まれます。
理解を深めるために、入力日を変更しました。
Time Message
____________________________________________
[11/16/2023 07:13:45] CAUAJM_I_12345 The application has rollover
[11/16/2023 07:13:45] CAUAJM_I_11111 The machine 111.test.com has lost connection
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: FAILURE JOB: ABC MACHINE: 111.test.com EXITCODE: 1
[11/16/2023 07:13:45] CAUAJM_I_40245 [222.test.com connected to ABC]
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS ALARM: JOBFAILURE JOB: ABC EXITCODE: 1
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: TERMINATED JOB: XYZ MACHINE: 222.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: STARTING JOB: 123 MACHINE: 333.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: SUCCESS JOB: 456 MACHINE: 444.test.com EXITCODE: 0
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: SUCCESS JOB: ABC123 MACHINE: 555.test.com
[11/16/2023 07:13:45] CAUAJM_I_40245 [222.test.com connected to ABC]
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: FAILURE JOB: ABC MACHINE: 111.test.com EXITCODE: 1
[11/16/2023 07:13:45] CAUAJM_I_40245 [333.test.com connected to 123]
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: TERMINATED JOB: XYZ MACHINE: 222.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: STARTING JOB: 123 MACHINE: 333.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: SUCCESS JOB: 456 MACHINE: 444.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: SUCCESS JOB: ABC123 MACHINE: 555.test.com EXITCODE: 0
このシェルスクリプトは、log
このファイルのMACHINEおよびSTATUS検索文字列をフィルタリングし、各システムで実行されているジョブの数を計算します。
私が得た結果は次のとおりです。
NP2 111.test.com 2
NP2 222.test.com 2
NP2 444.test.com 2
NP2 555.test.com 2
$date_YYYYMMDD
に変えようとしました。$dt:
cat /tmp/log.$AUTOSERVE.dt \
| perl -ne '/STATUS:\s+(\w+).+MACHINE:\s+(\w+.\w+.\w+)$/ && print join( "\t", $1, $2 ). "\n"' \
| grep -E '(SUCCESS|FAILURE|TERMINATED)' \
| cut -f2 \
| sort \
| uniq -c \
| perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", $ARGV[1], $ENV{AUTOSERV}, $2, $1) . "\n"' $dt \
> /tmp/output.txt
ただし、次のエラーが発生します。
Can't open 11152023: No such file or directory.
$AUTOSERVE
この出力に値を提供する環境変数があると仮定すると、NP2
予想される結果は次のとおりです。
11152023 NP2 111.test.com 2
11152023 NP2 222.test.com 2
11152023 NP2 444.test.com 2
11152023 NP2 555.test.com 2
答え1
似たようなものが欲しいようです。
#! /bin/sh -
DT=$(date -d yesterday +%m%d%Y) || exit
export DT
exec perl -lne '
if (
($status, $machine) = /STATUS:\s+(\w+).+MACHINE:\s+(\w+\.\w+\.\w+)$/ and
$status =~ /^(SUCCESS|FAILURE|TERMINATED)\z/
) {$count{$machine}++}
END {
for (keys %count) {
print join "\t", $ENV{DT}, $ENV{AUTOSERVE}, $_, $count{$_};
}
}' < ~/tmp/log."$AUTOSERVE.$dt" > ~/tmp/output.txt
メモ:
たとえば、固定名のファイルは誰でも書き込み可能なディレクトリで使用しないでください。
/tmp
したがって、ここに切り替えるか、または//...の専用領域を使用してください~/tmp
。/var
~/var
~/.local
$XDG_RUNTIME_DIR
このコードにはbashに関連する内容がないため、bashの依存関係を追加する必要はありません。
追加の
-n
パラメータはperl
スクリプトへの入力です。Chrisがすでに述べたように、あなたの引用に問題があります。
あなたは
AUTOSERV
/AUTOSERVE
違いがあります。.
単一文字に一致する正規表現演算子です。テキストポイントを一致させるには、\.
またはを使用します。[.]
使用法は
date
GNUによって異なります。すべてのdate
実装が-d
オプションをサポートしているわけではなく、サポートしている場合は、BSDの項目や認識できない項目(ビジボックスやおもちゃ箱yesterday
など)など、まったく関連のない項目に使用できます。このスクリプトをGNU以外のシステムに移植する必要がある場合は、日付操作も可能です。date
perl
次のように、単一の正規表現を使用するように簡単に変更できます。
/STATUS:\s+(?:SUCCESS|FAILURE|TERMINATED)\b.+MACHINE:\s+(\w+.\w+.\w+)$/
マシンのリストをアルファベット順に並べ替えるには、を
keys %count
に置き換えます。sort cmp, keys %count
exec
このようなラッパースクリプトで非常に一般的なのは、プロセスを保存することです。perl
子プロセスで実行し、待つのではなく、同じプロセスで実行するようにシェルに指示します。cmd
+fork()
&をexec(cmd)
実行しますがwait(child)
(exec cmd
おそらくと呼ばれる必要がありますnofork cmd
)exec(cmd)
、入力に時間がかかりますが、システムの実行はより簡単で短く、リソースの使用量が少なくなります。%m%d%Y
タイムスタンプ形式を選択することはお勧めできません。あいまいで、語彙順(の出力と同様ls
)が年代順と一致しません。%Y-%m-%d
それとも%F
普遍的に認識され、語彙的に年代順(少なくとも0001から9999まで)にソートされるので、より良いです。cat
ファイルをリンクするコマンドです。ファイルに使用することは意味がありません。cmd < input > output
(または、<input cmd >output
しかしいいえcmd > output < input
input
)には、読み取り用に開くことができない場合はcmd
実行されず、output
削除されないという追加の利点があります。
1たとえば、ここに-MPOSIX
aBEGIN{@t = localtime; $t[3]--; $dt = strftime "%m%d%Y", @t}
またはハッキングとして追加します-M'POSIX;@t = localtime; $t[3]--; $dt = strftime "%m%d%Y", @t'
。
答え2
私の心の中に浮かぶ質問は2perl
行目です。
perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", '$ARGV[1]', $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD
$ARGV[1]
シェルが解析を試みるように、タイムリーに単一引用符を使用できます。通常、$ARGV
シェル変数は設定されていないため、渡される結果行は次perl
のようになります。
perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", [1], $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD
これは構文的に有効ですが、役に立つ可能性は低いため、エラーは発生しません。
行の中央にある2つの一重引用符を削除すると、必要なものとほぼ同じ結果が得られます。-n
読むには明示的なループを交換する必要があります。標準入力@ARGV
あなたの価値を捉えることができます。含まれているリストは@ARGV
最初から始まるので、そのリストも変更しました。
perl -e 'while (<STDIN>) { chomp; /^\s+(\d+)\s+(.*)$/ && print join("\t", $ARGV[0], $ENV{AUTOSERV}, $2, $1) . "\n" }' "$date_YYYYMMDD"
ソースファイルをインポートして指定された出力を生成するパイプラインは次のとおりです。
awk -v date="$(date --date 'yesterday' +'%m%d%Y')" '
# Count instances of IP address for finished jobs
/SUCCESS|FAILURE|TERMINATED/ {
if (m = index($0, "MACHINE:")) {
# address is after "machine"
ip = substr($0, m+9, length($0))
if (s = index(ip, " ")) {
# discard trailing text too
ip = substr(ip, 1, s-1)
}
# capture address
seen[ip]++
}
}
# Output list of addresses and counts
END {
OFS="\t"
for (ip in seen) {
print date, ENVIRON["AUTOSERVE"], seen[ip], ip
}
}
' "/tmp/log.$AUTOSERVE.dt"
適切な日付マッチングによってAUTOSERVE=NP2
サンプルデータファイルからこの結果が得られました。
11162023 NP2 2 222.test.com
11162023 NP2 2 111.test.com
11162023 NP2 2 555.test.com
11162023 NP2 2 444.test.com
if (m = index($0, "MACHINE:"))
この構造は次のような点に注目する価値があります。仕事その後、ゼロ以外のテストが続きます。比較したい場合は、==
代わりに=
次のように書くことができます。
m = index($0, "MACHINE:")
if (m<>0)
答え3
最初に失敗するのは、単一引用符をエスケープするためです。
perl -ne '[...] '$ARGV[1]', [...]'
したがって、あなたのものが代わりに$ARGV[1]
シェルに表示されますperl
。次に、次を使用しているため、標準入力から読むようにARGV
言っているので、実際にここに配列がありません。perl
-n
$ perl -le 'print "$ARGV[0]"' foo
foo
$ perl -nle 'print "$ARGV[0]"' foo
$
あなたはできます誰でもこれ-n
により、データをパイプするか、Perlに自動的にファイルをロードして繰り返すように要求できます。または引数を渡すことはできますが、どちらも渡すことはできません。
したがって、実際にやりたいことは次のとおりです。
export dt=$(date -d "yesterday" '+%m%d%Y')
perl -ne '/STATUS:\s+(\w+).+MACHINE:\s+(\w+.\w+.\w+)$/ && print join( "\t", $1, $2 ). "\n"' /tmp/log.$AUTOSERVE.dt |
grep -E '(SUCCESS|FAILURE|TERMINATED)' |
cut -f2 |
sort |
uniq -c |
perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", $ENV{dt}, $ENV{AUTOSERV}, $2, $1) . "\n"' > /tmp/output.txt
または、すでに使用しているので、perl
データを正しく推測すると仮定します。
export dt=$(date -d "yesterday" '+%m%d%Y')
perl -lne '/STATUS:\s+(SUCCESS|FAILURE|TERMINATED).+MACHINE:\s+(\w+.\w+.\w+)$/ &&
print "$2"' /tmp/log.$AUTOSERVE.dt |
sort |
uniq -c |
perl -ne '/^\s+(\d+)\s+(.*)$/ &&
print join("\t", $ENV{dt}, $ENV{AUTOSERV}, $2, $1) . "\n"' > /tmp/output.txt
でも:
export dt=$(date -d "yesterday" '+%m%d%Y')
perl -lne '
if(/STATUS:\s+(SUCCESS|FAILURE|TERMINATED).+MACHINE:\s+(\w+.\w+.\w+)$/){
$k{$2}++;
}
END{
foreach $key (keys(%k)){
print "$ENV{dt}\t$ENV{AUTOSERV}\t$key\t$k{$key}"
}
}' > /tmp/output.txt