Bash脚本监视文件夹

4
我有以下情况:
在Linux机器上挂载了一个Windows文件夹。这个Windows挂载中可能有多个文件夹(事先设置好)。我需要做一些事情(最好是从脚本开始)来监视这些文件夹。
以下是步骤: 观察任何传入的文件。确保它们被完全传输。 将其移动到另一个文件夹。 我无法控制Windows机器上的文件传输程序。我相信这是一个安全的FTP。因此,我不能要求该进程向我发送尾随文件以确保文件传输完成。
我编写了一个bash脚本。我想知道这种方法可能存在的潜在问题。原因是,可能会有多个副本运行于像这样的多个目录中。
目前,最多可能有100个目录需要进行监视。
以下是脚本。很抱歉在此粘贴了一个非常长的脚本。请花时间审查并发表评论/批评。 :-)
它需要3个参数:要监视的文件夹、要移动文件的文件夹和一个时间间隔,下面已经解释过了。
很抱歉似乎对齐存在问题。Markdown似乎不喜欢它。我试图正确组织它,但无法做到。 Linux servername 2.6.9-42.ELsmp #1 SMP Wed Jul 12 23:27:17 EDT 2006 i686 i686 i386 GNU/Linux
#!/bin/bash
log_this()
{
    message="$1"
    now=`date "+%D-%T"`
    echo $$": "$now ": " $message
}
usage()
{
    cat << EOF
Usage: $0 <Directory to be watched> <Directory to transfer> <time interval>
Time interval is the amount of time after which the modification time of a
file will be monitored. 
EOF
    `exit 1`
}

if [ $# -lt 2 ]
then
    usage
fi

WATCH_DIR=$1
APP_DIR=$2

if [ ! -d "$WATCH_DIR" ]
then
    log_this "FATAL: WATCH_DIR, $WATCH_DIR does not exist. Exiting"
    exit 1
fi

if [ ! -d "$APP_DIR" ]
then
    log_this "APP_DIR: $APP_DIR does not exist. Exiting"
    exit 1
fi


# This needs to be set after considering the rate of file transfer.
# Represents the seconds elapsed after the last modification to the file.
# If not supplied as parameter, defaults to 3.

seconds_between_mods=$3

if ! [[ "$seconds_between_mods" =~ ^[0-9]+$ ]]; then
        if [ ${#seconds_between_mods} -eq 0 ]; then
                log_this "No value supplied for elapse time. Defaulting to 3."
                seconds_between_mods=3
        else
                log_this "Invalid value provided for elapse time"
                exit 1
        fi
fi

log_this "Start Monitor."

while true
do
        ls -1 $WATCH_DIR | while read file_name
        do
            log_this "Start Monitoring for $file_name"

            # Refer only the modification with reference to the mount folder.
            # If there is a diff in time between servers, we are in trouble.

            token_file=$WATCH_DIR/foo.$$
            current_time=`touch $token_file && stat -c "%Y" $token_file`
            rm -f $token_file 2>/dev/null

            log_this "Current Time: $current_time"
            last_mod_time=`stat -c "%Y" $WATCH_DIR/$file_name`

            elapsed_time=`expr $current_time - $last_mod_time`
            log_this "Elapsed time ==> $elapsed_time"

            if [ $elapsed_time -ge $seconds_between_mods ]
            then
                    log_this "Moving $file_name to $APP_DIR"

                    # In case if there is no space left on the target mount, hide the     file
                    # in the mount itself and remove the incomplete file from APP_DIR.
                    mv $WATCH_DIR/$file_name $APP_DIR
                    if [ $? -ne 0 ]
                    then
                            log_this "FATAL: mv failed!! Hiding $file_name"
                            rm $APP_DIR/$file_name
                            mv $WATCH_DIR/$file_name $WATCH_DIR/.$file_name
                            log_this "Removed $APP_DIR/$file_name. Look for $WATCH_DIR/.$file_name and submit later."
                    fi

                    log_this "End Monitoring for $file_name"
            else
                    log_this "$file_name: Transfer seems to be in progress"
            fi
    done
    log_this "Nothing more to monitor."
    echo
    sleep 5
done

可能是监视目录更改的重复问题。 - Jonathan Leffler
6个回答

5

这种方法长期使用不可行。在生产环境中,你将会遇到网络问题和其他错误,可能会在上传目录中留下部分文件。我也不喜欢“尾随”文件的想法。通常的做法是将文件上传到临时名称下,然后在上传完成后重命名它。

这样,你只需要列出目录,过滤掉临时名称,如果有剩余的,就使用它。

如果你无法进行这个更改,请向你的老板申请书面许可,以实现可能导致任意数据损坏的功能。这是为了两个目的:1)让他们明白这是一个真正的问题,而不是你杜撰的;2)当它出了问题时保护自己……因为它肯定会出问题,你猜会怪罪谁?


4

我认为更明智的方法是使用内核级别的文件系统通知项,例如inotify。你可以在这里获取工具


inotify似乎是正确的方法。但是此时,我没有权限打补丁来安装它。 - prabhu

2

首先确保已安装inotify-tools

然后按照以下方式使用:

logOfChanges="/tmp/changes.log.csv" # Set your file name here.

# Lock and load
inotifywait -mrcq $DIR > "$logOfChanges" & # monitor, recursively, output CSV, be quiet.
IN_PID=$$

# Do your stuff here
...

# Kill and analyze
kill $IN_PID
cat "$logOfChanges" | while read entry; do
   # Split your CSV, but beware that file names may contain spaces too.
   # Just look up how to parse CSV with bash. :)
   path=... 
   event=...
   ...  # Other stuff like time stamps
   # Depending on the event…
   case "$event" in
     SOME_EVENT) myHandlingCode path ;;
     ...
     *) myDefaultHandlingCode path ;;
