处理目录中出现的文件

处理目录中出现的文件

可能的重复:
当目录内容更新时如何运行命令?

我正在尝试编写一个简单的 etl 进程,该进程每分钟在目录中查找文件,如果是,则将它们加载到远程系统(通过脚本),然后删除它们。

让事情变得复杂的是:加载可能需要一分多钟的时间。为了解决这个问题,我想我可以将所有文件移动到临时处理目录中,在那里对它们进行操作,然后从那里删除它们。另外,在我尝试更好地编写命令行脚本时,我正在尝试一种更优雅的解决方案。我首先编写一个简单的脚本来完成我的任务,如下所示:

#!/bin/bash

for i in ${find /home/me/input_files/ -name "*.xml"}; do
FILE=$i;
done;
BASENAME=`basename $FILE`
mv $FILE /tmp/processing/$BASENAME
myscript.sh /tmp/processing/$BASENAME other_inputs
rm /tmp/processing/$BASENAME

该脚本几乎立即从处理目录中删除文件(这会阻止重复处理问题),最后自行清理,并允许在两者之间处理文件。

然而,这毕竟是U/Linux。我觉得我应该能够通过管道和移动事物在一行中完成所有这些,而不是维护庞大的脚本。

此外,使用并行到并发进程这将是一个优点。

附录:某种 FIFO 队列也可能是这个问题的答案。或者也许是其他类型的目录观察程序而不是 cron。我愿意接受所有比我的小脚本更优雅的建议。唯一的问题是“输入目录”中的文件在实际写入之前被触及,所以某种! -size -0 仅需要处理真实文件。

答案1

听起来好像您只需编写一个小型处理脚本并使用 GNU Parallel 进行并行处理:

http://www.gnu.org/software/parallel/man.html#example__gnu_parallel_as_dir_processor

所以像这样:

inotifywait -q -m -r -e CLOSE_WRITE --format %w%f my_dir |
  parallel 'mv {} /tmp/processing/{/};myscript.sh /tmp/processing/{/} other_inputs; rm /tmp/processing/{/}'

观看介绍视频以了解更多信息:http://pi.dk/1

编辑:

要求 myscript.sh 可以处理 0 长度的文件(例如忽略它们)。

如果你可以避免,touch你甚至可以这样做:

inotifywait -q -m -r -e CLOSE_WRITE --format %w%f my_dir |
  parallel myscript.sh {} other_inputs

安装 GNU Parallel 非常简单:

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel

答案2

首先,您的脚本将对一个文件(列表中的最后一个)进行操作。另外,我认为单衬并不总是合适或优雅的。 Cron 在幕后做了很多事情,您需要能够检查失败的事情。 “频繁”运行 cron 可能是一个问题。您最终可能会运行数十个这样的进程,从而减慢系统速度,因为它们都在尝试处理队列中的文件。

这就是我要做的。

Dir="$HOME/input_files"   # never hardcode when you have variables
for filename in "$Dir"/*.xml; do
    # is the file non-empty AND is it still there, or may caught by another
    # process
    if [ -s "$filename" ]; then
        # move files locally will be faster than crossing filesystems to /tmp
        mkdir -p "$Dir/.processing"
        # temp name should use pid, just in case another input with the same name comes in
        tempname="$Dir/.processing/`basename $filename .xml`.$$"
        mv "$filename" "$tempname"
        # send stdout and stderr to a .output file
        myscript.sh "$tempname" other_inputs > "$tempname.output" 2>&1
        rc=$?
        if [ $rc -eq 0 ]; then
            rm "$tempname" "$tempname.output"
        else
            echo "Error processing $filename; rc=$rc" >&2
            echo "File in $tempname" >&2
        fi
    done

这将在处理后删除文件,或者在出错时将文件保留在.processing包含命令输出的目录中。上面的命令不会限制任何东西,但它确实允许多个命令运行而不会互相干扰。关于如何创建相当高效的工作队列来增强还有其他问题。

答案3

使用inotify(7)接口来监视传入目录而不是通过 cron 轮询。 inotify-tools 为您提供了 inotifywait 程序,如果您不想针对系统调用接口编写代码,您可以使用该程序来监视目录。

相关内容