Bagaimana cara mengeksekusi perintah setiap kali file berubah?

434

Saya ingin cara cepat dan sederhana untuk mengeksekusi perintah setiap kali file berubah. Saya menginginkan sesuatu yang sangat sederhana, sesuatu yang akan saya tinggalkan berjalan di terminal dan menutupnya setiap kali saya selesai bekerja dengan file itu.

Saat ini, saya menggunakan ini:

while read; do ./myfile.py ; done

Dan kemudian saya harus pergi ke terminal itu dan tekan Enter, setiap kali saya menyimpan file itu di editor saya. Yang saya inginkan adalah sesuatu seperti ini:

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

Atau solusi lain semudah itu.

BTW: Saya menggunakan Vim, dan saya tahu saya bisa menambahkan perintah otomatis untuk menjalankan sesuatu di BufWrite, tapi ini bukan solusi yang saya inginkan sekarang.

Pembaruan: Saya menginginkan sesuatu yang sederhana, jika mungkin dibuang. Terlebih lagi, saya ingin sesuatu berjalan di terminal karena saya ingin melihat output program (saya ingin melihat pesan kesalahan).

Tentang jawabannya: Terima kasih atas semua jawaban Anda! Semuanya sangat baik, dan masing-masing mengambil pendekatan yang sangat berbeda dari yang lain. Karena saya hanya perlu menerima satu, saya menerima yang sudah saya gunakan (itu sederhana, cepat dan mudah diingat), meskipun saya tahu itu bukan yang paling elegan.

Denilson Sa Maia
sumber
Kemungkinan duplikat situs silang dari: stackoverflow.com/questions/2972765/… (meskipun di sini ada topik =))
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
saya telah direferensikan sebelum duplikat situs lintas dan ditolak: S;)
Francisco Tapia
4
Solusi oleh Jonathan Hartley dibangun di atas solusi lain di sini dan memperbaiki masalah besar yang dimiliki jawaban terpilih: kehilangan beberapa modifikasi dan menjadi tidak efisien. Silakan ubah jawaban yang diterima untuk jawabannya, yang juga dipertahankan di github di github.com/tartley/rerun2 (atau solusi lain tanpa cacat itu)
nealmcb

Jawaban:

405

Sederhana, menggunakan inotifywait (instal inotify-toolspaket distribusi Anda ):

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

atau

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

Cuplikan pertama lebih sederhana, tetapi memiliki kelemahan signifikan: ia akan kehilangan perubahan yang dilakukan saat inotifywaittidak berjalan (khususnya saat myfilesedang berjalan). Cuplikan kedua tidak memiliki cacat ini. Namun, berhati-hatilah karena ini mengasumsikan bahwa nama file tidak mengandung spasi. Jika itu masalah, gunakan --formatopsi untuk mengubah output untuk tidak menyertakan nama file:

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

Either way, ada batasan: jika beberapa program mengganti myfile.pydengan file yang berbeda, daripada menulis ke yang sudah ada myfile, inotifywaitakan mati. Banyak editor bekerja seperti itu.

Untuk mengatasi batasan ini, gunakan inotifywaitpada direktori:

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

Sebagai alternatif, gunakan alat lain yang menggunakan fungsi dasar yang sama, seperti incron (memungkinkan Anda mendaftarkan acara saat file dimodifikasi) atau fswatch (alat yang juga berfungsi pada banyak varian Unix lainnya, menggunakan analog masing-masing varian dari Linux yang tidak diotifikasi).

Gilles
sumber
46
Saya telah merangkum semua ini (dengan beberapa trik bash) dalam sleep_until_modified.shskrip yang mudah digunakan , tersedia di: bitbucket.org/denilsonsa/small_scripts/src
Denilson Sá Maia
14
while sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; donefantastis. Terima kasih.
Rhys Ulerich
5
inotifywait -e delete_selftampaknya bekerja dengan baik untuk saya.
Kos
3
Ini sederhana tetapi memiliki dua masalah penting: Peristiwa mungkin terlewatkan (semua peristiwa dalam loop) dan inisialisasi inotifywait dilakukan setiap kali yang membuat solusi ini lebih lambat untuk folder rekursif besar.
Wernight
6
Untuk beberapa alasan while inotifywait -e close_write myfile.py; do ./myfile.py; doneselalu keluar tanpa menjalankan perintah (bash dan zsh). Agar ini berfungsi, saya perlu menambahkan || true, misalnya: while inotifywait -e close_write myfile.py || true; do ./myfile.py; done
ideasman42
166

