Câu hỏi Làm thế nào để thực hiện một lệnh bất cứ khi nào một tập tin thay đổi?


Tôi muốn một cách nhanh chóng và đơn giản để thực hiện một lệnh bất cứ khi nào một tập tin thay đổi. Tôi muốn một cái gì đó rất đơn giản, một cái gì đó tôi sẽ để lại chạy trên một thiết bị đầu cuối và đóng nó bất cứ khi nào tôi đã hoàn thành làm việc với tập tin đó.

Hiện tại, tôi đang sử dụng điều này:

while read; do ./myfile.py ; done

Và sau đó tôi cần phải đi đến thiết bị đầu cuối đó và nhấn Đi vào, bất cứ khi nào tôi lưu tệp đó vào trình chỉnh sửa của mình. Những gì tôi muốn là một cái gì đó như thế này:

while sleep_until_file_has_changed myfile.py ; do ./myfile.py ; done

Hoặc bất kỳ giải pháp nào khác dễ dàng như vậy.

BTW: Tôi đang sử dụng Vim, và tôi biết tôi có thể thêm một autocommand để chạy một cái gì đó trên BufWrite, nhưng đây không phải là loại giải pháp tôi muốn bây giờ.

Cập nhật: Tôi muốn một cái gì đó đơn giản, có thể loại bỏ nếu có thể. Hơn nữa, tôi muốn một cái gì đó để chạy trong một thiết bị đầu cuối bởi vì tôi muốn xem đầu ra chương trình (Tôi muốn xem thông báo lỗi).

Về câu trả lời: Cảm ơn tất cả các câu trả lời của bạn! Tất cả đều rất tốt, và mỗi người có một cách tiếp cận rất khác với những người khác. Vì tôi chỉ cần chấp nhận một cái, tôi chấp nhận cái mà tôi đã thực sự sử dụng (nó đơn giản, nhanh chóng và dễ nhớ), mặc dù tôi biết nó không phải là thanh lịch nhất.


357
2017-08-27 20:02


gốc


Có thể trùng lặp trang web trùng lặp của: stackoverflow.com/questions/2972765/… (mặc dù ở đây là chủ đề =)) - Ciro Santilli 新疆改造中心 六四事件 法轮功
Tôi đã tham chiếu trước một trang web trùng lặp và nó đã bị từ chối: S;) - Francisco Tapia
Các giải pháp của Jonathan Hartley xây dựng trên các giải pháp khác ở đây và sửa chữa các vấn đề lớn mà các câu trả lời hàng đầu bình chọn có: thiếu một số sửa đổi và không hiệu quả. Vui lòng thay đổi câu trả lời được chấp nhận cho anh ấy, điều này cũng đang được duy trì trên github tại github.com/tartley/rerun2 (hoặc một số giải pháp khác mà không có những sai sót) - nealmcb


Các câu trả lời:


Đơn giản, sử dụng inotifywait (cài đặt phân phối của bạn inotify-tools gói):

while inotifywait -e close_write myfile.py; do ./myfile.py; done

hoặc là

inotifywait -q -m -e close_write myfile.py |
while read -r filename event; do
  ./myfile.py         # or "./$filename"
done

Đoạn mã đầu tiên đơn giản hơn, nhưng nó có một nhược điểm quan trọng: nó sẽ bỏ lỡ những thay đổi được thực hiện trong khi inotifywait không chạy (đặc biệt trong khi myfile đang chạy). Đoạn thứ hai không có lỗi này. Tuy nhiên, hãy cẩn thận rằng nó giả định rằng tên tệp không chứa khoảng trắng. Nếu đó là một vấn đề, hãy sử dụng --format tùy chọn thay đổi đầu ra thành không bao gồm tên tệp:

inotifywait -q -m -e close_write --format %e myfile.py |
while read events; do
  ./myfile.py
done