done

另外,使用inotifywait--format选项而非-c选项可能是一个好主意。

更多信息请查看inotifywaitinotifywatch的手册。


2

incron 是一个“inotify cron”系统。它由守护程序和表格操作器组成。您可以像使用常规cron一样使用它。不同之处在于,inotify cron处理文件系统事件而不是时间段。


1
说实话,一个设置在启动时运行的Python应用程序可以快速高效地完成这项任务。Python拥有惊人的操作系统支持和相当完整的功能。
运行脚本可能会起作用,但是维护和管理可能会很麻烦。我猜你会将其作为频繁的cron作业来运行?

嗯,我不懂Python。你能告诉我一个可以帮助完成这个任务的Python包或模块吗?谢谢。 - prabhu
  1. 请跟随www.python.org上的Python教程。
  2. 查看os.path模块。它有一个walk()方法,可以用来检查目录树。
- Aaron Digulla
Pynotify是一个Python库,用于在目录发生更改时接收通知:http://pyinotify.sourceforge.net - Joaquin Sargiotto

0
为了让你入门,这是一个我写的小应用程序,它接受一个路径并查看JPEG文件的二进制输出。我从未完全完成它,但它会让你开始并了解Python的结构以及一些使用os的方法。
我不会花太多时间担心我的代码。
import time, os, sys

#analyze() takes in a path and moves into the output_files folder, to then analyze files

def analyze(path):
    list_outputfiles = os.listdir(path + "/output_files")
    print list_outputfiles
    for i in range(len(list_outputfiles)):
        #print list_outputfiles[i]
        f = open(list_outputfiles[i], 'r')
        f.readlines()

#txtmaker reads the media file and writes its binary contents to a text file.

def txtmaker(c_file): 
    print c_file
    os.system("cat" + " " + c_file + ">" + " " + c_file +".txt")
    os.system("mv *.txt output_files")

#parser() takes in the inputed path, reads and lists all files, creates a directory, then calls txtmaker.

def parser(path):
    os.chdir(path)
    os.mkdir(path + "/output_files", 0777)
    list_files = os.listdir(path)
    for i in range(len(list_files)):
        if os.path.isdir(list_files[i]) == True:
            print (list_files[i], "is a directory")
        else:
            txtmaker(list_files[i])
    analyze(path)

def main():
    path = raw_input("Enter the full path to the media: ")
    parser(path)


if __name__ == '__main__':

    main()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接