Script để theo dõi thư mục cho tập tin mới?


127

Làm cách nào để phát hiện các tệp mới trong một thư mục có tập lệnh ? Tôi muốn xử lý các tập tin ngay khi chúng được tạo trong thư mục. Điều này có thể làm như vậy hay tôi phải lên lịch cho một tập lệnh với kiểm tra các tệp mới mỗi phút hoặc lâu hơn?


1
Bạn sẽ xóa các tập tin khỏi thư mục một khi chúng được xử lý?
ztank1013

Câu trả lời:


151

Bạn nên xem xét sử dụng inotifywait, như một ví dụ:

inotifywait -m /path -e create -e moved_to |
    while read path action file; do
        echo "The file '$file' appeared in directory '$path' via '$action'"
        # do something with the file
    done

Trong Ubuntu inotifywaitđược cung cấp bởi inotify-toolsgói. Kể từ phiên bản 3.13 (hiện tại trong Ubuntu 12.04) inotifywaitsẽ bao gồm tên tệp mà không có tùy chọn -f. Các phiên bản cũ hơn có thể cần phải được ép buộc. Điều quan trọng cần lưu ý là -etùy chọn inotifywaitlà cách tốt nhất để thực hiện lọc sự kiện. Ngoài ra, readlệnh của bạn có thể gán đầu ra vị trí thành nhiều biến mà bạn có thể chọn sử dụng hoặc bỏ qua. Không cần sử dụng grep / sed / awk để tiền xử lý đầu ra.


1
Tuyệt quá! Các inotifywaitchỉ là những gì tôi muốn.
ihatetoregister

2
Chỉ muốn cập nhật điều này. Bạn không cần awk để đạt được điều này. bạn có thể lọc các sự kiện bằng '-e tạo' và chỉ lấy tên tệp bằng cách thực hiện '-f% f' hoặc đường dẫn đầy đủ bằng cách sử dụng '-f% w% f'. Vì vậy, dòng đầu tiên của tập lệnh trên trở thành: inotifywait -m / path -f% w% f -e tạo |
Lugoues

2
@Lugoues và bây giờ khi bạn cố gắng sử dụng -f bạn nhận được The '--filename' option no longer exists. The option it enabled in earlier versions of inotifywait is now turned on by default.Vì vậy, bạn chỉ phải làm inotifywait -m /path -e create |Tôi sẽ cố gắng chỉnh sửa câu trả lời này.
Bruno Bronosky

1
Bây giờ cũng có một công cụ di động cho nó được gọi là fswatch. Tôi đã không viết nó, nhưng nó là nguồn mở và tôi sử dụng nó.

1
@Wender inotfiywait xuất ra 3 mẩu thông tin trên một dòng khi được kích hoạt. Tích hợp bash 'read' đọc dòng đầu vào và gán từng trong ba phần thông tin cho một biến. Do đó, phần đầu tiên được gán cho đường dẫn biến, phần thứ hai thành hành động và phần thứ ba để tập tin. Đã gán giá trị cho các biến đó, sau đó chúng có sẵn để được sử dụng sau này (như trên dòng echo). Thêm thông tin: tldp.org/LDP/Bash-Beginners-Guide/html/sect_08_02.html
Tim

26

Tôi thích incron, vì nó dễ quản lý hơn. Về cơ bản, đây là một dịch vụ tận dụng inotifyvà bạn có thể thiết lập cấu hình để thực hiện hành động dựa trên các hoạt động thay đổi tệp.

Ví dụ:

<directory> <file change mask> <command or action>  options
/var/www/html IN_CREATE /root/scripts/backup.sh

Bạn có thể xem một ví dụ đầy đủ ở đây: http://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/


24

Tôi mới thực hiện điều này và không thấy có vấn đề gì lớn với nó, ngoài khả năng thiếu các tập tin ở giữa các lần kiểm tra.

while true
do
       touch  ./lastwatch
       sleep 10
       find /YOUR/WATCH/PATH -cnewer ./lastwatch -exec SOMECOMMAND {} \;
done

Nếu quá trình xử lý tệp của bạn không mất quá nhiều thời gian, bạn không nên bỏ lỡ bất kỳ tệp mới nào. Bạn cũng có thể làm nền cho các hoạt động ... Nó không phải là bằng chứng đạn, nhưng nó phục vụ một số mục đích mà không cần các công cụ bên ngoài như inotify.


Nắm bắt tốt. Tôi đã cải thiện nó một chút để hỗ trợ không gian trong tên tệp.
Michael Sacchi

Chắc chắn rồi. Đó là con đường để đi. Không thực sự chắc chắn tại sao tôi đi trên con đường đó, tôi sử dụng -exec thường xuyên.
Michael Sacchi

nó không phải là thời gian thực. thời gian thực luôn tốt nhất
Farhan

