排序 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] }}'
我正在尝试按日期对证书进行排序,只将最新的证书留在单独的文件中。
这是示例主机的示例 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] }}'