entr ( http://entrproject.org/ ) menyediakan antarmuka yang lebih ramah untuk memberitahukan (dan juga mendukung * BSD & Mac OS X).

Itu membuatnya sangat mudah untuk menentukan beberapa file untuk ditonton (dibatasi hanya oleh ulimit -n), menghilangkan kerumitan berurusan dengan file yang diganti, dan membutuhkan sintaksis kurang bash:

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

Saya telah menggunakannya di seluruh pohon sumber proyek saya untuk menjalankan tes unit untuk kode yang saya modifikasi saat ini, dan itu sudah merupakan dorongan besar untuk alur kerja saya.

Panji suka -c(hapus layar di antara menjalankan) dan -d(keluar saat file baru ditambahkan ke direktori yang dipantau) menambah lebih banyak fleksibilitas, misalnya yang dapat Anda lakukan:

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

Pada awal 2018 masih dalam pengembangan aktif dan dapat ditemukan di Debian & Ubuntu ( apt install entr); membangun dari repo penulis itu bebas rasa sakit dalam hal apapun.

Paul Fenney
sumber
3
Tidak menangani file baru dan modifikasinya.
Wernight
2
@Wernight - mulai 7 Mei 2014 entr memiliki -dbendera baru ; ini sedikit lebih bertele-tele, tetapi Anda bisa lakukan while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; doneuntuk menangani file baru.
Paul Fenney
1
entr juga tersedia di repo debian setidaknya dari debian jessie / 8.2 pada ...
Peter V. Mørch
5
terbaik yang saya temukan di OS X pasti. fswatch mengambil terlalu banyak acara yang funky dan saya tidak ingin menghabiskan waktu untuk mencari tahu mengapa
dtc
5
Perlu dicatat bahwa entr tersedia di Homebrew, jadi brew install entrakan berfungsi seperti yang diharapkan
jmarceli
108

Saya menulis sebuah program Python untuk melakukan hal ini dengan tepat yang disebut ketika-diubah .

Penggunaannya sederhana:

when-changed FILE COMMAND...

Atau untuk menonton banyak file:

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

FILEbisa berupa direktori. Tonton secara rekursif dengan -r. Gunakan %funtuk meneruskan nama file ke perintah.

joh
sumber
1
@ysangkok ya, dalam versi terbaru dari kode :)
joh
4
Sekarang tersedia dari "pip install when-berubah". Masih bekerja dengan baik. Terima kasih.
AL Flanagan
2
Untuk menghapus layar pertama yang bisa Anda gunakan when-changed FILE 'clear; COMMAND'.
Dave James Miller
1
Jawaban ini jauh lebih baik karena saya juga bisa melakukannya di Windows. Dan orang ini benar-benar menulis sebuah program untuk mendapatkan jawabannya.
Wolfpack'08
4
Berita baik semuanya! when-changedsekarang lintas platform! Lihat rilis 0.3.0 terbaru :)
joh
52

Bagaimana dengan skrip ini? Ia menggunakan statperintah untuk mendapatkan waktu akses file dan menjalankan perintah setiap kali ada perubahan dalam waktu akses (setiap kali file diakses).

#!/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
VDR
sumber
2
Bukankah statwaktu modifikasi akan lebih baik "setiap kali file berubah" menjawab?
Xen2050
1
Apakah menjalankan stat berkali-kali per detik menyebabkan banyak membaca ke disk? atau apakah panggilan sistem fstat secara otomatis membuat cache tanggapan ini entah bagaimana? Saya mencoba menulis semacam 'gerutuan menonton' untuk mengkompilasi kode c saya setiap kali saya membuat perubahan
Oskenso Kashi
Ini bagus jika Anda tahu nama file yang harus ditonton terlebih dahulu. Lebih baik memberikan nama file ke skrip. Lebih baik lagi jika Anda bisa melewati banyak nama file (mis. "Mywatch * .py"). Lebih baik lagi jika bisa beroperasi secara rekursif pada file dalam subdir juga, yang dilakukan oleh beberapa solusi lain.
Jonathan Hartley
5
Kalau-kalau ada yang bertanya-tanya tentang bacaan yang berat, saya menguji skrip ini di Ubuntu 17,04 dengan tidur 0,05 dan vmstat -duntuk melihat akses disk. Tampaknya linux melakukan pekerjaan yang luar biasa untuk melakukan caching hal ini: D
Oskenso Kashi
Ada kesalahan ketik dalam "PERINTAH", saya mencoba untuk memperbaikinya, tetapi SO mengatakan "Edit tidak boleh kurang dari 6 karakter"
user337085
30

