排序 bash 脚本错误

Sorting bash script bug

我正在尝试按日期对证书进行排序,只将最新的证书留在单独的文件中。

这是示例主机的示例 pki_certs.res 输入文件,其中包含我需要排序的过去未排序的证书列表:

下面是排序和弹出最后一个的脚本:

cat "${_file}" | sort -k10,10 | sed -e 's/Not After : //' -e 's/GMT/GMT;/' | grep "${_domain}" | \
  while read line; do
    _first=`echo $line | cut -d';' -f1`
    _second=`echo $line | cut -d';' -f2-`
    _date=`date -d "${_first}" +%Y%m%d%H%M`
    echo "$_date $_second"
  done |sort -k 3,3 -k 1,1r | awk "{if (i[$3] < $1) i[$3]=$1} END{for(x in i){ print x\" \"i[x] }}" | \
  sed -e 's/CN=//g' | sort -k 2,2 > pki_certs.final.sorted

问题是排序将 pki_certs.final.sorted 文件中的最后一个留在最近的之前。

预期输出:

Apr 7 20:09:26 2023

但我没有得到这个输出:

Apr 12 18:12:02 2022

关于我遗漏了什么的任何想法

(对我来说)不清楚 OP 的最终 objective 所以 fwiw ...

Assumptions/Understandings:

  • objective #1 - 打印 latest/newest 日期
  • objective #2 - 打印包含 latest/newest 日期
  • 的行
  • objective #3 - 根据日期时间戳对整个输入文件进行排序
  • 所有证书详细信息位于一行中(即,单个证书的详细信息不跨越多行)
  • 每行包含一个格式为 'mmm dd HH:MM:SS yyyy'
  • 的日期时间戳

一个想法使用 GNU awk:

awk '   # define field pattern as " mmm dd HH:MM:SS yyyy "
BEGIN { FPAT=" [[:alpha:]]{3} [0-9]{1,2} [0-2][0-9]:[0-5][0-9]:[0-5][0-9] [0-9]{4} "

        # build array of months to allow converting from 3-character to numeric
        n=split("Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Nov:Dec",arr,":")
        for (i=1;i<=n;i++)
            month[(arr[i])]=i
      }

      { n=split(,arr)                        # split 1st (and only?) FPAT matching field on white space
        gsub(/:/," ",arr[3])                   # convert ":" to " "

        # convert current datetime stamp to epoch seconds
        epoch=mktime(arr[4] " " month[arr[1]] " " arr[2] " " arr[3])

        if (epoch > maxepoch +0) {
           maxepoch=epoch
           dt=                               # save current "max" datetime stamp
        }
        lines[epoch]=[=10=]                        # save current line, indexed by epoch
      }

END   { print dt                               # objective #1: print max datetime stamp
        print lines[maxepoch]                  # objective #2: print cert line containing max datetime stamp

        PROCINFO["sorted_in"]="@ind_num_asc"   # sort array by numeric index in ascending order
        for (i in lines)                       # objective #3: print lines[] array in epoch ascending order
            print lines[i] > "pki_certs.final.sorted"
      }
' cert.dat

备注:

  • 需要 GNU awk 用于 a) FPAT、b) mktime() 函数和 c) PROCinfo["sorted_in"] 排序指令
  • 替换所有 OP 的当前代码
  • OP 可以根据预期结果修改 END {...}

这会生成:

# objective #1:

 Apr 7 20:09:26 2023

# objective #2:

pki_certs.res:Not After : Apr 7 20:09:26 2023 GMT DNS:MDVARTREPO01.cpp.nonlive

# objective #3:

$ cat pki_certs.final.sorted
pki_certs.res:Not After : May 7 11:58:19 2020 GMT Subject: CN=MDVARTREPO01.cpp.nonlive DNS:MDVARTREPO01.cpp.nonlive
pki_certs.res:Not After : May 7 12:05:44 2020 GMT Subject: CN=MDVARTREPO01.cpp.nonlive DNS:MDVARTREPO01.cpp.nonlive
pki_certs.res:Not After : Apr 8 17:06:54 2021 GMT Subject: CN=MDVARTREPO01.cpp.nonlive DNS:MDVARTREPO01.cpp.nonlive
pki_certs.res:Not After : Apr 9 17:02:43 2021 GMT Subject: CN=MDVARTREPO01.cpp.nonlive DNS:MDVARTREPO01.cpp.nonlive
pki_certs.res:Not After : Apr 9 17:42:27 2021 GMT Subject: CN=MDVARTREPO01.cpp.nonlive DNS:MDVARTREPO01.cpp.nonlive
pki_certs.res:Not After : Apr 17 09:09:35 2021 GMT Subject: CN=MDVARTREPO01.cpp.nonlive DNS:MDVARTREPO01.cpp.nonlive
pki_certs.res:Not After : Apr 12 18:12:02 2022 GMT Subject: CN=MDVARTREPO01.cpp.nonlive DNS:MDVARTREPO01.cpp.nonlive
pki_certs.res:Not After : Apr 7 20:09:26 2023 GMT DNS:MDVARTREPO01.cpp.nonlive

使用任何版本的强制性 Unix 工具 awk、sort、cut 和 head 应用 DSU (Decorate/Sort/Undecorate) 习语以获得整行输出:

$ awk '{printf "%04d%02d%02d%s\t%s\n", , (index("JanFebMarAprMayJunJulAugSepOctNovDec",)+2)/3, , , [=10=]}' file |
    sort -r | head -1 | cut -f2-
pki_certs.res:Not After : Apr 7 20:09:26 2023 GMT DNS:MDVARTREPO01.cpp.nonlive

或只是日期:

$ awk '{printf "%04d%02d%02d%s\t%s\n", , (index("JanFebMarAprMayJunJulAugSepOctNovDec",)+2)/3, , , " "" "" "}' file |
    sort -r | head -1 | cut -f2-
Apr 7 20:09:26 2023

第一个 awk 在每行的前面添加一个日期+时间的可排序版本,然后 sort 按该时间戳对其进行排序,然后 cut 删除 awk 添加的字符串。查看每个步骤的中间输出显示它是如何工作的:

$ awk '{printf "%04d%02d%02d%s\t%s\n", , (index("JanFebMarAprMayJunJulAugSepOctNovDec",)+2)/3, , , " "" "" "}' file
2023040720:09:26        Apr 7 20:09:26 2023
2020050712:05:44        May 7 12:05:44 2020
2021040817:06:54        Apr 8 17:06:54 2021
2020050711:58:19        May 7 11:58:19 2020
2021040917:42:27        Apr 9 17:42:27 2021
2021041709:09:35        Apr 17 09:09:35 2021
2021040917:02:43        Apr 9 17:02:43 2021
2022041218:12:02        Apr 12 18:12:02 2022

$ awk '{printf "%04d%02d%02d%s\t%s\n", , (index("JanFebMarAprMayJunJulAugSepOctNovDec",)+2)/3, , , " "" "" "}' file | sort -r
2023040720:09:26        Apr 7 20:09:26 2023
2022041218:12:02        Apr 12 18:12:02 2022
2021041709:09:35        Apr 17 09:09:35 2021
2021040917:42:27        Apr 9 17:42:27 2021
2021040917:02:43        Apr 9 17:02:43 2021
2021040817:06:54        Apr 8 17:06:54 2021
2020050712:05:44        May 7 12:05:44 2020
2020050711:58:19        May 7 11:58:19 2020

$ awk '{printf "%04d%02d%02d%s\t%s\n", , (index("JanFebMarAprMayJunJulAugSepOctNovDec",)+2)/3, , , " "" "" "}' file | sort -r | head -1
2023040720:09:26        Apr 7 20:09:26 2023

$ awk '{printf "%04d%02d%02d%s\t%s\n", , (index("JanFebMarAprMayJunJulAugSepOctNovDec",)+2)/3, , , " "" "" "}' file | sort -r | head -1 | cut -f2-
Apr 7 20:09:26 2023

顺便说一句,在您的代码中:

awk "{if (i[$3] < $1) i[$3]=$1} END{for(x in i){ print x\" \"i[x] }}"

您必须转义所有这些符号,因为您使用了错误的引号,因此请 shell 在 awk 看到它之前解释脚本。只是不要那样做,除非有特殊原因,否则请一如既往地使用单引号:

awk '{if (i[] < ) i[]=} END{for(x in i){ print x" "i[x] }}'