3
Giải pháp tốt nhất nếu inotifykhông có sẵn. Tôi sẽ chỉ thêm -type fvào để lọc ra các tập tin. Nếu không thì thư mục cũng sẽ được trả lại.
Xiao Peng - ZenUML.com

Đúng - -f filenametùy chọn là tuyệt vời. Vì vậy, câu hỏi duy nhất còn lại là làm thế nào để bắt đầu điều này khi khởi động lại. Tôi sẽ sử dụng cái này với nhà máy năng lượng mặt trời của mình để os.system("ssh me@mysystem ' ( touch /home/me/alarms/low24 ) '")sau đó việc tạo tập tin này sẽ khiến máy tính chủ sử dụng espeakvà thông báo điện áp thấp. Nó đã gửi cho tôi một email nhưng vì hệ thống của tôi đã nói thời gian vào đầu giờ nên nó có tất cả phần còn lại. Askubfox.com/questions/977613/NH
SDsolar

19

Bạn có thể sử dụng watchtrong tập lệnh của mình

watch -n 0.1 ls <your_folder>

Theo dõi thư mục của bạn và liệt kê cho bạn mọi thứ trong đó cứ sau 0,1 giây

Hạn chế

Không phải là thời gian thực, vì vậy nếu một tệp được tạo và xóa trong chưa đầy 0,1 giây, thì điều này sẽ không hoạt động, watchchỉ hỗ trợ tối thiểu 0,1 giây.


Đó chính xác là những gì tôi đã cố nhớ! Cảm ơn rất nhiều!!
Joabe Lucena

9

Tôi giả sử thư mục đích (tôi sẽ gọi nó isemptychỉ để thuận tiện) trống và bạn đang chờ một hoặc nhiều tệp được bỏ ở đó.

Bạn có thể sử dụng lệnh sau:

ls -1A isempty | wc -l

chỉ để kiểm tra xem thư mục có trống không, thực tế nó sẽ trả về 0 nếu không có tệp mới (do đó isemptythư mục vẫn trống) hoặc mặt khác, nó sẽ trả về giá trị lớn hơn 0 (thực tế là số của các tập tin hiện có trong thư mục).

Điều đó nói rằng một thử nghiệm ngớ ngẩn nếu / sau đó có thể làm cho phần còn lại của công việc:

if [ $(ls -1A isempty | wc -l) -gt 0 ] ; then do_something ; fi

Tất nhiên do_somethingchức năng sẽ phải thao tác (các) tệp trong isemptythư mục và sau đó loại bỏ nó (chúng) khỏi chính thư mục đó sau khi xử lý.

Thêm một dòng như sau trong crontab của bạn sẽ chạy kiểm tra một lần một phút và sẽ kích hoạt do_somethinghành động nếu thư mục không trống tất nhiên:

* * * * *     if [ $(ls -1A isempty | wc -l) -gt 0 ] ; then do_something ; fi

Giải pháp này hoạt động cho các hệ thống tập tin từ xa gắn kết. nhà phát triển công cụ inotify đang làm việc trên cầu chì (hoặc vào giữa năm 2014).
Rondo

3
Bạn không bao giờ nên sử dụng lscho kịch bản. findThay vào đó, hãy sử dụng hoặc đơn giản hóa toàn cầu: mywiki.wooledge.org/ParsingLs
andsens

6

Nếu bạn muốn phát hiện các tệp mới, sau đó xử lý chúng và cuối cùng xóa các tệp đã xử lý, bạn có thể sử dụng systemd.path . Phương pháp này dựa trên cơ sở inotify. Có một tùy chọn DirectoryNotEmpty, vì vậy systemd có thể chạy tập lệnh của bạn luôn khi nó phát hiện bất kỳ tệp nào trong thư mục. Bạn phải nhớ nó sẽ chỉ hoạt động nếu bạn có thể xóa các tập tin đã xử lý và tập lệnh để trống thư mục.

Trước tiên hãy chuẩn bị tệp mymonitor.service

[Unit]
Description=Start the script

[Service]
Type=oneshot
ExecStart=/path/to/your/script

tiếp theo hãy đến mymonitor.path để xác định đường dẫn

[Unit]
Description= Triggers the service

[Path]
DirectoryNotEmpty=/path/to/monitor

[Install]
WantedBy=multi-user.target

Nếu tên của tệp .path giống với tên của dịch vụ thì không cần chỉ định tên dịch vụ trong tệp .path.

Nó dựa trên quyền truy cập tệp giám sát cho người giả


4

entr

Sử dụng entrlà cách mới để làm điều này (đó là nền tảng chéo). Lưu ý entrkhông sử dụng bỏ phiếu cho nó một lợi thế rất lớn so với nhiều lựa chọn thay thế.

