Làm thế nào để thực thi một lệnh bất cứ khi nào một tập tin thay đổi?


434

Tôi muốn một cách nhanh chóng và đơn giản để thực thi lệnh mỗi khi tệp thay đổi. Tôi muốn một cái gì đó rất đơn giản, một cái gì đó tôi sẽ bỏ 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 công việc với tập tin đó.

Hiện tại, tôi đang sử dụng cái 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 Enter, bất cứ khi nào tôi lưu tệp đó vào trình soạn thảo 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 khác dễ dàng như vậy.

BTW: Tôi đang sử dụng Vim và tôi biết rằng tôi có thể thêm một trình tự động tự động để chạy một cái gì đó trên BufWrite, nhưng đây không phải là 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ả chúng đề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, 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 thanh lịch nhất.


Trang web chéo có thể 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 khi một trang web trùng lặp trùng lặp và nó đã bị từ chối: S;)
Francisco Tapia

4
Giải pháp của Jonathan Hartley xây dựng trên các giải pháp khác ở đây và khắc phục các vấn đề lớn mà các câu trả lời được bình chọn hàng đầu 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 câu trả lời được chấp nhận trên github tại github.com/tartley/rerun2 (hoặc một số giải pháp khác không có sai sót đó)
nealmcb

Câu trả lời:


404

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

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 đáng kể: nó sẽ bỏ lỡ các thay đổi được thực hiện trong khi inotifywaitkhông chạy (đặc biệt là trong khi myfileđang chạy). Đoạn mã thứ hai không có khuyết điểm này. Tuy nhiên, hãy cẩn thận rằng nó giả sử rằng tên tệp không chứa khoảng trắng. Nếu đó là một vấn đề, sử dụng --formattùy chọn để thay đổi đầu ra để 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 hạn chế: nếu một số chương trình thay thế myfile.pybằng một tệp khác, thay vì ghi vào tệp hiện có myfile, inotifywaitsẽ chết. Nhiều biên tập viên làm việc theo cách đó.

Để khắc phục hạn chế này, sử dụng inotifywaittrê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, 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ý các 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, sử dụng từng biến thể tương tự của Linux).


46
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 sleep_until_modified.shtập lệnh dễ sử dụng , có sẵn tại: bitbucket.org/denilsonsa/small_scripts/src
Denilson Sá Maia

14
while sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; donethật tuyệt vời Cảm ơn bạn.
Rhys Ulerich

5
inotifywait -e delete_selfdường như làm việc tốt cho tôi
Kos

3
Điều này đơn giản nhưng có hai vấn đề quan trọng: Có thể bỏ qua các sự kiện (tất cả các sự kiện trong vòng lặp) và việc khởi tạo inotifywait được thực hiện mỗi lần khiến giải pháp này chậm hơn đối với các thư mục đệ quy lớn.
Wernight

6
Vì một số lý do, while inotifywait -e close_write myfile.py; do ./myfile.py; doneluôn luôn thoát mà không 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
ideaman42

166

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

Nó giúp bạn dễ dàng chỉ định nhiều tệp để xem (chỉ giới hạn bởi ulimit -n), loại bỏ rắc rối khi xử lý các tệp đượ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ã tôi hiện đang sửa đổi và đó là một sự thúc đẩy lớn cho quy trình làm việc của tôi.

Các cờ như -c(xóa màn hình giữa các lần chạy) và -d(thoát khi một tệp mới được thêm vào thư mục được giám sát) thêm 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

Kể từ đầu năm 2018, nó vẫn đang trong giai đoạn 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 trong mọi trường hợp.


3
Không xử lý các tập tin mới và sửa đổi của họ.
Wernight

2
@Wernight - kể từ ngày 7 tháng 5 năm 2014, entr có -dcờ mới ; hơi dài dòng hơn một chút, nhưng bạn có thể làm gì while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; doneđể xử lý các tệp mới.
Paul Fenney

1
entr cũng có sẵn trong repos debian ít nhất là từ debian jessie / 8.2 vào ...
Peter V. Mørch

