提问人:Paul Hodges 提问时间:12/14/2022 最后编辑:brian d foyPaul Hodges 更新时间:12/15/2022 访问量:140
按格式不一致的经过时间字段排序(K8S 事件按事件发生后的实际时间排序)
sorting by inconsistently formatted elapsed time field ( k8s events by actual time since event )
问:
编辑
我发现自己经常在寻找一些事情是否停止了发生。为此,按时间顺序查看事件会有所帮助......
这个解决方案似乎有效,但格式仍然让我发疯......
我“有点”有效的解决方案——
kubectl get events |
sed -E '/^[6789][0-9]s/{h; s/^(.).*/\1/; y/6789/0123/; s/^(.)/01m\1/;
x; s/^.(.*)/\1/; H;
x; s/\n//; };
s/^10([0-9]s)/01m4\1/; s/^11([0-9]s)/01m5\1/; s/^([0-9]s)/00m0\1/; s/^([0-9]+s)/00m\1/;
s/^([0-9]m)/0\1/; s/^([0-9]+m)([0-9]s)/\10\2/;
s/^L/_L/;' | sort -r
...这对我来说似乎有点矫枉过正。
以空格分隔的左对齐字段没有前导零,仅报告最多 2m 的秒数,然后报告最多 5m,之后似乎只报告。[0-9]+s
[0-9]+m[0-9]+s
[0-9]+m
有人有一个简短的,甚至可能很简单,更容易阅读的解决方案吗?
没有工具的偏好(、、、本机等),只要它有效并且可能已经安装在我需要工作的任何地方。sed
awk
perl
bash
这不是一个高优先级,但似乎是一个有趣的小挑战,我想我会分享。
我的测试数据:
$: cat sample
LAST ...
28s ...
2m22s ...
46m ...
7s ...
75s ...
119s ...
具有所需输出的结果 -
$: sed -E '/^[6789][0-9]s/{h; s/^(.).*/\1/; y/6789/0123/; s/^(.)/01m\1/;
x; s/^.(.*)/\1/; H;
x; s/\n//; };
s/^10([0-9]s)/01m4\1/; s/^11([0-9]s)/01m5\1/; s/^([0-9]s)/00m0\1/; s/^([0-9]+s)/00m\1/;
s/^([0-9]m)/0\1/; s/^([0-9]+m)([0-9]s)/\10\2/;
s/^L/_L/;' sample | sort -r
_LAST ...
46m ...
02m22s ...
01m59s ...
01m15s ...
00m28s ...
00m07s ...
我任意转换为现有通用输出格式的标准化版本,只是为了让它能够轻松传输给团队的其他成员。无论哪种方式,它都只是用于“盯着”数据,所以只要它易于阅读,其他格式就不是问题。
虽然理论上可以包括小时和天,但这种旧事件通常不会被这个工具报告,并且超出了这个问题的范围,如果需要,我可能会推断出提出的任何解决方案。由于我可以从这种方法中获得订单,因此我真的只是在寻找优雅的格式选项。
对 Daweo 解决方案的笨拙改编,带有格式 -awk
$: awk '/^[0-9]/{ if($1!~/m/){$1="0m" $1}; split($1,arr,/m/);
t=arr[1]*60+arr[2]; m=(t-(t%60))/60; s=t-(m*60);
m=sprintf("%02dm",m); if(s){ s=sprintf("%02ds",s) } else s="";
$1=sprintf("%s%s",m,s); print; } /^L/{print "_"$0}' sample |
sort -r
_LAST ...
46m ...
02m22s ...
01m59s ...
01m15s ...
00m28s ...
00m07s ...
其他人仍然表示赞赏。
答:
我会利用 GNU 来完成这个任务,让内容成为AWK
file.txt
2m36s ...
2m9s ...
28s ...
2m22s ...
2m6s ...
46m ...
7s ...
45m ...
3m9s ...
31m ...
16m ...
75s ...
74s ...
67s ...
46m ...
63s ...
2m15s ...
119s ...
16m ...
75s ...
74s ...
69s ...
46m ...
31m ...
16m ...
75s ...
62s ...
然后
awk '$1!~/m/{$1="0m" $1}{split($1,arr,/m/);$1=arr[1]*60+arr[2];print}' file.txt
给出输出
156 ...
129 ...
28 ...
142 ...
126 ...
2760 ...
7 ...
2700 ...
189 ...
1860 ...
960 ...
75 ...
74 ...
67 ...
2760 ...
63 ...
135 ...
119 ...
960 ...
75 ...
74 ...
69 ...
2760 ...
1860 ...
960 ...
75 ...
62 ...
解释:如果第一个字段中没有我前面的字段,那么我在字符处使用拆分
函数,然后我计算值:我乘以 60 之前的内容转换为秒,然后将后面的内容相加以获得以秒为单位的总和,对于没有秒部分的行,秒部分是空字符串,用于算术时变为零。然后,可以对此输出进行数字排序,即m
0m
m
m
awk '$1!~/m/{$1="0m" $1}{split($1,arr,/m/);$1=arr[1]*60+arr[2];print}' file.txt | sort -n
给出输出
7 ...
28 ...
62 ...
63 ...
67 ...
69 ...
74 ...
74 ...
75 ...
75 ...
75 ...
119 ...
126 ...
129 ...
135 ...
142 ...
156 ...
189 ...
960 ...
960 ...
960 ...
1860 ...
1860 ...
2700 ...
2760 ...
2760 ...
2760 ...
(在 GNU Awk 5.0.1 和 sort (GNU coreutils) 8.30 中测试)
评论
printf
##m##s
sed
sprintf
$1=arr[1]*60+arr[2]
$1=sprintf("%04d",arr[1]*60+arr[2])
如果示例中显示了所有可能的时间格式,则可能会起作用。它显示了文件、输出和最终排序,为清楚起见,粘贴在一起。
它查找 ,乘以 60 并添加任何现有的秒数。
如果未找到,则仅打印秒数。m
m
$ paste sample <(awk '/m/{split($1,ar,"m"); print ar[1] * 60 + ar[2]}
!/m/{print $1 * 1}' sample) | sort -nk 3
7s ... 7
28s ... 28
62s ... 62
63s ... 63
67s ... 67
69s ... 69
74s ... 74
74s ... 74
75s ... 75
75s ... 75
75s ... 75
119s ... 119
2m6s ... 126
2m9s ... 129
2m15s ... 135
2m22s ... 142
2m36s ... 156
3m9s ... 189
16m ... 960
16m ... 960
16m ... 960
31m ... 1860
31m ... 1860
45m ... 2700
46m ... 2760
46m ... 2760
46m ... 2760
评论
I'm really only looking for elegant formatting options
LAST
_LAST
仅使用 GNU awk:
awk '{match($1, /[0-9]+m/, m); match($1, /[0-9]+s/, s)
arr[m[0]*60 + s[0]] = $0
}
END {
n = asorti(arr, sorted, "@ind_num_asc")
for(i = 1; i <= n; i++)
print arr[sorted[i]]
}
' sample
看起来比一堆链子干净一点。它打印:
LAST ...
7s ...
28s ...
62s ...
63s ...
67s ...
69s ...
74s ...
75s ...
119s ...
2m6s ...
2m9s ...
2m15s ...
2m22s ...
2m36s ...
3m9s ...
16m ...
31m ...
45m ...
46m ...
因为我实际上认为您希望显示最新的条目 靠近顶部,如果不是,只需将ind_num_asc更改为ind_num_desc。
评论
用sed
$ cat script.sed
1! {
s/^[0-9]s/0m0&/
/^[0-9]{2,}s/ {
s/^6/01m0/
s/^7/01m1/
s/^8/01m2/
s/^9/01m3/
s/^10/01m4/
s/^11/01m5/
s/^[0-5][0-9]s/0m&/
}
s/^([0-9]m)([0-9]s)/0\10\2/
s/^([0-9]+m)([0-9]s)/\10\2/
s/^[0-9]m/0&/
}
1s/^/_/
运行
$ sed -Ef script.sed input_file | sort -r
_LAST ...
46m ...
46m ...
46m ...
45m ...
31m ...
31m ...
16m ...
16m ...
16m ...
03m09s ...
02m36s ...
02m22s ...
02m15s ...
02m09s ...
02m06s ...
01m59s ...
01m15s ...
01m15s ...
01m15s ...
01m14s ...
01m14s ...
01m09s ...
01m07s ...
01m03s ...
01m02s ...
00m28s ...
00m07s ...
如果你正在寻找一个通用的实用程序来“规范化”报告的值,这里有一个“hack”演示了这种能力。
#!/bin/bash
DBG=1
INPUT=`basename "$0" ".sh" `.input
cat >"${INPUT}" <<"EnDoFiNpUt"
2m36s ...
2m9s ...
28s ...
2m22s ...
2m6s ...
46m ...
7s ...
45m ...
3m9s ...
31m ...
16m ...
75s ...
74s ...
67s ...
46m ...
63s ...
2m15s ...
119s ...
16m ...
75s ...
74s ...
69s ...
46m ...
31m ...
16m ...
75s ...
62s ...
EnDoFiNpUt
#cat >"${INPUT}" <<"EnDoFiNpUt"
#119s ...
#EnDoFiNpUt
awk -v dbg="${DBG}" 'BEGIN{
split("", times) ;
items=0 ;
}{
if( $0 == "" ){
exit ;
}else{
if( dbg == 1 ){ print "\n"$0 | "cat >&2" ; } ;
rem=$1 ;
items++ ;
posH=index( rem, "h" ) ;
if( posH == 0 ){
hr=0 ;
if( dbg == 1 ){ print "\thr = "hr | "cat >&2" ; } ;
posM=index( rem, "m" ) ;
if( posM == 0 ){
min=0 ;
if( dbg == 1 ){ print "\tmin = "min | "cat >&2" ; } ;
posS=index( rem, "s" ) ;
if( posS == 0 ){
if( rem = "" ){
sec=0 ;
}else{
minX=sprintf("%d", rem/60 ) ;
sec=rem-minX*60 ;
min=min+minX ;
if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
} ;
}else{
beg=substr( rem, 1, posS-1) ;
if( rem = "" ){
sec=0 ;
}else{
minX=sprintf("%d", beg/60 ) ;
sec=beg-minX*60 ;
min=min+minX ;
if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
} ;
} ;
}else{
min=substr( rem, 1, posM-1) ;
rem=substr( rem, posM+1 ) ;
if( dbg == 1 ){ print "\tmin = "min | "cat >&2" ; } ;
posS=index( rem, "s" ) ;
if( posS == 0 ){
if( rem = "" ){
sec=0 ;
}else{
minX=sprintf("%d", rem/60 ) ;
sec=rem-minX*60 ;
min=min+minX ;
if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
} ;
}else{
beg=substr( rem, 1, posS-1) ;
if( rem = "" ){
sec=0 ;
}else{
minX=sprintf("%d", beg/60 ) ;
sec=beg-minX*60 ;
min=min+minX ;
if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
} ;
} ;
} ;
}else{
hr=substr( rem, 1, posH-1) ;
rem=substr( rem, posH+1 ) ;
if( dbg == 1 ){ print "\thr = "hr | "cat >&2" ; } ;
posM=index( rem, "m" ) ;
if( posM == 0 ){
min=0 ;
if( dbg == 1 ){ print "\tmin = "min | "cat >&2" ; } ;
posS=index( rem, "s" ) ;
if( posS == 0 ){
if( rem = "" ){
sec=0 ;
}else{
minX=sprintf("%d", rem/60 ) ;
sec=rem-minX*60 ;
min=min+minX ;
if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
} ;
}else{
beg=substr( rem, 1, posS-1) ;
if( rem = "" ){
sec=0 ;
}else{
minX=sprintf("%d", beg/60 ) ;
sec=beg-minX*60 ;
min=min+minX ;
if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
} ;
} ;
}else{
min=substr( rem, 1, posM-1) ;
rem=substr( rem, posM+1 ) ;
if( dbg == 1 ){ print "\tmin = "min | "cat >&2" ; } ;
posS=index( rem, "s" ) ;
if( posS == 0 ){
if( rem = "" ){
sec=0 ;
}else{
minX=sprintf("%d", rem/60 ) ;
sec=rem-minX*60 ;
min=min+minX ;
if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
} ;
}else{
beg=substr( rem, 1, posS-1) ;
if( rem = "" ){
sec=0 ;
}else{
minX=sprintf("%d", beg/60 ) ;
sec=beg-minX*60 ;
min=min+minX ;
if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
} ;
} ;
} ;
if( dbg == 1 ){ print "\tsec = "sec | "cat >&2" ; } ;
} ;
times[items]=sprintf("%02dh%02dm%02ds", hr, min, sec ) ;
if( dbg == 1 ){ print "\t"times[items] | "cat >&2" ; } ;
} ;
}END{
if( dbg == 1 ){ print "Normalized Values:" } ;
for( i=1 ; i <= items ; i++ ){
print times[i] ;
} ;
}' "${INPUT}" > "${INPUT}.out"
echo ""
cat "${INPUT}.out"
echo ""
echo "Sorted Values:"
grep -v 'Normalized' "${INPUT}.out" | sort -n
评论
00h00m00s
我会先将所有内容转换为秒,然后将其打印为 ,例如:HH:MM:SS
$ cat tst.awk
BEGIN {
split("h m s",denoms)
fmts["s"] = fmts["m"] = fmts["h"] = "%02d"
mults["s"] = 1
mults["m"] = 60
mults["h"] = 60 * 60
}
sub(/^L/,"_L") {
print
next
}
{
time = $1
secs = 0
while ( match(time,/[0-9]+./) ) {
value = substr(time,1,RLENGTH-1)
denom = substr(time,RLENGTH)
time = substr(time,RLENGTH+1)
secs += value * mults[denom]
}
for ( i=1; i in denoms; i++ ) {
denom = denoms[i]
out = (i>1 ? out ":" : "") sprintf(fmts[denom],int(secs/mults[denom]))
secs %= mults[denom]
}
$1 = out
print | "sort -r"
}
$ awk -f tst.awk sample
_LAST ...
00:46:00 ...
00:02:22 ...
00:01:59 ...
00:01:15 ...
00:00:28 ...
00:00:07 ...
显然,如果您想包括天数,请在该部分添加定义,并类似地添加其他更长的持续时间。"d"
BEGIN
我要感谢大家的时间和贡献。
不出所料,我学到了一些东西。:)
一位同事看到了这一点,并私下向我发送了一个与我在问题中引用的同一页面中与此解决方案非常相似的解决方案,这有助于我了解我的问题和解决问题的内置方法。我在这里转发了一些解释,希望有人会发现它有用,也许可以帮助我完善自己的理解。
我对典型输出的主要问题是时间字段的格式(imo)非常不一致 - 我假设为了简洁起见,这可能很好 - 并且排序显然是按对象而不是时间(这在许多情况下也有意义)然后对对象的..lastTimestamp
作为记录,列表(以及其他许多)kubectl get --help
--sort-by='': If non-empty, sort list types using this field specification.
从 K8S 对象定义中注意到。以及The field specification is expressed as a JSONPath expression
-o, --output='': Output format.
它有一长串选项,包括让你“滚动自己的”。-o custom-columns=...
因此,我重建了正常输出,将有问题的第一列替换为实际的一致时间戳字段,并更改了默认排序顺序。
kubectl get events -o custom-columns="TIMESTAMP:{.lastTimestamp},REASON:{.reason},TYPE:{.type},OBJ_NAME:{.involvedObject.name},MESSAGE:{.message}" --sort-by={.lastTimestamp,.type,.reason}
更好的是,帮助文本直接引用了相关文档,因此我很快就能够将其转换为使用模板文件:
$: cat $HOME/.kube/custCol.txt
TIMESTAMP REASON TYPE OBJ_NAME MESSAGE
lastTimestamp reason type involvedObject.name message
$: kubectl get events -o custom-columns-file=$HOME/.kube/custCol.txt --sort-by={.metadata.creationTimestamp,.type,.reason}
仍然很长,但不需要通过管道连接到另一个我可以摸索逻辑的进程。为了简明扼要,更易于打字和阅读,我做了一个别名——
alias events='kubectl get events -o custom-columns-file=$HOME/.kube/custCol.txt --sort-by={.metadata.creationTimestamp,.type,.reason}
现在,我可以通过在末尾添加集群和命名空间来指定它们 -
$: events --context bulk -n bulk-sit1 | head -6
TIMESTAMP REASON TYPE OBJ_NAME MESSAGE
2022-12-14T16:15:18Z BackOff Warning seasonalsuspends-cron-job-1671034500-7c7md Back-off restarting failed container
2022-12-14T16:00:29Z BackOff Warning nonpays-cron-job-1671033600-7xhl7 Back-off restarting failed container
2022-12-14T16:06:51Z BackoffLimitExceeded Warning nonpays-cron-job-1671033600 Job has reached the specified backoff limit
2022-12-14T16:22:07Z BackoffLimitExceeded Warning seasonalsuspends-cron-job-1671034500 Job has reached the specified backoff limit
2022-12-14T15:45:20Z Completed Normal nonpays-cron-job-1671032700 Job completed
还没有弄清楚内置过滤器,所以现在我有一个功能,只用于警告 -grep
warnings() { local args=(get events --sort-by={.lastTimestamp,.type,.reason} "$@"
-o custom-columns=TIMESTAMP:{.lastTimestamp},REASON:{.reason},TYPE:{.type},OBJ_NAME:{.involvedObject.name},MESSAGE:{.message});
kubectl "${args[@]}" | grep ' Warning '
}
希望有人能从中得到一些用处。
评论