Solusi menggunakan Vim:

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

Tapi saya tidak ingin solusi ini karena agak menjengkelkan untuk mengetik, agak sulit untuk mengingat apa yang harus diketik, dan agak sulit untuk membatalkan efeknya (perlu dijalankan :au! BufWritePost myfile.py). Selain itu, solusi ini memblokir Vim sampai perintah selesai dieksekusi.

Saya telah menambahkan solusi ini di sini hanya untuk kelengkapan, karena dapat membantu orang lain.

Untuk menampilkan output program (dan benar-benar mengganggu aliran pengeditan Anda, karena output akan menulis di editor Anda selama beberapa detik, sampai Anda menekan Enter), hapus :silentperintah.

Denilson Sa Maia
sumber
1
Ini bisa sangat bagus ketika dikombinasikan dengan entr(lihat di bawah) - buat saja vim touch file dummy yang sedang ditonton oleh entr, dan biarkan entr melakukan sisanya di latar belakang ... atau tmux send-keysjika Anda berada di lingkungan seperti itu :)
Paul Fenney
bagus! Anda dapat membuat makro untuk .vimrcfile Anda
ErichBSchulz
23

Jika Anda telah npmmenginstal, nodemonmungkin cara termudah untuk memulai, terutama pada OS X, yang tampaknya tidak memiliki alat inotify. Ini mendukung menjalankan perintah ketika folder berubah.

davidtbernal
sumber
5
Namun, itu hanya menonton file .js dan .coffee.
zelk
6
Versi saat ini tampaknya mendukung perintah apa pun, misalnya: nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/models
kek
1
Saya berharap saya memiliki lebih banyak info, tetapi osx memang memiliki metode untuk melacak perubahan, fsevents
ConstantineK
1
Pada OS X Anda juga dapat menggunakan Launch Daemon dengan WatchPathskunci seperti yang ditunjukkan pada tautan saya.
Adam Johns
19

Bagi mereka yang tidak dapat menginstal inotify-toolsseperti saya, ini harus bermanfaat:

watch -d -t -g ls -lR

Perintah ini akan keluar ketika output berubah, ls -lRakan mencantumkan setiap file dan direktori dengan ukuran dan tanggalnya, jadi jika file diubah itu harus keluar dari perintah, seperti yang dikatakan manusia:

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

Saya tahu jawaban ini mungkin tidak dibaca oleh siapa pun, tetapi saya berharap seseorang akan mencapainya.

Contoh baris perintah:

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

Buka terminal lain:

~ $ echo "testing" > /tmp/test

Sekarang terminal pertama akan di-output 1,2,3

Contoh skrip sederhana:

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

watch -d -t -g ls -lR ${DIR_TO_WATCH} && ${COMMAND}
Sebastian
sumber
5
Retas yang bagus. Saya menguji dan tampaknya memiliki masalah ketika daftar panjang dan file yang diubah jatuh di luar layar. Modifikasi kecil bisa jadi seperti ini: watch -d -t -g "ls -lR tmp | sha1sum"
Atle
3
jika Anda melihat solusi Anda setiap detik, ia berfungsi selamanya dan menjalankan MY_COMMAND hanya jika beberapa file berubah: watch -n1 "watch -d -t -g ls -lR && MY_COMMAND"
mnesarco
Versi arloji saya (Di Linux, watch from procps-ng 3.3.10) menerima float detik untuk intervalnya, karenanya watch -n0.2 ...akan melakukan polling setiap perlima detik. Bagus untuk tes unit sub-milidetik yang sehat.
Jonathan Hartley
15

rerun2( on github ) adalah skrip Bash 10-baris dari formulir:

#!/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

Simpan versi github sebagai 'jalankan kembali' di PATH Anda, dan panggil menggunakan:

rerun COMMAND

Ini menjalankan PERINTAH setiap kali ada acara memodifikasi sistem file dalam direktori Anda saat ini (rekursif.)

Hal-hal yang mungkin orang sukai tentang itu:

  • Ini menggunakan inotify, jadi lebih responsif daripada polling. Luar biasa untuk menjalankan tes unit sub-milidetik, atau rendering file graphviz dot, setiap kali Anda menekan 'save'.
  • Karena begitu cepat, Anda tidak perlu repot-repot mengatakannya untuk mengabaikan subdir besar (seperti node_modules) hanya karena alasan kinerja.
  • Ini ekstra super responsif, karena hanya memanggil tidak menunggu satu kali, pada startup, bukan menjalankannya, dan menimbulkan hit mahal membangun jam tangan, pada setiap iterasi.
  • Itu hanya 12 baris Bash
  • Karena itu Bash, ia mengartikan perintah yang Anda berikan persis seperti jika Anda mengetikkannya di prompt Bash. (Agaknya ini kurang keren jika Anda menggunakan shell lain.)
  • Itu tidak kehilangan peristiwa yang terjadi ketika COMMAND sedang mengeksekusi, tidak seperti kebanyakan dari solusi tidak sah lainnya di halaman ini.
  • Pada acara pertama, ia memasuki 'periode mati' selama 0,15 detik, di mana peristiwa lain diabaikan, sebelum PERINTAH dijalankan tepat sekali. Ini dimaksudkan agar berbagai peristiwa yang disebabkan oleh tarian buat-tulis-gerak yang dilakukan Vi atau Emacs saat menyimpan buffer tidak menyebabkan banyak eksekusi yang melelahkan dari test suite yang mungkin berjalan lambat. Peristiwa apa pun yang terjadi saat COMMAND sedang dieksekusi tidak diabaikan - mereka akan menyebabkan periode mati kedua dan eksekusi berikutnya.

Hal-hal yang orang mungkin tidak suka tentang itu:

  • Ini menggunakan inotify, jadi tidak akan berfungsi di luar Linuxland.
  • Karena ia menggunakan inotify, ia akan berusaha untuk mencoba menonton direktori yang berisi lebih banyak file daripada jumlah maksimum pengguna yang memberikan inotify jam tangan. Secara default, ini tampaknya diatur sekitar 5.000 hingga 8.000 pada mesin yang berbeda yang saya gunakan, tetapi mudah untuk ditingkatkan. Lihat https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Gagal menjalankan perintah yang mengandung alias Bash. Saya bersumpah bahwa ini dulu berhasil. Pada prinsipnya, karena ini Bash, tidak mengeksekusi PERINTAH dalam subkulit, saya berharap ini berfungsi. Saya ingin sekali mendengar. Jika ada yang tahu mengapa tidak. Banyak solusi lain di halaman ini yang tidak dapat menjalankan perintah seperti itu juga.
  • Secara pribadi saya berharap saya dapat menekan tombol di terminal yang sedang berjalan untuk secara manual menyebabkan eksekusi ekstra PERINTAH. Bisakah saya menambahkan ini entah bagaimana, sederhana? Secara bersamaan menjalankan 'while read -n1' loop yang juga memanggil eksekusi?
  • Saat ini saya telah mengkodekannya untuk menghapus terminal dan mencetak PERINTAH yang dieksekusi pada setiap iterasi. Beberapa orang mungkin ingin menambahkan bendera baris perintah untuk mematikan hal-hal seperti ini, dll. Tetapi ini akan meningkatkan ukuran dan kompleksitas berlipat-lipat.

Ini adalah penyempurnaan dari jawaban @ cychoi.

