shell脚本NO行使用时间戳从输入文件追加

问题描述 投票:0回答:1

输入文件为/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中没有任何行。

[有人可以帮助我解决此问题。

linux bash shell ksh
1个回答
0
投票

代码逻辑的本质问题是使用字符串比较来比较日期。在字符串比较中,Apr 17小于Apr 2Feb 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

我们知道测试成功。如果您想知道时间测试是由于第一部分(时间不够新)还是第二部分(时间太新)还是与第三部分(现在是时间)不匹配而失败,则将其放入他们自己的ifelse将允许跟踪显示该信息。

但是我做了其他事情使调试更简单。虽然带有(( $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,ksh93zsh更新的现代bash编写了调试器。但是,当我在kshdb上尝试上述示例时,它失败了,我不确定为什么。

© www.soinside.com 2019 - 2024. All rights reserved.