如何使用bash监视目录中新创建的文件?

时间:2016-04-25 18:45:40

标签: linux bash

我有一个由一堆日志文件组成的日志目录,一旦发生系统事件就会创建一个日志文件。我想编写一个oneline bash脚本,它始终监视文件列表并在终端上显示新创建的文件的内容。这是它的样子:

目前,我只需要显示整个目录的内容:

for f in *; do cat $f; done

它缺乏我想要的监控功能。我的系统的一个限制是我没有 watch命令。我也没有任何软件包管理器来安装花哨的工具。原始BSD就是我所拥有的。我确实有tail,我想的是像tail -F $(ls)这样的东西,但这会尾随每个文件而不是文件列表。

总之,我想修改我的脚本,以便我可以监视所有新创建的文件的内容。

6 个答案:

答案 0 :(得分:2)

  1. 第一种方法 - 在你的dir中使用一个隐藏文件(在我的例子中它有一个名称.watch)。然后你的单行可能看起来像:

    for f in $(find . -type f -newer .watch); do cat $f; done; touch .watch
    
  2. 第二种方法 - 使用inotify-tools:https://unix.stackexchange.com/questions/273556/when-a-particular-file-arrives-then-execute-a-procedure-using-shell-script/273563#273563

答案 1 :(得分:2)

监视器模式下使用inotifywait

首先是这个小小的演示:

  1. 打开一个终端并运行:

    ext=(php css other)
    while :;do
        subname=''
        ((RANDOM%10))||printf -v subname -- "-%04x" $RANDOM
        date >/tmp/test$subname.${ext[RANDOM%3]}
        sleep 1
      done
    

    这将创建名为/tmp/test.php/tmp/test.css/tmp/test.other的随机文件,但随机(大约1次/ 10),名称将为/tmp/test-XXXX.[css|php|other],其中XXXX是十六进制随机数。

  2. 打开另一个终端并运行:

    waitPaths=(/{home,tmp})
    while read file ;do
        if [ "$file" ] &&
         ( [ -z "${file##*.php}" ] || [ -z  "${file##*.css}" ] ) ;then
            (($(stat -c %Y-%X $file)))||echo -n new
            echo file: $file, content:
            cat $file
        fi
      done < <(
        inotifywait -qme close_write --format %w%f ${waitPaths[*]}
    )
    

    这可能会产生类似的结果:

    file: /tmp/test.css, content:
    Tue Apr 26 18:53:19 CEST 2016
    file: /tmp/test.php, content:
    Tue Apr 26 18:53:21 CEST 2016
    file: /tmp/test.php, content:
    Tue Apr 26 18:53:23 CEST 2016
    file: /tmp/test.css, content:
    Tue Apr 26 18:53:25 CEST 2016
    file: /tmp/test.php, content:
    Tue Apr 26 18:53:27 CEST 2016
    newfile: /tmp/test-420b.php, content:
    Tue Apr 26 18:53:28 CEST 2016
    file: /tmp/test.php, content:
    Tue Apr 26 18:53:29 CEST 2016
    file: /tmp/test.php, content:
    Tue Apr 26 18:53:30 CEST 2016
    file: /tmp/test.php, content:
    Tue Apr 26 18:53:31 CEST 2016
    
  3. 一些解释:

    • waitPaths=(/{home,tmp})可以写为waitPaths=(/home /tmp),也可以仅写入一个目录:waitPaths=/var/log
    • if条件搜索与*.php*.css匹配的文件名
    • (($(stat -c %Y-%X $file)))||echo -n new将比较创建修改时间。
    • inotifywait
      • -q留下quiet(不要打印超出要求)
      • -m用于监视器模式:命令不是终结,而是打印每个匹配事件。
      • -e close_write仅对指定类型的事件做出反应。
      • -f %w%f输出格式: path/file

    另一种方式:

    有一个更复杂的样本:

    • 聆听两种事件(CLOSE_WRITE | CREATE)
    • 使用新文件列表标志,以便在发生CLOSE_WRITE事件时知道哪些文件是新文件。

    在第二个控制台中,点击 Ctrl + C ,或者在新终端中,点击这个:

    waitPaths=(/{home,tmp})
    declare -A newFiles
    while read path event file; do
        if [ "$file" ] && ( [ -z "${file##*.php}" ] || [ -z "${file##*.css}" ] ); then
            if [ "$event" ] && [ -z "${event//*CREATE*}" ]; then
                newFiles[$file]=1
            else
                if [ "${newFiles[$file]}" ]; then
                    unset newFiles[$file]
                    echo NewFile: $file, content:
                    sed 's/^/>+ /' $file
                else
                    echo file: $file, content:
                    sed 's/^/>  /' $path/$file
                fi
            fi
        fi
    done < <(inotifywait -qme close_write -e create ${waitPaths[*]})
    

    可能产生类似:

    file: test.css, content:
    >  Tue Apr 26 22:16:02 CEST 2016
    file: test.php, content:
    >  Tue Apr 26 22:16:03 CEST 2016
    NewFile: test-349b.css, content:
    >+ Tue Apr 26 22:16:05 CEST 2016
    file: test.css, content:
    >  Tue Apr 26 22:16:08 CEST 2016
    file: test.css, content:
    >  Tue Apr 26 22:16:10 CEST 2016
    file: test.css, content:
    >  Tue Apr 26 22:16:13 CEST 2016
    