Jonathan Hartley
sumber
2
Saya percaya Anda harus menggunakan "$@"alih-alih $@, agar berfungsi dengan baik dengan argumen yang berisi spasi. Tetapi pada saat yang sama Anda gunakan eval, yang memaksa pengguna menjalankan kembali ekstra hati-hati saat mengutip.
Denilson Sá Maia
Terima kasih Denilson. Bisakah Anda memberi contoh di mana mengutip perlu dilakukan dengan hati-hati? Saya telah menggunakannya 24 jam terakhir dan belum melihat masalah dengan ruang sejauh ini, atau dengan cermat mengutip apa pun - hanya dipanggil sebagai rerun 'command'. Apakah Anda hanya mengatakan bahwa jika saya menggunakan "$ @", maka pengguna bisa memanggil sebagai rerun commandItu tampaknya tidak berguna bagi saya (tanpa tanda kutip?): Saya biasanya tidak ingin Bash melakukan setiap pengolahan perintah sebelum diteruskan untuk menjalankan kembali. mis. jika perintah mengandung "echo $ myvar", maka saya ingin melihat nilai-nilai baru myvar di setiap iterasi.
Jonathan Hartley
1
Sesuatu seperti rerun foo "Some File"mungkin pecah. Tetapi karena Anda menggunakan eval, itu dapat ditulis ulang sebagai rerun 'foo "Some File". Perhatikan bahwa terkadang perluasan jalur mungkin menghasilkan spasi: rerun touch *.fookemungkinan akan pecah, dan menggunakan rerun 'touch *.foo'memiliki semantik yang sedikit berbeda (perluasan jalur hanya terjadi sekali, atau beberapa kali).
Denilson Sá Maia
Terima kasih untuk bantuannya. Yap: rerun ls "some file"pecah karena spasi. rerun touch *.foo*biasanya berfungsi dengan baik, tetapi gagal jika nama file yang cocok * .foo berisi spasi. Terima kasih telah membantu saya melihat rerun 'touch *.foo'perbedaan semantik, tetapi saya menduga versi dengan tanda kutip tunggal adalah semantik yang saya inginkan: Saya ingin setiap iterasi tayangan ulang bertindak seolah-olah saya mengetikkan perintah lagi - maka saya ingin *.foo diperluas pada setiap iterasi . Saya akan mencoba saran Anda untuk memeriksa efeknya ...
Jonathan Hartley
Diskusi lebih lanjut tentang PR ini ( github.com/tartley/rerun2/pull/1 ) dan lainnya.
Jonathan Hartley
12

Berikut ini skrip shell Bourne shell sederhana yang:

  1. Membawa dua argumen: file yang akan dimonitor dan perintah (dengan argumen, jika perlu)
  2. Menyalin file yang Anda pantau ke direktori / tmp
  3. Memeriksa setiap dua detik untuk melihat apakah file yang Anda pantau lebih baru dari salinan
  4. Jika lebih baru itu menimpa salinan dengan yang lebih baru dan menjalankan perintah
  5. Bersihkan setelahnya sendiri saat Anda menekan Rtr-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  
    

Ini berfungsi pada FreeBSD. Satu-satunya masalah portabilitas yang dapat saya pikirkan adalah jika beberapa Unix lain tidak memiliki perintah mktemp (1), tetapi dalam hal ini Anda hanya dapat membuat kode nama file temp yang sulit.

MikeyMike
sumber
9
Polling adalah satu-satunya cara portabel, tetapi sebagian besar sistem memiliki mekanisme pemberitahuan perubahan file (inotify di Linux, kqueue di FreeBSD, ...). Anda memiliki masalah mengutip yang parah ketika melakukannya $cmd, tetapi untungnya itu mudah diperbaiki: parit cmdvariabel dan jalankan "$@". Skrip Anda tidak cocok untuk memantau file besar, tetapi itu bisa diperbaiki dengan mengganti cpdengan touch -r(Anda hanya perlu tanggal, bukan konten). Dari sisi portabilitas, -nttes ini membutuhkan bash, ksh atau zsh.
Gilles
8

Lihatlah incron . Ini mirip dengan cron, tetapi menggunakan acara yang tidak resmi alih-alih waktu.

Florian Diesch
sumber
Ini bisa dilakukan untuk bekerja, tetapi membuat entri incron cukup proses padat karya dibandingkan dengan solusi lain di halaman ini.
Jonathan Hartley
6

Solusi lain dengan NodeJs, fsmonitor :

  1. Pasang

    sudo npm install -g fsmonitor
    
  2. Dari baris perintah (misalnya, monitor log dan "eceran" jika satu file log berubah)

    fsmonitor -s -p '+*.log' sh -c "clear; tail -q *.log"
    
