使用Unix ksh shell脚本或Perl脚本监视文件夹中的新文件并触发Perl脚本。

9

我已经在谷歌和Stack Overflow上搜索了一段时间,但没有找到任何有用的东西。

我需要一个脚本来监视公共文件夹,并在创建新文件时触发,然后将文件移动到私人位置。

我在Unix上有一个Samba共享文件夹/exam/ple/,映射到Windows上的X:\。在某些操作中,txt文件被写入共享。我想绑架出现在文件夹中的任何txt文件,并将其放到Unix上的私人文件夹/pri/vate中。移动该文件后,我想触发一个单独的perl脚本。

编辑 如果有人有任何想法,请继续等待shell脚本... 想要监视新文件并运行类似下面的内容:

#!/bin/ksh
mv -f /exam/ple/*.txt /pri/vate

1
你需要以编程的方式完成它,还是可以利用现有的设施?这就是cron的用途。 - quack quixote
Cron可以通过新文件触发吗? - CheeseConQueso
我也不希望cron一遍又一遍地运行第二个脚本... 我只想在成功传输新文件到私有文件夹后再运行第二个脚本。 - CheeseConQueso
8个回答

9

查看incron,它似乎正好满足你的需求。


这看起来相当不错...太糟糕了,我无法安装它 :< - CheeseConQueso
一个非常好的“Hello World”类型的incron示例:http://www.errr-online.com/2011/02/25/monitor-a-directory-or-file-for-changes-on-linux-using-inotify/ - mdaddy
incron可以在Windows上安装吗? - Helen Craigman

6
如果我理解得正确,您只是想要像这样的东西吗?
#!/usr/bin/perl

use strict;
use warnings;

use File::Copy

my $poll_cycle = 5;
my $dest_dir = "/pri/vate";

while (1) {
    sleep $poll_cycle;

    my $dirname = '/exam/ple';

    opendir my $dh, $dirname 
        or die "Can't open directory '$dirname' for reading: $!";

    my @files = readdir $dh;
    closedir $dh;

    if ( grep( !/^[.][.]?$/, @files ) > 0 ) {
        print "Dir is not empty\n";

        foreach my $target (@files) {
            # Move file
            move("$dirname/$target", "$dest_dir/$target");

            # Trigger external Perl script
            system('./my_script.pl');
    }
}

我会测试一下... 这个会无限运行吧? 另外,我只需要文本文件,但grep炸弹也很酷。 - CheeseConQueso
1
@CheeseConQueso:是的,这是一个无限循环,以您指定的频率进行轮询。我没有严格测试过代码,但这个想法足够简单。 - ire_and_curses
1
@CheeseConQueso:如果你想要时间特定的功能,那么最简单的答案可能是删除while循环和sleep,并通过cron设置脚本运行。你可以指定运行间隔,并限制时间。正如quack所说,这就是cron的设计目的。 - ire_and_curses
我可能会使用这个 - @files = glob "/exam/ple/*.txt"; - 来构建数组。 - CheeseConQueso
@ire - 是的...忘记了这个。 - CheeseConQueso
显示剩余4条评论

5

3

我知道我来晚了,但出于完整性和向未来的访问者提供信息的目的;

在IT技术方面,如果您想涵盖所有范围,那么将需要考虑一些具体问题。
#!/bin/ksh
# Check a File path for any new files
# And execute another script if any are found

POLLPATH="/path/to/files"
FILENAME="*.txt" # Or can be a proper filename without wildcards
ACTION="executeScript.sh argument1 argument2"
LOCKFILE=`basename $0`.lock

# Make sure we're not running multiple instances of this script
if [ -e /tmp/$LOCKFILE ] ; then 
     exit 0
else
     touch /tmp/$LOCKFILE
fi

# check the dir for the presence of our file
# if it's there, do something, if not exit

if [ -e $POLLPATH/$FILENAME ] ; then
     exec $ACTION
else
     rm /tmp/$LOCKFILE
     exit 0
fi

从cron上运行它;

*/1 7-22/1 * * * /path/to/poll-script.sh >/dev/null 2>&1

在您的后续脚本($ACTION)中,您需要使用lockfile,然后在退出时清理它,以避免任何堆叠进程。


2
$ python autocmd.py /exam/ple .txt,.html /pri/vate some_script.pl

