Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Linux中,如何在不扫描整个文件的情况下显示过去5分钟添加的文件内容?_Linux_Bash_Unix - Fatal编程技术网

在Linux中,如何在不扫描整个文件的情况下显示过去5分钟添加的文件内容?

在Linux中,如何在不扫描整个文件的情况下显示过去5分钟添加的文件内容?,linux,bash,unix,Linux,Bash,Unix,我有数据库错误日志文件,它将不断增长。 现在我想每隔5分钟对该文件设置一些错误监视。 问题是我不想每5分钟扫描一次整个文件(在监视cron执行时),因为它将来可能会变得很大。每5分钟扫描一次整个(大)文件将消耗更多的资源。 所以我只想扫描在最后5分钟间隔内插入/写入日志的行。 日志中记录的每个错误都有时间戳,如下所示: 180418 23:45:00[错误]mysql收到信号11 所以我只想用pattern[ERROR]搜索过去5分钟添加的行(不是整个文件),然后将输出放到另一个文件中 请帮帮我

我有数据库错误日志文件,它将不断增长。 现在我想每隔5分钟对该文件设置一些错误监视。 问题是我不想每5分钟扫描一次整个文件(在监视cron执行时),因为它将来可能会变得很大。每5分钟扫描一次整个(大)文件将消耗更多的资源。 所以我只想扫描在最后5分钟间隔内插入/写入日志的行。 日志中记录的每个错误都有时间戳,如下所示:

180418 23:45:00[错误]mysql收到信号11

所以我只想用pattern[ERROR]搜索过去5分钟添加的行(不是整个文件),然后将输出放到另一个文件中

请帮帮我。 如果你需要更多关于我的问题的澄清,请随意

我正在使用RHEL 7,并试图通过bash shell脚本来实现上述监视 这将继续上一个实例停止的地方。如果每5分钟运行一次,它将扫描5分钟的数据

请注意,此实现可以有意地扫描调用运行期间添加的数据两次。这有点草率,但扫描重叠数据两次要比根本不读取数据安全得多,如果依赖
cron
按计划运行程序,可能会出现这种风险(同样,如果系统忙,
sleep
s可以在请求的时间内运行)


另一种方法可以是这样的:

DB_FILE="FULL_PATH_TO_YOUR_DB_FILE"

current_db_size=$(du -b "$DB_FILE" | cut -f 1)
if [[ ! -a SOME_PATH_OF_YOUR_CHOICE/last_size_db_file ]] ; then
    tail --bytes $current_db_size $DB_FILE > SOME_PATH_OF_YOUR_CHOICE/log-file_$(date +%Y-%m-%d_%H-%M-%S)
else
    if [[ $(cat last_size_db_file) -gt $current_db_size ]] ; then
        previously_readed_bytes=0
    else
        previously_readed_bytes=$(cat last_size_db_file)
    fi
    new_bytes=$(($current_db_size - $previously_readed_bytes))
    tail --bytes $new_bytes $DB_FILE > SOME_PATH_OF_YOUR_CHOICE/log-file_$(date +%Y-%m-%d_%H-%M-%S)
fi
printf $current_db_size > SOME_PATH_OF_YOUR_CHOICE/last_size_db_file
*/5 * * * * PATH_TO_YOUR_SCRIPT
这将打印
DB\u文件的所有字节
以前未打印到您选择的
log-FILE\u$(日期+%Y-%m-%d\u%H-%m-%S)

请注意,
$(日期+%Y-%m-%d\u%H-%m-%S)
将是创建日志文件时的当前“完整”日期

您可以将其设置为脚本,并使用
cron
每五分钟执行一次该脚本;大概是这样的:

DB_FILE="FULL_PATH_TO_YOUR_DB_FILE"

current_db_size=$(du -b "$DB_FILE" | cut -f 1)
if [[ ! -a SOME_PATH_OF_YOUR_CHOICE/last_size_db_file ]] ; then
    tail --bytes $current_db_size $DB_FILE > SOME_PATH_OF_YOUR_CHOICE/log-file_$(date +%Y-%m-%d_%H-%M-%S)
else
    if [[ $(cat last_size_db_file) -gt $current_db_size ]] ; then
        previously_readed_bytes=0
    else
        previously_readed_bytes=$(cat last_size_db_file)
    fi
    new_bytes=$(($current_db_size - $previously_readed_bytes))
    tail --bytes $new_bytes $DB_FILE > SOME_PATH_OF_YOUR_CHOICE/log-file_$(date +%Y-%m-%d_%H-%M-%S)