Sử dụng kqueue(2)hoặc inotify(7)để tránh bỏ phiếu. entrđược viết để làm cho phản hồi nhanh chóng và kiểm tra tự động tự nhiên và hoàn toàn bình thường.

Trên BSD, nó sử dụng pledge(2)

Bạn có thể cài đặt nó với

apt-get install entr
dnf install entr

Bạn có thể theo dõi một thư mục để bổ sung mới bằng cách sử dụng

while $(true); do
  # echo ./my_watch_dir | entr -dnr echo "Running trigger..."
  echo ./my_watch_dir | entr -dnr ##MY COMMAND##
done;

Tùy chọn giải thích (từ các tài liệu),

  • -d Theo dõi các thư mục của các tệp thông thường được cung cấp làm đầu vào và thoát nếu một tệp mới được thêm vào. Tùy chọn này cũng cho phép các thư mục được chỉ định rõ ràng. Các tệp có tên bắt đầu bằng '.' bị bỏ qua.
  • -nChạy trong chế độ không tương tác. Trong chế độ này, entr không cố đọc từ TTY hoặc thay đổi thuộc tính của nó.
  • -r Tải lại một quá trình con kiên trì. Như với chế độ hoạt động tiêu chuẩn, một tiện ích chấm dứt sẽ không được thực thi lại cho đến khi một sự kiện hệ thống tệp hoặc bàn phím được xử lý. SIGTERMđược sử dụng để chấm dứt tiện ích trước khi nó được khởi động lại. Một nhóm quy trình được tạo để ngăn các tập lệnh shell khỏi các tín hiệu che. entrchờ cho tiện ích thoát ra để đảm bảo rằng các tài nguyên như ổ cắm đã bị đóng. Kiểm soát TTY không được chuyển quá trình con.

2

Bash không thể làm điều này một cách dễ dàng. Về cơ bản, bạn phải có một danh sách tất cả các tệp trong thư mục và định kỳ lấy một danh sách mới và so sánh chúng để xem những gì đã thay đổi.

Những gì bạn đang tìm kiếm được gọi là inotify. Nó được tích hợp vào kernel linux và về cơ bản bạn có thể ngồi đó chờ đợi điều gì đó xảy ra tại điểm inotify quay lại và nói 'hey, có một tệp mới gọi là foobar'

Để thực hiện những gì bạn muốn, bạn phải chuyển sang một thứ như perl và sử dụng Linux :: Inotify2 (python có thể cũng hỗ trợ inotify, nhưng tôi là một người perl).


0

Điều này hoạt động trong cygwin và Linux. Một số giải pháp trước đó ghi một tệp sẽ khiến đĩa bị đập. Scipt này không có vấn đề đó:

SIG=1
SIG0=$SIG
while [ $SIG != 0 ] ; do
 while [ $SIG = $SIG0 ] ; do
   SIG=`ls -1 | md5sum | cut -c1-32`
   sleep 10
 done
 SIG0=$SIG
 ls -lrt | tail -n 1
done

0

Dưới đây là một phiên bản rút gọn của ví dụ về stackoverflow mà tôi đã thử nghiệm và kết hợp vào một trong những dự án của tôi yêu cầu giám sát các thư mục cụ thể.

Var_dir="${1:-/tmp}"
Var_diff_sleep="${2:-120}"
Var_diff_opts="--suppress-common-lines"
Func_parse_diff(){
    _added="$(grep -E '>' <<<"${@}")"
    if [ "${#_added}" != "0" ]; then
        mapfile -t _added_list <<<"${_added//> /}"
        _let _index=0
        until [ "${#_added_list[@]}" = "${_index}" ]; do
            _path_to_check="${Var_dir}/${_added_list[${_index}]}"
            if [ -f "${_path_to_check}" ]; then
                echo "# File: ${_path_to_check}"
            elif [ -d "${_path_to_check}" ]; then
                echo "# Directory: ${_path_to_check}"
            if [ -p "${_path_to_check}" ]; then
                echo "# Pipe: ${_path_to_check}"
            fi
            let _index++
        done
        unset _index
    fi
}
Func_watch_bulk_dir(){
    _current_listing=""
    while [ -d "${Var_dir}" ]; do
        _new_listing="$(ls "${Var_dir}")"
        _diff_listing="$(diff ${Var_dec_diff_opts} <(${Var_echo} "${_current_listing}") <(${Var_echo} "${_new_listing}"))"
        if [ "${_diff_listing}" != "0" ]; then
            Func_parse_diff "${_diff_listing}"
        fi
        _current_listing="${_new_listing}"
        sleep ${Var_diff_sleep}
    done
}

Đây là một liên kết đến một tập lệnh sử dụng một phiên bản sửa đổi ở trên để tự động giải mã các tập tin hoặc thư mục được tìm thấy trong điểm gắn kết sshfs của nó; các dự án đã đề cập.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.