ARGV[] 不接受参数

ARGV[] 不接受参数

我有一个 shell 脚本,我试图向其传递参数dateARGV[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

此 shell 脚本过滤此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是脚本的输入

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

  • 你有AUTOSERV/AUTOSERVE差异。

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

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

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

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

  • exec,在像这样的包装器脚本中很常见的就是保存一个进程。它告诉 shellperl在同一进程中运行,而不是在子进程中运行并等待它。does 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(或<input cmd >output, 但是不是 cmd > output < input)还有一个好处是,如果input无法打开阅读,则cmd不会运行并且output不会被破坏。


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

答案2

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

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

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

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"

这是一个可供您选择的管道,它将获取您的源文件并生成指定的输出:

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]被 shell 看到,而不是perl。接下来,您实际上没有ARGV这里的数组,因为您告诉perl从标准输入读取,因为您正在使用-n

$ perl -le 'print "$ARGV[0]"' foo 
foo
$ perl -nle 'print "$ARGV[0]"' foo
$ 

你可以任何一个use-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

相关内容