Atika
sumber
Catatan: contohnya bisa diselesaikan dengan tail -F -q *.log, saya pikir.
Volker Siegel
Itu hanya untuk memberi contoh, tail -fbukan clearterminal.
Atika
6

Lihatlah ke Penjaga, khususnya dengan plugin ini:

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

Anda dapat mengaturnya untuk menonton sejumlah pola di direktori proyek Anda, dan menjalankan perintah ketika perubahan terjadi. Kesempatan baik bahkan bahwa ada plugin yang tersedia untuk itu apa yang Anda coba lakukan di tempat pertama.

Wouter Van Vliet
sumber
6

jika Anda telah menginstal nodemon , maka Anda dapat melakukan ini:

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

Dalam kasus saya, saya mengedit html secara lokal dan mengirimkannya ke server jarak jauh saya ketika file berubah.

nodemon -w <watch directory> -x "scp filename [email protected]:/var/www" -e ".html"
Jay
sumber
6

Di bawah Linux:

man watch

watch -n 2 your_command_to_run

Akan menjalankan perintah setiap 2 detik.

Jika perintah Anda membutuhkan lebih dari 2 detik untuk dijalankan, jam akan menunggu sampai selesai sebelum melakukannya lagi.

Eric Leschinski
sumber
Itu cukup sederhana, meskipun agak sia-sia, mudah untuk tugas pengembangan seperti membuat perubahan langsung ke gaya.
Xeoncross
2
Apa yang terjadi ketika perintah membutuhkan waktu lebih dari dua detik untuk dijalankan?
tigapuluh tiga
@thirtythreeforty Eksperimen cepat di Ubuntu menunjukkan bahwa arloji akan menunggu dua detik penuh tidak peduli berapa lama perintah ini dijalankan. FWIW, periode tidur dapat ditentukan dengan '-n', hingga minimum 0,1 detik.
Jonathan Hartley
5

Watchdog adalah proyek Python, dan mungkin hanya apa yang Anda cari:

Platform yang didukung

  • Linux 2.6 (tidak resmi)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD / BSD (kqueue)
  • Windows (ReadDirectoryChangesW dengan port penyelesaian I / O; thread pekerja ReadDirectoryChangesW)
  • OS-independent (polling disk untuk snapshot direktori dan membandingkannya secara berkala; lambat dan tidak disarankan)

Baru saja menulis pembungkus baris perintah untuk itu watchdog_exec:

Contoh berjalan

Pada acara fs yang melibatkan file dan folder dalam direktori saat ini, jalankan echo $src $dstperintah, kecuali jika acara fs diubah, kemudian jalankan python $srcperintah.

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

Menggunakan argumen pendek, dan membatasi untuk hanya mengeksekusi ketika acara melibatkan " main .py":

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

EDIT: Baru saja ditemukan Watchdog memiliki CLI resmi yang dipanggil watchmedo, jadi periksa juga.

Samuel Marks
sumber
4

Jika program Anda menghasilkan semacam log / output, Anda dapat membuat Makefile dengan aturan untuk log / output yang tergantung pada skrip Anda dan melakukan sesuatu seperti

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

Sebagai alternatif, Anda dapat membuat target palsu dan memiliki aturan untuk itu baik memanggil skrip Anda dan menyentuh target palsu (sementara masih tergantung pada skrip Anda).

ctgPi
sumber
11
while sleep 1 ; do something ; donesedikit lebih baik daripada while true ; do something ; sleep 1 ; done. Setidaknya itu berhenti dengan mudah ketika menekan Ctrl + C.
Denilson Sá Maia
Akankah menghilangkan tidur menyebabkan loop sibuk (CPU menghasilkan panas dan merusak masa pakai baterai pada laptop)?
Steven Lu
2
@ SevenLu: tidak, tidur bukan menunggu sibuk. Masalahnya adalah bahwa jika tidur di dalam tubuh, Control-C akan membunuh tidur dan loop akan memulai kembali. Penggunaan daya untuk memulai perulangan tidak signifikan. Coba sendiri di terminal. Anda harus memegang Control-C agar bisa berfungsi, jika Anda tidur di tubuh.
Janus Troelsen
Baik. Saya pikir saya melewatkannya dan tidak melihat bahwa tidur masih ada sebagai kondisi loop. Tweak kecil itu cukup mengagumkan.
Steven Lu
4