答案 2 :(得分:2)

使用

查看旧文件中的新文件和新行

使用 bashisms 之类的关联数组还有另一种解决方案:

样品:

wpath=/var/log
while : ;do
    while read -a crtfile ;do
        if [ "${crtfile:0:1}" = "-" ] &&
          [ "${crtfile[8]##*.}" != "gz" ] &&
          [ "${files[${crtfile[8]}]:-0}" -lt ${crtfile[4]} ] ;then
            printf "\e[47m## %-14s :- %(%a %d %b %y %T)T ##\e[0m\n" ${crtfile[8]} -1
            tail -c +$[1+${files[${crtfile[8]}]:-0}] $wpath/${crtfile[8]}
            files[${crtfile[8]}]=${crtfile[4]}
        fi
    done < <( /bin/ls -l $wpath )
    sleep 1
done

这将在.gz中转储每个文件(文件名不以/var/log结尾),并注意修改或新文件,然后转储新行。

演示:

  1. 在第一个终端控制台中,点击:

    ext=(php css other)
    ( while :; do
        subname=''
        ((RANDOM%10)) || printf -v subname -- "-%04x" $RANDOM
        name=test$subname.${ext[RANDOM%3]}
        printf "%-16s" $name
        { 
            date +"%a %d %b %y %T" | tee /dev/fd/5
            fortune /usr/share/games/fortunes/bofh-excuses
        } >> /tmp/$name
        sleep 1
    done ) 5>&1
    

    您需要使用 BOFH借口图书馆安装fortune

    如果你真的没有fortune,你可以改用:

    LANG=C ext=(php css other)
    ( while :; do
        subname=''
        ((RANDOM%10)) || printf -v subname -- "-%04x" $RANDOM
        name=test$subname.${ext[RANDOM%3]}
        printf "%-16s" $name
        { 
            date +"%a %d %b %y %T" | tee /dev/fd/5
            for ((1; RANDOM%5; 1))
            do
                printf -v str %$[RANDOM&12]s
                str=${str// /blah, }
                echo ${str%, }.
            done
        } >> /tmp/$name
        sleep 1
    done ) 5>&1
    

    这可能会输出如下内容:

    test.css        Thu 28 Apr 16 12:00:02
    test.php        Thu 28 Apr 16 12:00:03
    test.other      Thu 28 Apr 16 12:00:04
    test.css        Thu 28 Apr 16 12:00:05
    test.css        Thu 28 Apr 16 12:00:06
    test.other      Thu 28 Apr 16 12:00:07
    test.php        Thu 28 Apr 16 12:00:08
    test.css        Thu 28 Apr 16 12:00:09
    test.other      Thu 28 Apr 16 12:00:10
    test.other      Thu 28 Apr 16 12:00:11
    test.php        Thu 28 Apr 16 12:00:12
    test.other      Thu 28 Apr 16 12:00:13
    
  2. 在第二个终端控制台中,点击:

    declare -A files
    wpath=/tmp
    while :; do
        while read -a crtfile; do
            if [ "${crtfile:0:1}" = "-" ] && [ "${crtfile[8]:0:4}" = "test" ] &&
             ( [ "${crtfile[8]##*.}" = "css" ] || [ "${crtfile[8]##*.}" = "php" ] ) &&
             [ "${files[${crtfile[8]}]:-0}" -lt ${crtfile[4]} ]; then
                printf "\e[47m## %-14s :- %(%a %d %b %y %T)T ##\e[0m\n" ${crtfile[8]} -1
                tail -c +$[1+${files[${crtfile[8]}]:-0}] $wpath/${crtfile[8]}
                files[${crtfile[8]}]=${crtfile[4]}
            fi
        done < <(/bin/ls -l $wpath)
        sleep 1
    done
    
  3. 这将每秒

    1. 用于监视目录中的所有条目

      1. 搜索文件(第一个字符为-),
      2. 搜索以test开头的文件名,
      3. 搜索以cssphp
      4. 结尾的文件名
      5. 已打印尺寸与新文件尺寸
      6. 进行比较
      7. 如果新尺寸更大,
        • 使用tail -c
        • 打印出新字节
        • 存储新的已打印尺寸
      8. 睡1秒
      9. 这可能会输出如下内容:

        ## test.css       :- Thu 28 Apr 16 12:00:09 ##
        Thu 28 Apr 16 12:00:02
        BOFH excuse #216:
        
        What office are you in? Oh, that one.  Did you know that your building was built over the universities first nuclear research site? And wow, aren't you the lucky one, your office is right over where the core is buried!
        Thu 28 Apr 16 12:00:05
        BOFH excuse #145:
        
        Flat tire on station wagon with tapes.  ("Never underestimate the bandwidth of a station wagon full of tapes hurling down the highway" Andrew S. Tannenbaum) 
        Thu 28 Apr 16 12:00:06
        BOFH excuse #301:
        
        appears to be a Slow/Narrow SCSI-0 Interface problem
        ## test.php       :- Thu 28 Apr 16 12:00:09 ##
        Thu 28 Apr 16 12:00:03
        BOFH excuse #36:
        
        dynamic software linking table corrupted
        Thu 28 Apr 16 12:00:08
        BOFH excuse #367:
        
        Webmasters kidnapped by evil cult.
        ## test.css       :- Thu 28 Apr 16 12:00:10 ##
        Thu 28 Apr 16 12:00:09
        BOFH excuse #25:
        
        Decreasing electron flux
        ## test.php       :- Thu 28 Apr 16 12:00:13 ##
        Thu 28 Apr 16 12:00:12
        BOFH excuse #3:
        
        electromagnetic radiation from satellite debris
        

        注意:如果某个文件在两次检查之间被修改了一次以上,则下次检查时将打印所有修改。

答案 3 :(得分:1)

如果你愿意,你可以把它塞进一行,但我建议你只在后台运行脚本:

#!/bin/bash

[ ! -d "$1" ] && {
    printf "error: argument is not a valid directory to monitory.\n"
    exit 1
}

while :; fname="$1/$(inotifywait -q -e modify -e create --format '%f' "$1")"; do
    cat "$fname"
done

将查看作为第一个参数给出的目录,以及cat该目录中的任何新文件或已更改文件。例如:

$ bash watchdir.sh my_logdir &

然后cat中的my_logdir新文件或已更改的文件将会出现。

答案 4 :(得分:0)

虽然不是很好,但下面给出(并重复)当前目录中最新文件的最后50行:

while true; do tail -n 50 $(ls -Art | tail -n 1); sleep 5; done

答案 5 :(得分:0)

您可以使用cronjob刷新每一分钟:

$crontabe -e


 * * * * * /home/script.sh

如果您需要在不到一分钟的时间内刷新,可以在脚本中使用“sleep”命令。