Dù bằng cách nào, có một giới hạn: nếu một số chương trình thay thế myfile.py với một tệp khác, thay vì ghi vào tệp hiện có myfile, inotifywait sẽ chết. Nhiều biên tập viên làm việc theo cách đó.

Để khắc phục giới hạn này, sử dụng inotifywait trên thư mục:

inotifywait -e close_write,moved_to,create -m . |
while read -r directory events filename; do
  if [ "$filename" = "myfile.py" ]; then
    ./myfile.py
  fi
done

Ngoài ra, hãy sử dụng một công cụ khác sử dụng cùng chức năng cơ bản, chẳng hạn như incron (cho phép bạn đăng ký sự kiện khi tệp được sửa đổi) hoặc fswatch (một công cụ cũng hoạt động trên nhiều biến thể Unix khác, bằng cách sử dụng tương tự của từng biến thể của inotify của Linux).


341
2017-08-27 20:54



Tôi đã gói gọn tất cả những điều này (với một vài thủ thuật bash) trong một đơn giản để sử dụng sleep_until_modified.sh tập lệnh, có sẵn tại: bitbucket.org/denilsonsa/small_scripts/src - Denilson Sá Maia
while sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; done thật tuyệt vời. Cảm ơn bạn. - Rhys Ulerich
inotifywait -e delete_self dường như làm việc tốt cho tôi. - Kos
Nó đơn giản nhưng có hai vấn đề quan trọng: Sự kiện có thể bị bỏ qua (tất cả các sự kiện trong vòng lặp) và khởi tạo inotifywait được thực hiện mỗi lần làm cho giải pháp này chậm hơn đối với các thư mục đệ quy lớn. - Wernight
Đối với một số lý do while inotifywait -e close_write myfile.py; do ./myfile.py; done luôn luôn thoát mà không cần chạy lệnh (bash và zsh). Để làm việc này, tôi cần thêm || true, ví dụ: while inotifywait -e close_write myfile.py || true; do ./myfile.py; done - ideasman42


entr (http://entrproject.org/) cung cấp một giao diện thân thiện hơn để inotify (và cũng hỗ trợ * BSD & Mac OS X).

Nó làm cho nó rất dễ dàng để xác định nhiều tập tin để xem (chỉ giới hạn bởi ulimit -n), sẽ gặp rắc rối khi xử lý các tệp đang được thay thế và yêu cầu cú pháp bash ít hơn:

$ find . -name '*.py' | entr ./myfile.py

Tôi đã sử dụng nó trên toàn bộ cây nguồn dự án của mình để chạy các bài kiểm tra đơn vị cho mã mà tôi hiện đang sửa đổi, và nó đã là một sự thúc đẩy rất lớn cho luồng công việc của tôi rồi.

Cờ như -c (xóa màn hình giữa các lần chạy) và -d (thoát ra khi một tập tin mới được thêm vào một thư mục được giám sát) thêm tính linh hoạt hơn, ví dụ bạn có thể làm:

$ while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; done

Tính đến đầu năm 2018 nó vẫn còn trong sự phát triển tích cực và nó có thể được tìm thấy trong Debian & Ubuntu (apt install entr); xây dựng từ repo của tác giả là không đau đớn trong mọi trường hợp.


120
2017-10-25 09:41



Không xử lý các tệp mới và các sửa đổi của chúng. - Wernight
@Wernight - tính đến ngày 7 tháng 5 năm 2014, entr mới có -d cờ; nó hơi dài hơn, nhưng bạn có thể làm while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; done để xử lý các tệp mới. - Paul Fenney
có sẵn trong aur aur.archlinux.org/packages/entr - Victor Häggqvist
tốt nhất tôi tìm thấy trên OS X cho chắc chắn. fswatch grabs quá nhiều sự kiện funky và tôi không muốn dành thời gian để tìm ra lý do tại sao - dtc
Điều đáng chú ý là entr có sẵn trên Homebrew, vì vậy brew install entr sẽ hoạt động như mong đợi - jmarceli


Tôi đã viết một chương trình Python để thực hiện chính xác cái gọi là khi thay đổi.

Cách sử dụng rất đơn giản:

when-changed FILE COMMAND...

Hoặc để xem nhiều tệp:

when-changed FILE [FILE ...] -c COMMAND

FILE có thể là một thư mục. Xem đệ quy với -r. Sử dụng %f để chuyển tên tệp vào lệnh.


100
2018-06-30 13:34



@ yangkok có nó, trong phiên bản mới nhất của mã :) - joh
Bây giờ có sẵn từ "pip install when-changed". Vẫn hoạt động tốt. Cảm ơn. - A. L. Flanagan
Để xóa màn hình trước tiên, bạn có thể sử dụng when-changed FILE 'clear; COMMAND'. - Dave James Miller
Câu trả lời này tốt hơn rất nhiều vì tôi cũng có thể làm điều đó trên Windows. Và anh chàng này thực sự đã viết một chương trình để có được câu trả lời. - Wolfpack'08
Tin tốt tất cả mọi người! when-changed bây giờ là nền tảng chéo! Xem thông tin mới nhất Bản phát hành 0.3.0 :) - joh