swarminglogic menulis sebuah skrip yang disebut watchfile.sh , juga tersedia sebagai GitHub Gist .

Denilson Sa Maia
sumber
2
Ini adalah skrip Bash 200 baris penuh fitur yang melakukan polling statpada nama file yang diberikan, berjalan md5sumpada output, dan menjalankan kembali perintah yang diberikan jika nilai ini berubah. Karena itu Bash, saya curiga ini melakukan pekerjaan yang baik dengan menjalankan perintah yang diberikan persis seperti jika Anda mengetiknya di Bash prompt. (Sebaliknya, sebagian besar solusi di sini yang ditulis dalam bahasa lain akan gagal menjalankan perintah yang, misalnya, berisi alias shell seperti ll)
Jonathan Hartley
4

Memperbaiki jawaban Gilles .

Versi ini berjalan inotifywaitsekali dan memonitor acara (.eg:) modifysesudahnya. Tersebut yang inotifywait tidak perlu dieksekusi kembali pada setiap peristiwa yang ditemui.

Cepat dan cepat! (bahkan ketika memonitor direktori besar secara rekursif)

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
cychoi
sumber
Ini adalah jawaban terbaik di halaman untuk pengguna yang hanya menggunakan Linux. Ganti hal-hal di dalam loop dengan 'jalankan $ @', dan pengguna dapat memanggil skrip ini lewat perintah mereka sendiri untuk dijalankan. Ia bahkan bekerja dengan perintah yang mengandung alias shell jika Anda sumbernya, menggunakan sesuatu seperti ". Scriptname COMMAND". Ini masih akan menemukan scriptname di PATH.
Jonathan Hartley
Saya pikir Anda bermaksud menempatkan 'sambil membaca REPLY'?
Jonathan Hartley
1
terimakasih atas klarifikasinya. Unthanks untuk pentahapan itu! Saya akan menghapus komentar itu, tapi tentu saja sekarang tidak.
Jonathan Hartley
3

Sedikit lagi di sisi pemrograman, tetapi Anda ingin sesuatu seperti tidak masuk akal . Ada banyak implementasi dalam banyak bahasa, seperti jnotify dan pyinotify .

Pustaka ini memungkinkan Anda untuk memantau file tunggal atau seluruh direktori, dan mengembalikan peristiwa ketika suatu tindakan ditemukan. Informasi yang dikembalikan mencakup nama file, tindakan (buat, ubah, ganti nama, hapus) dan jalur file, di antara informasi bermanfaat lainnya.

John T
sumber
3

Bagi Anda yang mencari solusi FreeBSD, berikut adalah portnya:

/usr/ports/sysutils/wait_on
akond
sumber
3

Saya suka kesederhanaan while inotifywait ...; do ...; donenamun memiliki dua masalah:

  • Perubahan file yang terjadi selama do ...;akan terlewatkan
  • Lambat saat digunakan dalam mode rekursif

Karenanya saya membuat skrip pembantu yang menggunakan inotifywait tanpa batasan-batasan itu: inotifyexec

Saya sarankan Anda meletakkan skrip ini di jalur Anda, seperti di ~/bin/. Penggunaan dijelaskan dengan hanya menjalankan perintah.

Contoh: inotifyexec "echo test" -r .

Wernight
sumber
Diperbarui skrip untuk mendukung pencocokan pola regex.
Wernight
Kedua masalah diselesaikan dengan menggunakan inotifywait dalam mode "--monitor". Lihat jawaban cychoi.
Jonathan Hartley
3

Solusi Sebastian yang ditingkatkan dengan watchperintah:

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

Contoh panggilan:

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

Berhasil tetapi hati-hati: watchperintah memiliki bug yang diketahui (lihat man): ia bereaksi terhadap perubahan hanya di VISIBLE di bagian terminal -g CMDoutput.

alex_1948511
sumber
2

Anda dapat mencoba refleks .

Refleks adalah alat kecil untuk menonton direktori dan menjalankan kembali perintah ketika file tertentu berubah. Ini bagus untuk menjalankan tugas kompilasi / serat / pengujian secara otomatis dan untuk memuat ulang aplikasi Anda ketika kode berubah.

