输入文件为/var/log/error.log
Apr 7 05:58:05 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical
Apr 7 09:21:12 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical
Apr 7 13:05:57 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical
我的脚本
#!/bin/ksh
d1=$(date --date="-50 min" "+%b %_d %H:%M")
d2=$(date "+%b %_d %H:%M")
while read line; do
[[ $line > $d1 && $line < $d2 || $line =~ $d2 ]] && echo $line | egrep -wi '[Cc]ritical' >> /mnt/dinesh/ksh-scripts/draft/text.txt
done < /var/log/error.log
运行脚本时,系统时间在13:06:05
附近
因此,根据脚本,/mnt/dinesh/ksh-scripts/draft/text.txt
文件应具有我的error.log
的最后一个条目,但mnt/dinesh/ksh-scripts/draft/text.txt
中没有任何行。
[有人可以帮助我解决此问题。
代码逻辑的本质问题是使用字符串比较来比较日期。在字符串比较中,Apr 17
小于Apr 2
,Feb 1
小于Jan 1
。
相反,POSIX外壳程序中的模式是将日期从1970-01-01 00:00:00 UTC(也称为“时代”)开始转换为秒。
对于date
命令,以秒为单位的时间分配方式是将FORMAT
设置为+%s
,例如date +%s
。
在ksh
中,有一个printf
格式说明符T
,用于将诸如从日志文件中读取的行之类的字符串转换为自该纪元以来的秒数。参见https://www.unix.com/302701867-post4.html?s=860f6c21431fa69ef9e083161f93739d
这里是重写脚本以通过将比较转换为秒来解决逻辑错误:
#!/bin/ksh
ERROR_LOG=/tmp/error.log
FILTERED_ERROR_LOG=/tmp/text
now_secs=$(date "+%s")
start_secs=$(date --date="-30000 min" +%s)
while read line; do
# Below stderr is redirected since I can't figure out how to get just the first part
# of the date parsed skipping the other stuff after that.
# Even though there is warning printed, the time comes out parsed correctly
line_secs=$(printf "%(%#)T" "$line" 2>/dev/null)
if (( $line_secs > $start_secs && line_secs < now_secs || line_secs == now_secs )); then
echo $line | egrep -wi '[Cc]ritical' >> $FILTERED_ERROR_LOG
fi
done < $ERROR_LOG
一些其他注释。
似乎您更广泛地希望了解此类调试此类问题。如您所见,在StackOverflow上问这样一个非常特殊的问题可能不会引起其他人的兴趣,因此可能需要一段时间才能获得任何有意义的响应。
基本跟踪选项是-x
。让我们在上面的脚本上运行它:
ksh -x /tmp/bug.ksh
+ ERROR_LOG=/tmp/error.log
+ FILTERED_ERROR_LOG=/tmp/text
+ date +%s
+ now_secs=1587473181
+ date '--date=-30000 min' +%s
+ start_secs=1585673181
+ 0< /tmp/error.log
+ read line
+ printf '%(%#)T' 'Apr 7 05:58:05 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical'
+ 2> /dev/null
+ line_secs=1586253485
+ (( 1586253485 > 1585673181 && line_secs < now_secs || line_secs == now_secs ))
+ egrep -wi '[Cc]ritical'
+ echo Apr 7 05:58:05 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical
+ 1>> /tmp/text
+ read line
+ printf '%(%#)T' 'Apr 7 09:21:12 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical'
+ 2> /dev/null
+ line_secs=1586265672
+ (( 1586265672 > 1585673181 && line_secs < now_secs || line_secs == now_secs ))
+ egrep -wi '[Cc]ritical'
+ echo Apr 7 09:21:12 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical
+ 1>> /tmp/text
+ read line
+ printf '%(%#)T' 'Apr 7 13:05:57 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical'
+ 2> /dev/null
+ line_secs=1586279157
+ (( 1586279157 > 1585673181 && line_secs < now_secs || line_secs == now_secs ))
+ egrep -wi '[Cc]ritical'
+ echo Apr 7 13:05:57 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical
+ 1>> /tmp/text
+ read line
我以几种方式更改了您的代码,以使其更易于调试和解决问题。将您要执行的操作作为附加语句添加到日志文件中,作为单独的语句放在if
中,而不是作为&&
条件的一部分,在上述跟踪中,您将看到它显示为单独的语句线。特别是当您看到:
+ egrep -wi '[Cc]ritical'
+ echo Apr 7 05:58:05 ip-172-31-19-169 kernel: BIOS-provided physical RAM map: critical
我们知道测试成功。如果您想知道时间测试是由于第一部分(时间不够新)还是第二部分(时间太新)还是与第三部分(现在是时间)不匹配而失败,则将其放入他们自己的if
或else
将允许跟踪显示该信息。
但是我做了其他事情使调试更简单。虽然带有(( $line_secs > $start_secs ))
这样的结构的ksh会警告:
/tmp/bug.ksh: warning: line 14: variable expansion makes arithmetic evaluation less efficient
因为它更喜欢使用效率更高的(( line_secs > start_secs ))
,因此,通过使用$
添加额外的替换,您将在跟踪中看到比较的值。这可以帮助您确定哪个部分出了故障。
[如果您已经知道这一点并尝试过,那么我想如果您使用上面显示的跟踪图进入StackOverflow,您会得到更多的响应,然后问题稍微缩小为“为什么这种比较失败了?”并且您可能会更快获得更多回复。
[最后,我要提到的是我为比2014-12-24,ksh93
和zsh
更新的现代bash
编写了调试器。但是,当我在kshdb
上尝试上述示例时,它失败了,我不确定为什么。