fi
printf $current_db_size > SOME_PATH_OF_YOUR_CHOICE/last_size_db_file
*/5 * * * * PATH_TO_YOUR_SCRIPT
我的做法如下:

首先,到目前为止,阅读整个日志一次。 如果到达末尾,收集并读取一段时间内的新行(在我的示例中为更快的测试,为9秒,而我的虚拟服务器每3秒追加一次日志文件)

在时间跨度之后,回显缓存,清除缓存(数组
arr
),循环并休眠一段时间,以便此过程不会占用所有CPU时间

首先,我的虚拟日志文件编写器:

#!/bin/bash
#
# dummy logfile writer
#
while true
do
  s=$(( $(date +%s) % 3600))
  echo $s server msg
  sleep 3
done >> seconds.log
通过
/seconds-out.sh&
启动

现在更复杂的部分是:

#!/bin/bash
#
# consume a logfile as written so far. Then, collect every new line
# and show it in an interval of $interval
#
interval=9 # 9 seconds
#
printf -v secnow '%(%s)T' -1 
start=$(( secnow % (3600*24*365) ))
declare -a arr
init=false
while true
do
  read line
  printf -v secnow '%(%s)T' -1 
  now=$(( secnow % (3600*24*365) ))

  # consume every line created in the past
  if (( ! init ))
  then
    # assume reading a line might not take longer than a second (rounded to whole seconds)
    while (( ${#line} > 0 && (now - start) < 2 ))
    do
      read line
      start=$now
      echo -n "." # for debugging purpose, remove  
      printf -v secnow '%(%s)T' -1 
      now=$(( secnow % (3600*24*365) ))
    done
    init=1
    echo "init=$init" # for debugging purpose, remove 
  # collect new lines, display them every $interval seconds
  else
    if ((${#line} > 0 ))
    then
      echo -n "-" # for debugging purpose, remove 
      arr+=("read: $line \n")
    fi
    if (( (now - start) > interval ))
    then
      echo -e "${arr[@]]}"
      arr=()
      start=$now
    fi
  fi
  sleep .1
done < seconds.log
  • 每个点代表过去和现在的日志文件行
  • 每个破折号表示收集的日志文件行

如果唯一的更改是附录,您可以将seek()设置为旧大小并从中读取。有关C调用,请参见
man 2 seek
;几乎每种编程语言(您没有告诉我们您使用的是哪种语言)都可以访问它。要确定停止读取的位置并序列化该位置,可以使用
tell()
调用……如果不能序列化字节偏移量(应该这样做!),另一种方法是对分文件以快速找到起始时间戳。请参阅,以及其中提到的@ole tange的
bsearch
工具。(顺便说一句,我希望您的日期格式是
yymmd HH:MM:SS
;如果是
DDMMYY
,您的工作就更难了。)@CharlesDuffy是的,是YYMMDD格式,我希望通过shell脚本实现此监视。您应该在问题中指定。如果您正在舒适地使用
#,请将其标记为
shell
,或者标记为更具体的shell,例如
bash
/bin/bash
shebang.PIPESTATUS可靠吗?我记得有评论说它不是。它是bash主义,所以在非bash shell上不可靠。这是我能想到的唯一一种“不可靠”的方式。
$(cat foo)
$要慢得多(
while(true)
,而不是
while true
?这对可读性来说是一个很大的性能代价。我还建议
printf-v现在就开始“%(%s)T'-1
,如果目标是现代bash,则可以避免调用
date
对性能的影响。当然,也可以使用
printf
而不是
echo-n
echo-e
。更大的问题是——正如我下面所说的,您正在执行一个到最后一个时间戳的读取循环,而不是直接在那里进行查找?读取所需的时间仍然会很长与总文件大小成线性比例,这违背了此问题的全部目的。问题是
我不想每5分钟扫描一次整个文件
,因此此操作仅扫描整个文件一次。然后每5分钟收集一次数据(每9秒测试一次)。感谢您提供有关printf-v…T和
的提示,而true
。是的,但不扫描文件的目的是不要随着文件变长而变慢—OP的目的很明显,它们的目的是限制资源消耗。如果您读取这些行并将其丢弃,您仍然在读取它们,因此您需要支付I/O成本—whi对于bash的
read
,ch的价格特别高,因为它一次读取一个字节。
./read-seconds.sh 
.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................init=1
---read: 1688 server msg 
 read: 1691 server msg 
 read: 1694 server msg 

---read: 1697 server msg 
 read: 1700 server msg 
 read: 1703 server msg 

----read: 1706 server msg 
 read: 1709 server msg 
 read: 1712 server msg 
 read: 1715 server msg 

^C