# Rerun make whenever a .c file changes
reflex -r '\.c$' make
masterxilo
sumber
Bisakah Anda mengutip / menjelaskan sedikit tentang alat ini? Baca cepat tentang cara merekomendasikan perangkat lunak untuk panduan.
bertieb
1

Jawaban oneliner yang saya gunakan untuk melacak perubahan file:

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

Anda tidak perlu menginisialisasi BF jika Anda tahu bahwa kencan pertama adalah waktu mulai.

Ini sederhana dan portabel. Ada jawaban lain berdasarkan strategi yang sama menggunakan skrip di sini. Lihatlah juga.


Penggunaan: Saya menggunakan ini untuk men-debug dan mengawasi ~/.kde/share/config/plasma-desktop-appletsrc; bahwa untuk beberapa alasan yang tidak diketahui terus kehilangan sayaSwitchTabsOnHover=false

Dr Beco
sumber
1

Saya menggunakan skrip ini untuk melakukannya. Saya menggunakan inotify dalam mode monitor

#!/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

Simpan ini sebagai runatwrite.sh

Usage: runatwrite.sh myfile.sh

itu akan menjalankan myfile.sh di setiap tulisan.

Fernando Silva
sumber
1

Bagi mereka yang menggunakan OS X, Anda dapat menggunakan LaunchAgent untuk menonton path / file untuk perubahan dan melakukan sesuatu ketika itu terjadi. FYI - LaunchControl adalah aplikasi bagus untuk membuat / memodifikasi / menghapus daemon / agen dengan mudah.

( contoh diambil dari sini )

<?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>
Hefewe1zen
sumber
0

Untuk orang-orang yang menemukan ini dengan Googling untuk perubahan pada file tertentu , jawabannya jauh lebih sederhana (terinspirasi oleh jawaban Gilles ).

Jika Anda ingin melakukan sesuatu setelah file tertentu ditulis, berikut caranya:

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

Simpan ini sebagai, misalnya, copy_myfile.shdan masukkan .shfile ke /etc/init.d/folder untuk menjalankannya saat startup.

LondonRob
sumber
Bagikan masalah dengan jawaban Giles yang dijalankannya tidak menunggu di setiap iterasi, yang mungkin tidak responsif untuk menonton direktori yang sangat besar secara rekursif. Lihat jawaban cychoi untuk perbaikannya.
Jonathan Hartley
0

Alat 'fido' mungkin merupakan pilihan lain untuk kebutuhan ini. Lihat https://www.joedog.org/fido-home/

David Ramirez
sumber
Silakan baca Bagaimana saya merekomendasikan perangkat lunak untuk beberapa tips tentang bagaimana Anda harus merekomendasikan perangkat lunak. Anda harus memberikan setidaknya tautan, beberapa informasi tambahan tentang perangkat lunak itu sendiri, dan bagaimana hal itu dapat digunakan untuk menyelesaikan masalah dalam pertanyaan.
DavidPostill
0

Seperti yang telah dilakukan beberapa orang lainnya, saya juga telah menulis alat baris perintah yang ringan untuk melakukan ini. Ini sepenuhnya didokumentasikan, diuji, dan modular.

Watch-Do

Instalasi

Anda dapat menginstalnya (jika Anda memiliki Python3 dan pip) menggunakan:

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

Pemakaian

Gunakan langsung dengan menjalankan:

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

Ikhtisar Fitur

  • Mendukung penggumpalan file (gunakan -w '*.py'atau-w '**/*.py' )
  • Jalankan beberapa perintah pada perubahan file (cukup tentukan -d bendera lagi)
  • Secara dinamis mengelola daftar file yang harus diperhatikan jika globbing digunakan (-r untuk mengaktifkannya)
  • Berbagai cara untuk "menonton" file:
    • Waktu modifikasi (standar)
    • Hash file
    • Sepele untuk mengimplementasikan Anda sendiri (ini adalah pengamat ModificationTime )
  • Desain modular. Jika Anda ingin menjalankan perintah, ketika file diakses, sepele untuk menulis pengamat Anda sendiri (mekanisme yang menentukan apakah pelakunya harus dijalankan).
vimist
sumber