优点:

  • 安装比incron更容易,因为pyinotify是纯Python
  • 事件驱动 -- 比perl脚本的影响小

autocmd.py:

#!/usr/bin/env python
"""autocmd.py 

Adopted from autocompile.py [1] example.

[1] http://git.dbzteam.org/pyinotify/tree/examples/autocompile.py

Dependencies:

Linux, Python, pyinotify
"""
import os, shutil, subprocess, sys

import pyinotify
from pyinotify import log

class Handler(pyinotify.ProcessEvent):
    def my_init(self, **kwargs):
        self.__dict__.update(kwargs)

    def process_IN_CLOSE_WRITE(self, event):
        # file was closed, ready to move it
        if event.dir or os.path.splitext(event.name)[1] not in self.extensions:
           # directory or file with uninteresting extension
           return # do nothing

        try:
            log.debug('==> moving %s' % event.name)
            shutil.move(event.pathname, os.path.join(self.destdir, event.name))
            cmd = self.cmd + [event.name]
            log.debug("==> calling %s in %s" % (cmd, self.destdir))
            subprocess.call(cmd, cwd=self.destdir)
        except (IOError, OSError, shutil.Error), e:
            log.error(e)

    def process_default(self, event):
        pass


def mainloop(path, handler):
    wm = pyinotify.WatchManager()
    notifier = pyinotify.Notifier(wm, default_proc_fun=handler)
    wm.add_watch(path, pyinotify.ALL_EVENTS, rec=True, auto_add=True)
    log.debug('==> Start monitoring %s (type c^c to exit)' % path)
    notifier.loop()


if __name__ == '__main__':
    if len(sys.argv) < 5:
       print >> sys.stderr, "USAGE: %s dir ext[,ext].. destdir cmd [args].." % (
           os.path.basename(sys.argv[0]),)
       sys.exit(2)

    path = sys.argv[1] # dir to monitor
    extensions = set(sys.argv[2].split(','))
    destdir = sys.argv[3]
    cmd = sys.argv[4:]

    log.setLevel(10) # verbose

    # Blocks monitoring
    mainloop(path, Handler(path=path, destdir=destdir, cmd=cmd,
                           extensions=extensions))

这看起来很有趣...虽然我没有Python,但从你关于通知是本地的说法中,我可能需要安装它并尝试一下...谢谢。 - CheeseConQueso
CheeseConQueso: 如果http://search.cpan.org/~drolsky/File-ChangeNotify-0.07/lib/File/ChangeNotify/Watcher/Inotify.pm子类可用,则可以通过@jsoversion提到的File::ChangeNotify执行与pyinotify相同的操作。快速的CPAN搜索揭示了另一个可能的解决方案http://search.cpan.org/~mlehmann/Linux-Inotify2-1.21/Inotify2.pm。 - jfs

1

我不使用ksh,但是这是我用sh的方法。我相信它很容易适应ksh。

#!/bin/sh
trap 'rm .newer' 0
touch .newer
while true; do
  (($(find /exam/ple -maxdepth 1 -newer .newer -type f -name '*.txt' -print \
      -exec mv {} /pri/vate \; | wc -l))) && found-some.pl &
  touch .newer
  sleep 10
done

1

这将导致大量的io - stat()调用等。如果您想要快速通知而不带来运行时开销(但需要更多的前期工作),请查看FAM/dnotify:链接文本链接文本


0
#!/bin/ksh
while true
do
    for file in `ls /exam/ple/*.txt`
    do
          # mv -f /exam/ple/*.txt /pri/vate
          # changed to
          mv -f  $file  /pri/vate

    done
    sleep 30
done

这是我在网上找到的一种在Korn Shell中每30秒进行搜索的方法...它不是由新文件触发的,而更像是cron类型的进程...我仍然找不到一个基于新文件存在运行的Korn Shell脚本。 - CheeseConQueso
@Cheese,这个例子有点笨重 - 如果在单次迭代中在/exam/ple中有两个文件,则for循环体将运行两次,但两个文件第一次移动。因此,在mv的第二次调用中会看到错误。那些反引号是必需的吗? - martin clayton
@Martin - 很好的观点... 我是在网上找到的,没有测试过,所以我不确定是否需要反引号。我只是把它放在这里,因为它是一种shell方法。此外,cron也可以执行同样的操作,这种方法有些笨重。 - CheeseConQueso

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