Làm thế nào về kịch bản này? Nó sử dụng stat lệnh để có được thời gian truy cập của một tập tin và chạy một lệnh bất cứ khi nào có sự thay đổi trong thời gian truy cập (bất cứ khi nào tập tin được truy cập).

#!/bin/bash

### Set initial time of file
LTIME=`stat -c %Z /path/to/the/file.txt`

while true    
do
   ATIME=`stat -c %Z /path/to/the/file.txt`

   if [[ "$ATIME" != "$LTIME" ]]
   then    
       echo "RUN COMMAND"
       LTIME=$ATIME
   fi
   sleep 5
done

46
2017-08-20 17:12



Sẽ không stat-Thời gian sửa đổi có tốt hơn "bất cứ khi nào một tập tin thay đổi" câu trả lời? - Xen2050
Sẽ chạy stat nhiều lần mỗi giây gây ra nhiều lần đọc đĩa? hoặc hệ thống fstat sẽ tự động gọi bộ nhớ cache những phản hồi này bằng cách nào đó? Tôi đang cố gắng viết một loại 'grunt watch' để biên dịch mã c của tôi bất cứ khi nào tôi thực hiện thay đổi - Oskenso Kashi
Điều này là tốt nếu bạn biết tên tập tin được theo dõi trước. Tốt hơn là chuyển tên tệp cho tập lệnh. Vẫn tốt hơn nếu bạn có thể truyền nhiều tên tập tin (ví dụ: "mywatch * .py"). Vẫn còn tốt hơn nếu nó có thể hoạt động đệ quy trên các tệp trong các thư mục con, mà một số giải pháp khác thực hiện. - Jonathan Hartley
Chỉ trong trường hợp bất cứ ai đang tự hỏi về đọc nặng, tôi đã thử nghiệm kịch bản này trong Ubuntu 17.04 với một giấc ngủ 0,05 và vmstat -d để xem để truy cập đĩa. Có vẻ như Linux làm một công việc tuyệt vời trong bộ nhớ đệm loại điều này: D - Oskenso Kashi
Có lỗi đánh máy trong "COMMAND", tôi đã cố sửa, nhưng S.O. nói "Chỉnh sửa không được nhỏ hơn 6 ký tự" - user337085


Giải pháp sử dụng Vim:

:au BufWritePost myfile.py :silent !./myfile.py

Nhưng tôi không muốn giải pháp này bởi vì nó khá phiền toái khi gõ, hơi khó để nhớ những gì cần gõ, chính xác, và hơi khó để hoàn tác các hiệu ứng của nó (cần phải chạy :au! BufWritePost myfile.py). Ngoài ra, giải pháp này chặn Vim cho đến khi lệnh đã thực hiện xong.

