Nec*_*der 2 shell bash perl shell-script
我有一个 shell 脚本,我试图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
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
让我解释一下我想在这里做什么:
我们每天都会生成日志文件,其名称如下
log.$AUTOSERVE.mmddyyyy
日志文件包含如下数据:
更改了输入日期以便更好地理解:
#!/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
Run Code Online (Sandbox Code Playgroud)
此 shell 脚本过滤此log
文件中的 MACHINE 和 STATUS 搜索字符串,并计算每台计算机上运行的作业数量
我得到的输出是:
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
Run Code Online (Sandbox Code Playgroud)
我尝试更改$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
Run Code Online (Sandbox Code Playgroud)
但我收到以下错误:
NP2 111.test.com 2
NP2 222.test.com 2
NP2 444.test.com 2
NP2 555.test.com 2
Run Code Online (Sandbox Code Playgroud)
鉴于我有一个$AUTOSERVE
提供NP2
此输出中的值的环境变量,我期望的是:
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
Run Code Online (Sandbox Code Playgroud)
听起来你想要类似的东西:
\n#! /bin/sh -\nDT=$(date -d yesterday +%m%d%Y) || exit\nexport DT\nexec perl -lne '\n if (\n ($status, $machine) = /STATUS:\\s+(\\w+).+MACHINE:\\s+(\\w+\\.\\w+\\.\\w+)$/ and\n $status =~ /^(SUCCESS|FAILURE|TERMINATED)\\z/\n ) {$count{$machine}++}\n END {\n for (keys %count) {\n print join "\\t", $ENV{DT}, $ENV{AUTOSERVE}, $_, $count{$_};\n }\n }' < ~/tmp/log."$AUTOSERVE.$dt" > ~/tmp/output.txt\n
Run Code Online (Sandbox Code Playgroud)\n笔记:
\n不得在世界可写目录中使用具有固定名称的文件,例如/tmp
(因此切换到此处,或在或/ / ...~/tmp
中使用专用区域)。/var
~/var
~/.local
$XDG_RUNTIME_DIR
该代码中没有任何特定于 bash 的内容,因此无需添加 bash 依赖项。
\n其中 的-n
额外参数是perl
脚本的输入
正如克里斯已经说过的,你的引用有问题。
\n你有AUTOSERV
/AUTOSERVE
差异。
.
是匹配任何单个字符的正则表达式运算符。使用\\.
或[.]
来匹配文字点。
请注意, 的用法date
是 GNU 特定的。并非所有date
实现都支持某个-d
选项,在那些支持的实现中,它可能用于完全不相关的东西,例如 BSD 上的东西,或者它们无法识别的东西yesterday
(例如date
busybox 或 toybox 的)。 perl
如果您需要将该脚本移植到非 GNU 系统,还可以执行日期操作\xc2\xb9。
您可以轻松地将其更改为使用单个正则表达式,例如:
\n/STATUS:\\s+(?:SUCCESS|FAILURE|TERMINATED)\\b.+MACHINE:\\s+(\\w+.\\w+.\\w+)$/\n
Run Code Online (Sandbox Code Playgroud)\n如果您希望机器列表按词法排序,请替换keys %count
为。sort cmp, keys %count
exec
,在像这样的包装器脚本中常见的只是保存一个进程。perl
它告诉 shell在同一进程中运行而不是在子进程中运行并等待它。cmd
确实fork()
+ exec(cmd)
& wait(child)
, while exec cmd
(也许应该被称为nofork cmd
) 就是exec(cmd)
这样,即使输入时间较长,但系统运行起来更简单/更短,并且使用更少的资源。
%m%d%Y
不是一个很好的时间戳格式选择。它是不明确的,并且它的词汇顺序(如 的输出中ls
)与时间顺序不匹配。%Y-%m-%d
或者%F
简称为更好,因为它被普遍认可,并且按词汇时间顺序排序(至少从 0001 到 9999 年)。
cat
是连接文件的命令,对一个文件使用它没有什么意义。使用cmd < input > output
(or <input cmd >output
,但不是 cmd > output < input
) 的好处是,如果input
无法打开阅读,则cmd
不会运行并且output
不会被破坏。
\xc2\xb9 例如,在这里添加-MPOSIX
and aBEGIN{@t = localtime; $t[3]--; $dt = strftime "%m%d%Y", @t}
或什至作为 hack 只是-M'POSIX;@t = localtime; $t[3]--; $dt = strftime "%m%d%Y", @t'
。
我突然想到的一个问题是第二行perl
:
Run Code Online (Sandbox Code Playgroud)perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", '$ARGV[1]', $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD
您正好可以及时使用单引号$ARGV[1]
,因此 shell 可以尝试解析它。$ARGV
通常不会设置shell 变量,因此传递给的结果行perl
是这样的:
Run Code Online (Sandbox Code Playgroud)perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", [1], $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD
这在语法上是有效的(但不太可能有用),因此您不会收到任何错误。
如果删除行中间的两个单引号,您几乎会得到看起来像您想要的东西。您需要换出一个显式循环来从标准输入-n
读取,以便可以捕获您的值。列表,包括,从零开始,所以我也改变了这一点。@ARGV
@ARGV
perl -e 'while (<STDIN>) { chomp; /^\s+(\d+)\s+(.*)$/ && print join("\t", $ARGV[0], $ENV{AUTOSERV}, $2, $1) . "\n" }' "$date_YYYYMMDD"
Run Code Online (Sandbox Code Playgroud)
这是一个可供您选择的管道,它将获取您的源文件并生成指定的输出:
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"
Run Code Online (Sandbox Code Playgroud)
通过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
Run Code Online (Sandbox Code Playgroud)
可能值得注意的是,该构造if (m = index($0, "MACHINE:"))
是一个带有后续非零测试的赋值。如果我想要进行比较,我应该使用==
而不是=
. 它可以等效地写成这样
m = index($0, "MACHINE:")
if (m<>0)
Run Code Online (Sandbox Code Playgroud)