ARGV[] 不接受参数

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)

Sté*_*las 6

听起来你想要类似的东西:

\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
    \n
  • 不得在世界可写目录中使用具有固定名称的文件,例如/tmp(因此切换到此处,或在或/ / ...~/tmp中使用专用区域)。/var~/var~/.local$XDG_RUNTIME_DIR

    \n
  • \n
  • 该代码中没有任何特定于 bash 的内容,因此无需添加 bash 依赖项。

    \n
  • \n
  • 其中 的-n额外参数是perl脚本的输入

    \n
  • \n
  • 正如克里斯已经说过的,你的引用有问题。

    \n
  • \n
  • 你有AUTOSERV/AUTOSERVE差异。

    \n
  • \n
  • .是匹配任何单个字符的正则表达式运算符。使用\\.[.]来匹配文字点。

    \n
  • \n
  • 请注意, 的用法date是 GNU 特定的。并非所有date实现都支持某个-d选项,在那些支持的实现中,它可能用于完全不相关的东西,例如 BSD 上的东西,或者它们无法识别的东西yesterday(例如datebusybox 或 toybox 的)。 perl如果您需要将该脚本移植到非 GNU 系统,还可以执行日期操作\xc2\xb9。

    \n
  • \n
  • 您可以轻松地将其更改为使用单个正则表达式,例如:

    \n
    /STATUS:\\s+(?:SUCCESS|FAILURE|TERMINATED)\\b.+MACHINE:\\s+(\\w+.\\w+.\\w+)$/\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • 如果您希望机器列表按词法排序,请替换keys %count为。sort cmp, keys %count

    \n
  • \n
  • exec,在像这样的包装器脚本中常见的只是保存一个进程。perl它告诉 shell在同一进程中运行而不是在子进程中运行并等待它。cmd确实fork()+ exec(cmd)& wait(child), while exec cmd(也许应该被称为nofork cmd) 就是exec(cmd)这样,即使输入时间较长,但系统运行起来更简单/更短,并且使用更少的资源。

    \n
  • \n
  • %m%d%Y不是一个很好的时间戳格式选择。它是不明确的,并且它的词汇顺序(如 的输出中ls)与时间顺序不匹配。%Y-%m-%d或者%F简称为更好,因为它被普遍认可,并且按词汇时间顺序排序(至少从 0001 到 9999 年)。

    \n
  • \n
  • cat是连接文件的命令,对一个文件使用它没有什么意义。使用cmd < input > output(or <input cmd >output,但不是 cmd > output < input) 的好处是,如果input无法打开阅读,则cmd不会运行并且output不会被破坏。

    \n
  • \n
\n
\n

\xc2\xb9 例如,在这里添加-MPOSIXand aBEGIN{@t = localtime; $t[3]--; $dt = strftime "%m%d%Y", @t}或什至作为 hack 只是-M'POSIX;@t = localtime; $t[3]--; $dt = strftime "%m%d%Y", @t'

\n


roa*_*ima 5

我突然想到的一个问题是第二行perl

perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", '$ARGV[1]', $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD
Run Code Online (Sandbox Code Playgroud)

您正好可以及时使用单引号$ARGV[1],因此 shell 可以尝试解析它。$ARGV通常不会设置shell 变量,因此传递给的结果行perl是这样的:

perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", [1], $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD
Run Code Online (Sandbox Code Playgroud)

这在语法上是有效的(但不太可能有用),因此您不会收到任何错误。

如果删除行中间的两个单引号,您几乎会得到看起来像您想要的东西。您需要换出一个显式循环来从标准输入-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)