Tôi đã thêm giải pháp này ở đây chỉ để hoàn thành, vì nó có thể giúp người khác.

Để hiển thị đầu ra chương trình (và hoàn toàn làm gián đoạn luồng chỉnh sửa của bạn, vì đầu ra sẽ ghi đè lên trình soạn thảo của bạn trong vài giây, cho đến khi bạn nhấn Enter), hãy xóa :silent chỉ huy.


28
2017-08-27 20:12



Điều này có thể khá tốt đẹp khi kết hợp với entr (xem bên dưới) - chỉ cần làm vim chạm vào một tập tin giả mà entr đang xem, và để cho entr làm phần còn lại trong nền ... hoặc tmux send-keys nếu bạn tình cờ ở trong môi trường như vậy :) - Paul Fenney
tốt đẹp! bạn có thể tạo macro cho .vimrc tập tin - ErichBSchulz


Nếu bạn tình cờ có npm Cài đặt, nodemon có lẽ là cách dễ nhất để bắt đầu, đặc biệt là trên OS X, mà dường như không có công cụ inotify. Nó hỗ trợ chạy một lệnh khi một thư mục thay đổi.


24
2018-06-09 23:51



Tuy nhiên, nó chỉ xem các tệp .js và .coffee. - zelk
Phiên bản hiện tại dường như hỗ trợ bất kỳ lệnh nào, ví dụ: nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/models - kek
Tôi muốn tôi có thêm thông tin, nhưng osx không có một phương pháp để theo dõi thay đổi, fsevents - ConstantineK
Trên OS X, bạn cũng có thể sử dụng Khởi chạy Daemon với một WatchPaths như được hiển thị trong liên kết của tôi. - Adam Johns


Đây là một kịch bản shell shell Bourne đơn giản mà:

  1. Lấy hai đối số: tệp sẽ được theo dõi và một lệnh (với các đối số, nếu cần)
  2. Sao chép tệp bạn đang theo dõi vào thư mục / tmp
  3. Kiểm tra mỗi hai giây để xem tệp bạn đang theo dõi có mới hơn bản sao không
  4. Nếu nó mới hơn, nó sẽ ghi đè bản sao bằng bản gốc mới hơn và thực hiện lệnh
  5. Dọn dẹp sau khi bạn nhấn Ctr-C

    #!/bin/sh  
    f=$1  
    shift  
    cmd=$*  
    tmpf="`mktemp /tmp/onchange.XXXXX`"  
    cp "$f" "$tmpf"  
    trap "rm $tmpf; exit 1" 2  
    while : ; do  
        if [ "$f" -nt "$tmpf" ]; then  
            cp "$f" "$tmpf"  
            $cmd  
        fi  
        sleep 2  
    done  
    

Điều này hoạt động trên FreeBSD. Vấn đề về tính di động duy nhất mà tôi có thể nghĩ đến là nếu một số Unix khác không có lệnh mktemp (1), nhưng trong trường hợp đó, bạn có thể chỉ cần mã hóa tên tệp tạm thời.


12
2017-08-27 21:23



Bỏ phiếu là cách di động duy nhất, nhưng hầu hết các hệ thống đều có cơ chế thông báo thay đổi tập tin (inotify trên Linux, kqueue trên FreeBSD, ...). Bạn có vấn đề trích dẫn nghiêm trọng khi bạn làm $cmd, nhưng may mắn thay điều đó dễ dàng khắc phục được: cmd biến và thực thi "$@". Tập lệnh của bạn không phù hợp để theo dõi một tệp lớn, nhưng có thể được sửa bằng cách thay thế cp bởi touch -r (bạn chỉ cần ngày, không phải nội dung). Portability-khôn ngoan, các -nt kiểm tra yêu cầu bash, ksh hoặc zsh. - Gilles