5
chắc chắn tôi đã tìm thấy trên OS X. fswatch lấy quá nhiều sự kiện thú vị và tôi không muốn dành thời gian để tìm hiểu tại sao
dtc

5
Điều đáng chú ý là entr có sẵn trên Homebrew, vì vậy brew install entrsẽ hoạt động như mong đợi
jmarceli 14/12/17

108

Tôi đã viết một chương trình Python để thực hiện chính xác điều này được 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

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


1
@ysangkok đúng vậy, trong phiên bản mới nhất của mã :)
joh

4
Hiện có sẵn từ "cài đặt pip khi thay đổi". Vẫn hoạt động độc đáo. Cảm ơn.
AL Flanagan

2
Để 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

1
Câu trả lời này tốt hơn 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âu trả lời.
Wolfpack'08

4
Tin vui nhé mọi người! when-changedbây giờ là đa nền tảng! Kiểm tra mới nhất 0.3.0 phát hành :)
Joh

52

Làm thế nào về kịch bản này? Nó sử dụng statlệnh để lấy thời gian truy cập của tệp và chạy lệnh bất cứ khi nào có thay đổi về thời gian truy cập (bất cứ khi nào tệp đượ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

2
Sẽ không - statthời gian sửa đổi sẽ là câu trả lời "bất cứ khi nào tập tin thay đổi" tốt hơn?
Xen2050

1
Việc chạy stat nhiều lần trong một giây sẽ khiến nhiều lần đọc vào đĩa? hoặc cuộc gọi hệ thống fstat sẽ tự động tạo bộ đệm cho 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 'đồng hồ grunt' để biên dịch mã c của mình 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 sẽ được xem trước. Tốt hơn là truyền tên tệp vào kịch bản. Vẫn tốt hơn nếu bạn có thể vượt qua nhiều tên tệp (ví dụ: "mywatch * .py"). Vẫ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, điều mà một số giải pháp khác làm.
Jonathan Hartley

5
Chỉ trong trường hợp bất cứ ai thắc mắc về việc đọc nhiều, tôi đã thử nghiệm tập lệnh này trong Ubuntu 17.04 với thời gian ngủ 0,05 giây và vmstat -dđể xem truy cập đĩa. Có vẻ như linux thực hiện một công việc tuyệt vời trong việc lưu trữ loại điều này: D
Oskenso Kashi

Có lỗi đánh máy trong "
LỰA CHỌN

30

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ì thật khó để gõ, thật khó để nhớ chính xác nên gõ gì, 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 thiện, vì nó có thể giúp đỡ người khác.

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


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

đẹp! bạn có thể tạo một macro cho .vimrctệp của mình
ErichBSchulz

23

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


5
Tuy nhiên, nó chỉ xem các tập tin .js và .coffee.
zelk

6
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

1
Tôi ước tôi có thêm thông tin, nhưng osx có một phương pháp để theo dõi các thay đổi, các thông tin
ConstantineK

1
Trên OS X, bạn cũng có thể sử dụng Launch Daemons với một WatchPathsphím như trong liên kết của tôi.
Adam Johns

19

Đối với những người không thể cài đặt inotify-toolsnhư tôi, điều này sẽ hữu ích:

watch -d -t -g ls -lR

Lệnh này sẽ thoát khi đầu ra thay đổi, ls -lRsẽ liệt kê mọi tệp và thư mục với kích thước và ngày của nó, vì vậy nếu một tệp bị thay đổi, nó sẽ thoát lệnh, như man nói:

-g, --chgexit
          Exit when the output of command changes.

Tôi biết câu trả lời này có thể không được đọc bởi bất cứ ai, nhưng tôi hy vọng ai đó sẽ tiếp cận nó.

Ví dụ dòng lệnh:

~ $ cd /tmp
~ $ watch -d -t -g ls -lR && echo "1,2,3"

Mở một thiết bị đầu cuối khác:

~ $ echo "testing" > /tmp/test

Bây giờ thiết bị đầu cuối sẽ xuất 1,2,3

Ví dụ tập lệnh đơn giản:

#!/bin/bash
DIR_TO_WATCH=${1}
COMMAND=${2}

watch -d -t -g ls -lR ${DIR_TO_WATCH} && ${COMMAND}

5
Đẹp hack. Tôi đã thử nghiệm và dường như có vấn đề khi danh sách dài và tập tin thay đổi nằm ngoài màn hình. Một sửa đổi nhỏ có thể là một cái gì đó như thế này: watch -d -t -g "ls -lR tmp | sha1sum"
Atle

3
nếu bạn xem giải pháp của mình mỗi giây, nó sẽ hoạt động mãi mãi và chỉ chạy MY_COMMAND nếu một số tệp thay đổi: watch -n1 "watch -d -t -g ls -lR && MY_COMMAND"
mnesarco 23/07/17

Phiên bản đồng hồ của tôi (Trên Linux, watch from procps-ng 3.3.10) chấp nhận số giây trôi nổi trong khoảng thời gian của nó, do đó watch -n0.2 ...sẽ thăm dò cứ sau 1/5 giây. Tốt cho những bài kiểm tra đơn vị dưới một phần nghìn giây khỏe mạnh.
Jonathan Hartley

15

rerun2( trên github ) là tập lệnh Bash 10 dòng có dạng:

#!/usr/bin/env bash

function execute() {
    clear
    echo "$@"
    eval "$@"
}

execute "$@"

inotifywait --quiet --recursive --monitor --event modify --format "%w%f" . \
| while read change; do
    execute "$@"
done

Lưu phiên bản github dưới dạng 'chạy lại' trên PATH của bạn và gọi nó bằng cách sử dụng:

rerun COMMAND

Nó chạy LỆNH mỗi khi có sự kiện sửa đổi hệ thống tệp trong thư mục hiện tại của bạn (đệ quy.)

Những điều người ta có thể thích về nó:

  • Nó sử dụng inotify, vì vậy phản ứng nhanh hơn so với bỏ phiếu. Tuyệt vời để chạy các bài kiểm tra đơn vị dưới một phần nghìn giây hoặc hiển thị các tệp chấm graphviz, mỗi khi bạn nhấn 'lưu'.
  • Bởi vì nó quá nhanh, bạn không cần phải bận tâm đến việc bỏ qua các thư mục con lớn (như node_modules) chỉ vì lý do hiệu suất.
  • Nó cực kỳ nhạy, bởi vì nó chỉ gọi inotifywait một lần, khi khởi động, thay vì chạy nó, và phát sinh thành công đắt giá của việc thiết lập đồng hồ, trên mỗi lần lặp.
  • Nó chỉ là 12 dòng của Bash
  • Bởi vì đó là Bash, nó diễn giải các lệnh bạn truyền nó chính xác như thể bạn đã gõ chúng tại dấu nhắc Bash. (Có lẽ điều này sẽ kém mát mẻ nếu bạn sử dụng vỏ khác.)
  • Nó không bị mất các sự kiện xảy ra trong khi LỆNH đang thực thi, không giống như hầu hết các giải pháp inotify khác trên trang này.
  • Trong sự kiện đầu tiên, nó bước vào 'thời gian chết' trong 0,15 giây, trong đó các sự kiện khác bị bỏ qua, trước khi LỆNH được chạy chính xác một lần. Điều này là do sự lộn xộn của các sự kiện gây ra bởi điệu nhảy tạo-ghi-di chuyển mà Vi hoặc Emacs thực hiện khi lưu bộ đệm không gây ra nhiều lần thực thi tốn công sức của bộ thử nghiệm có thể chạy chậm. Bất kỳ sự kiện nào xảy ra trong khi điều hành đang thực thi đều không được bỏ qua - chúng sẽ gây ra khoảng thời gian chết thứ hai và lần thực hiện tiếp theo.

Những điều người ta có thể không thích về nó:

  • Nó sử dụng inotify, vì vậy sẽ không hoạt động bên ngoài Linuxland.
  • Bởi vì nó sử dụng inotify, nó sẽ không cố gắng xem các thư mục chứa nhiều tệp hơn số lượng đồng hồ inotify tối đa của người dùng. Theo mặc định, điều này dường như được đặt ở mức khoảng 5.000 đến 8.000 trên các máy khác nhau mà tôi sử dụng, nhưng rất dễ tăng. Xem https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Nó không thực thi các lệnh chứa bí danh Bash. Tôi có thể thề rằng điều này được sử dụng để làm việc. Về nguyên tắc, bởi vì đây là Bash, không thực thi LỆNH trong một mạng con, tôi mong muốn nó hoạt động. Tôi rất muốn nghe Nếu có ai biết tại sao nó không. Nhiều giải pháp khác trên trang này cũng không thể thực hiện các lệnh như vậy.
  • Cá nhân tôi ước tôi có thể nhấn một phím trong thiết bị đầu cuối mà nó đang chạy để tự thực hiện thêm một lệnh điều khiển. Tôi có thể thêm cái này bằng cách nào đó, đơn giản không? Một vòng lặp đồng thời chạy 'while read -n1' cũng gọi thực thi?
  • Ngay bây giờ tôi đã mã hóa nó để xóa thiết bị đầu cuối và in LỰA CHỌN đã thực hiện trên mỗi lần lặp. Một số người có thể muốn thêm cờ dòng lệnh để tắt những thứ như thế này, v.v. Nhưng điều này sẽ tăng kích thước và độ phức tạp lên gấp nhiều lần.

Đây là một sàng lọc của anwer @ cychoi.


2
Tôi tin rằng bạn nên sử dụng "$@"thay vì $@, để làm việc đúng với các đối số có chứa khoảng trắng. Nhưng đồng thời bạn sử dụng eval, điều này buộc người dùng chạy lại phải hết sức cẩn thận khi trích dẫn.
Denilson Sá Maia

Cảm ơn Denilson. Bạn có thể đưa ra một ví dụ về nơi trích dẫn cần phải được thực hiện cẩn thận? Tôi đã sử dụng nó trong 24 giờ qua và không thấy bất kỳ vấn đề nào với không gian cho đến nay, cũng không được trích dẫn cẩn thận bất cứ điều gì - chỉ được gọi là rerun 'command'. Có phải bạn đang nói rằng nếu tôi đã sử dụng "$ @", thì người dùng có thể gọi là rerun command(không có dấu ngoặc kép?) Điều đó dường như không hữu ích đối với tôi: Tôi thường không muốn Bash thực hiện bất kỳ xử lý lệnh nào trước khi chuyển nó để chạy lại. ví dụ: nếu lệnh chứa "echo $ myvar", thì tôi sẽ muốn xem các giá trị mới của myvar trong mỗi lần lặp.
Jonathan Hartley

1
Một cái gì đó như rerun foo "Some File"có thể phá vỡ. Nhưng vì bạn đang sử dụng eval, nó có thể được viết lại thành rerun 'foo "Some File". Lưu ý rằng đôi khi việc mở rộng đường dẫn có thể giới thiệu các khoảng trắng: rerun touch *.foocó thể sẽ bị rerun 'touch *.foo'hỏng và việc sử dụng có ngữ nghĩa hơi khác nhau (việc mở rộng đường dẫn chỉ xảy ra một lần hoặc nhiều lần).
Denilson Sá Maia

Cảm ơn đã giúp đỡ. Yep: rerun ls "some file"nghỉ vì không gian. rerun touch *.foo*hoạt động tốt thường, nhưng không thành công nếu tên tệp khớp với * .foo chứa khoảng trắng. Cảm ơn vì đã giúp tôi xem làm thế nào rerun 'touch *.foo'có ngữ nghĩa khác nhau, nhưng tôi nghi ngờ phiên bản có dấu ngoặc đơn là ngữ nghĩa tôi muốn: Tôi muốn mỗi lần lặp lại chạy lại như thể tôi gõ lại lệnh - do đó tôi muốn *.foo được mở rộng trên mỗi lần lặp . Tôi sẽ thử đề xuất của bạn để kiểm tra hiệu ứng của chúng ...
Jonathan Hartley

Thảo luận thêm về PR này ( github.com/tartley/rerun2/pull/1 ) và những người khác.
Jonathan Hartley

12

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

  1. Có hai đối số: tệp cần giám sát và lệnh (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 hai giây một lần để 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 đè lên bản sao bằng bản gốc mới hơn và thực thi 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 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 chỉ có thể mã cứng tên tệp tạm thời.


9
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 (inotify trên Linux, kqueue trên FreeBSD, ...). Bạn có một vấn đề trích dẫn nghiêm trọng khi bạn làm $cmd, nhưng may mắn là điều đó có thể dễ dàng sửa chữa: bỏ cmdbiến và thực hiện "$@". Tập lệnh của bạn không phù hợp để theo dõi một tệp lớn, nhưng điều đó có thể được khắc phục bằng cách thay thế cpbằng touch -r(bạn chỉ cần ngày, không phải nội dung). Tính di động, -ntthử nghiệm yêu cầu bash, ksh hoặc zsh.
Gilles

8

Có một cái nhìn vào incron . Nó tương tự như cron, nhưng sử dụng các sự kiện inotify thay vì thời gian.


Điều này có thể được thực hiện để làm việc, nhưng tạo ra một mục incron là một quá trình tốn nhiều công sức so với các giải pháp khác trên trang này.
Jonathan Hartley

6

Một giải pháp khác với NodeJs, fsmonitor :

  1. Tải về

    sudo npm install -g fsmonitor
    
  2. Từ dòng lệnh (ví dụ: theo dõi nhật ký và "bán lẻ" nếu một tệp nhật ký thay đổi)

    fsmonitor -s -p '+*.log' sh -c "clear; tail -q *.log"
    

Lưu ý bên lề: ví dụ có thể được giải quyết bằng tail -F -q *.log, tôi nghĩ vậy.
Volker Siegel

Nó chỉ là một ví dụ, tail -fkhông phải clearlà thiết bị đầu cuối.
Atika

6

Nhìn vào Guard, đặc biệt với plugin này:

https://github.com/hawx/guard-shell

Bạn có thể thiết lập nó để xem bất kỳ số lượng mẫu nào trong thư mục dự án của bạn và thực hiện các lệnh khi có thay đổi. Cơ hội tốt ngay cả khi có một plugin có sẵn cho những gì bạn đang cố gắng làm ở nơi đầu tiên.


6

Nếu bạn đã cài đặt gật đầu , thì bạn có thể làm điều này:

nodemon -w <watch directory> -x "<shell command>" -e ".html"

Trong trường hợp của tôi, tôi chỉnh sửa html cục bộ và gửi nó đến máy chủ từ xa khi tệp thay đổi.

nodemon -w <watch directory> -x "scp filename jaym@jay-remote.com:/var/www" -e ".html"

6

Trong Linux:

man watch

watch -n 2 your_command_to_run

Sẽ chạy lệnh cứ sau 2 giây.

Nếu lệnh của bạn mất hơn 2 giây để chạy, đồng hồ sẽ đợi cho đến khi hoàn thành trước khi thực hiện lại.


Điều đó khá đơn giản, mặc dù hơi lãng phí, thật dễ dàng cho các nhiệm vụ phát triển như thay đổi trực tiếp các kiểu dáng.
Xeoncross

2
Điều gì xảy ra khi lệnh mất nhiều hơn hai giây để chạy?
thứ ba mươi 24/2/2015

@thentythreeforty Một thử nghiệm nhanh trên Ubuntu cho thấy đồng hồ sẽ đợi đủ hai giây cho dù lệnh có mất bao lâu để chạy. FWIW, thời gian ngủ có thể được chỉ định bằng '-n', xuống tối thiểu 0,1 giây.
Jonathan Hartley

5

Watchdog là một dự án Python và có thể là những gì bạn đang tìm kiếm:

Nền tảng được hỗ trợ

  • Linux 2.6 (inotify)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD / BSD (kqueue)
  • Windows (ReadDirectoryChangesW với các cổng hoàn thành I / O; Các luồng công nhân ReadDirectoryChangesW)
  • Độc lập với hệ điều hành (bỏ phiếu cho các ảnh chụp nhanh thư mục và so sánh chúng định kỳ; chậm và không được đề xuất)

Chỉ cần viết một trình bao bọc dòng lệnh cho nó watchdog_exec:

Ví dụ chạy

Trên sự kiện fs liên quan đến các tệp và thư mục trong thư mục hiện tại, hãy chạy echo $src $dstlệnh, trừ khi sự kiện fs được sửa đổi, sau đó chạy python $srclệnh.

python -m watchdog_exec . --execute echo --modified python

Sử dụng các đối số ngắn và hạn chế chỉ thực hiện khi các sự kiện liên quan đến " main .py":

python -m watchdog_exec . -e echo -a echo -s __main__.py

EDIT: Vừa tìm thấy Watchdog có CLI chính thức được gọi watchmedo, vì vậy hãy kiểm tra xem.


4

Nếu chương trình của bạn tạo ra một số loại nhật ký / đầu ra, bạn có thể tạo một Makefile với quy tắc cho nhật ký / đầu ra đó phụ thuộc vào tập lệnh của bạn và làm một cái gì đó như

while true; do make -s my_target; sleep 1; done

Thay phiên, bạn có thể tạo mục tiêu giả mạo và có quy tắc cho cả hai gọi tập lệnh của bạn và chạm vào mục tiêu giả mạo (trong khi vẫn phụ thuộc vào tập lệnh của bạn).


11
while sleep 1 ; do something ; donelà tốt hơn một chút while true ; do something ; sleep 1 ; done. Ít nhất nó dừng lại dễ dàng khi nhấn Ctrl + C.
Denilson Sá Maia

Sẽ loại bỏ giấc ngủ gây ra một vòng lặp bận rộn (CPU tạo nhiệt và làm giảm tuổi thọ pin trên máy tính xách tay)?
Steven Lu

2
@StevenLu: không, giấc ngủ không phải là một sự chờ đợi bận rộn. Vấn đề là nếu giấc ngủ ở trong cơ thể, Control-C sẽ giết chết giấc ngủ và vòng lặp sẽ bắt đầu lại. Việc sử dụng năng lượng của việc bắt đầu vòng lặp là không đáng kể. Hãy thử nó trong một thiết bị đầu cuối. Bạn cần giữ Control-C để nó hoạt động, nếu bạn có giấc ngủ trong cơ thể.
Janus Troelsen

Đúng. Tôi nghĩ rằng tôi đã bỏ lỡ nó và không thấy rằng giấc ngủ vẫn còn là điều kiện vòng lặp. Đó là tinh chỉnh nhỏ là khá tuyệt vời.
Steven Lu

4

2
Đây là tập lệnh Bash 200 dòng được đóng gói tính năng, thăm dò statcác tên tệp đã cho, chạy md5sumtrên đầu ra và chạy lại lệnh đã cho nếu giá trị này thay đổi. Bởi vì đó là Bash, tôi nghi ngờ rằng nó thực hiện tốt việc chạy lệnh đã cho chính xác như thể bạn đã gõ nó tại dấu nhắc Bash. (Ngược lại, hầu hết các giải pháp ở đây được viết bằng các ngôn ngữ khác sẽ không thực hiện được các lệnh, ví dụ, có chứa các bí danh shell như ll)
Jonathan Hartley

4

Cải thiện theo câu trả lời của Gilles .

Phiên bản này chạy inotifywaitmột lần và theo dõi các sự kiện (.eg :) modifysau đó. Như vậy inotifywait không cần phải được thực hiện lại cho mỗi sự kiện gặp phải.

Thật nhanh và nhanh! (ngay cả khi theo dõi đệ quy thư mục lớn)

inotifywait --quiet --monitor --event modify FILE | while read; do
    # trim the trailing space from inotifywait output
    REPLY=${REPLY% }
    filename=${REPLY%% *}
    # do whatever you want with the $filename
done

Đây là câu trả lời tốt nhất trên trang dành cho người dùng chỉ dành cho Linux. Thay thế nội dung trong vòng lặp bằng 'thực thi $ @' và người dùng có thể gọi tập lệnh này chuyển qua lệnh riêng của họ để chạy. Nó thậm chí hoạt động với các lệnh có chứa bí danh shell nếu bạn lấy nguồn đó, sử dụng cái gì đó như ". Scriptname Mission". Điều này vẫn sẽ tìm thấy scriptname trên PATH.
Jonathan Hartley

Tôi nghĩ bạn có nghĩa là đặt 'trong khi đọc TRẢ LỜI'?
Jonathan Hartley

1
Cảm ơn bạn đã làm rõ. Unthanks cho giai đoạn của nó! Tôi đã xóa những bình luận đó, nhưng tất nhiên bây giờ tôi sẽ không.
Jonathan Hartley

3

Một chút nữa về phía lập trình, nhưng bạn muốn một cái gì đó như inotify . Có các triển khai trong nhiều ngôn ngữ, chẳng hạn như jnotifypyinotify .

Thư viện này cho phép bạn giám sát các tệp đơn lẻ hoặc toàn bộ thư mục và trả về các sự kiện khi một hành động được phát hiện. Thông tin được trả về bao gồm tên tệp, hành động (tạo, sửa đổi, đổi tên, xóa) và đường dẫn tệp, trong số các thông tin hữu ích khác.


3

Đối với những bạn đang tìm kiếm giải pháp FreeBSD, đây là cổng:

/usr/ports/sysutils/wait_on

3

Tôi thích sự đơn giản while inotifywait ...; do ...; donetuy nhiên nó có hai vấn đề:

  • Thay đổi tập tin xảy ra trong quá trình do ...;sẽ bị bỏ lỡ
  • Chậm khi sử dụng ở chế độ đệ quy

Do đó, tôi đã tạo một tập lệnh trợ giúp sử dụng inotifywait mà không có những hạn chế đó: inotifyexec

Tôi đề nghị bạn đặt tập lệnh này vào đường dẫn của bạn, như trong ~/bin/. Cách sử dụng được mô tả bằng cách chỉ chạy lệnh.

Thí dụ: inotifyexec "echo test" -r .


Cập nhật tập lệnh để hỗ trợ khớp mẫu regex.
Wernight

Cả hai vấn đề đều được giải quyết bằng cách sử dụng inotifywait trong chế độ "--monitor". Xem câu trả lời của cychoi.
Jonathan Hartley

3

Cải thiện giải pháp của Sebastian với watchlệnh:

watch_cmd.sh:

#!/bin/bash
WATCH_COMMAND=${1}
COMMAND=${2}

while true; do
  watch -d -g "${WATCH_COMMAND}"
  ${COMMAND}
  sleep 1     # to allow break script by Ctrl+c
done

Gọi ví dụ:

watch_cmd.sh "ls -lR /etc/nginx | grep .conf$" "sudo service nginx reload"

Nó hoạt động nhưng hãy cẩn thận: watchlệnh đã biết lỗi (xem man): nó chỉ phản ứng với những thay đổi chỉ trong VISIBLE trong các phần -g CMDđầu ra của đầu ra.


2

Bạn có thể thử phản xạ .

Reflex là một công cụ nhỏ để xem thư mục và chạy lại lệnh khi một số tệp nhất định thay đổi. Thật tuyệt vời khi tự động chạy các tác vụ biên dịch / lint / test và để tải lại ứng dụng của bạn khi mã thay đổi.

# Rerun make whenever a .c file changes
reflex -r '\.c$' make

Bạn có thể trích dẫn / giải thích một chút về công cụ này không? Đọc nhanh về cách giới thiệu phần mềm để được hướng dẫn.
bertieb

1

Câu trả lời của oneliner mà tôi đang sử dụng để theo dõi thay đổi tập tin:

$ while true ; do NX=`stat -c %Z file` ; [[ $BF != $NX ]] && date >> ~/tmp/fchg && BF=$NX || sleep 2 ; done

Bạn không cần phải khởi tạo BF nếu bạn biết rằng ngày đầu tiên là thời gian bắt đầu.

Điều này là đơn giản và di động. Có một câu trả lời khác dựa trên cùng một chiến lược sử dụng một kịch bản ở đây. Hãy xem đi.


Cách sử dụng: Tôi đang sử dụng điều này để gỡ lỗi và theo dõi ~/.kde/share/config/plasma-desktop-appletsrc; rằng vì một số lý do không rõ, tôi mấtSwitchTabsOnHover=false


1

Tôi sử dụng kịch bản này để làm điều đó. Tôi đang sử dụng inotify ở chế độ màn hình

#!/bin/bash
MONDIR=$(dirname $1)
ARQ=$(basename $1)

inotifywait -mr -e close_write $MONDIR | while read base event file 
do
  if (echo $file |grep -i "$ARQ") ; then
    $1
  fi
done

Lưu cái này dưới dạng runatwrite.sh

Usage: runatwrite.sh myfile.sh

nó sẽ chạy myfile.sh tại mỗi lần viết.


1

Đối với những người sử dụng OS X, bạn có thể sử dụng LaunchAgent để xem đường dẫn / tệp để thay đổi và làm điều gì đó khi điều đó xảy ra. FYI - LaunchControl là một ứng dụng tốt để dễ dàng thực hiện / sửa đổi / xóa daemon / tác nhân.

( ví dụ lấy từ đây )

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>test</string>
    <key>ProgramArguments</key>
    <array>
        <string>say</string>
        <string>yy</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>~/Desktop/</string>
    </array>
</dict>
</plist>


0

Đối với những người tìm thấy điều này bởi Googling để thay đổi một tệp cụ thể , câu trả lời đơn giản hơn nhiều (lấy cảm hứng từ câu trả lời của Gilles ).

Nếu bạn muốn làm gì đó sau khi một tệp cụ thể đã được ghi vào, đây là cách:

while true; do
  inotifywait -e modify /path/to/file
  # Do something *after* a write occurs, e.g. copy the file
  /bin/cp /path/to/file /new/path
done

Lưu ví dụ này, ví dụ, copy_myfile.shvà đặt .shtệp vào /etc/init.d/thư mục để nó chạy khi khởi động.


Chia sẻ vấn đề với câu trả lời của Giles rằng nó chạy inotifywait trên mỗi lần lặp, điều này có thể không phản hồi khi xem đệ quy các thư mục rất lớn. Xem câu trả lời của cychoi để khắc phục điều này.
Jonathan Hartley


0

Như một vài người khác đã làm, tôi cũng đã viết một công cụ dòng lệnh nhẹ để làm điều này. Đó là tài liệu đầy đủ, thử nghiệm và mô-đun.

Đồng hồ

Cài đặt

Bạn có thể cài đặt nó (nếu bạn có Python3 và pip) bằng cách sử dụng:

pip3 install git+https://github.com/vimist/watch-do

Sử dụng

Sử dụng ngay lập tức bằng cách chạy:

watch-do -w my_file -d 'echo %f changed'

Tổng quan về tính năng

  • Hỗ trợ tập tin toàn cầu (sử dụng -w '*.py'hoặc -w '**/*.py')
  • Chạy nhiều lệnh khi thay đổi tệp (chỉ cần xác định lại -dcờ)
  • Tự động duy trì danh sách các tệp cần xem nếu sử dụng toàn cầu ( -rđể bật tính năng này)
  • Nhiều cách để "xem" một tệp:
    • Thời gian sửa đổi (mặc định)
    • Băm tập tin
    • Tầm thường để thực hiện của riêng bạn (đây là trình theo dõi ModificationTime )
  • Thiết kế mô-đun. Nếu bạn muốn có các lệnh chạy, khi một tệp được truy cập, việc viết trình theo dõi của riêng bạn là rất đơn giản (cơ chế xác định xem có nên chạy hay không).
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.