วิธีรันคำสั่งเมื่อใดก็ตามที่ไฟล์มีการเปลี่ยนแปลง


434

ฉันต้องการวิธีที่ง่ายและรวดเร็วในการรันคำสั่งเมื่อใดก็ตามที่ไฟล์มีการเปลี่ยนแปลง ฉันต้องการบางอย่างที่เรียบง่ายสิ่งที่ฉันจะปล่อยให้ทำงานบนเทอร์มินัลและปิดเมื่อใดก็ตามที่ฉันทำงานกับไฟล์นั้นเสร็จ

ขณะนี้ฉันใช้สิ่งนี้:

while read; do ./myfile.py ; done

จากนั้นฉันต้องไปที่เทอร์มินัลนั้นแล้วกดEnterเมื่อใดก็ตามที่ฉันบันทึกไฟล์นั้นไว้ในเครื่องมือแก้ไข สิ่งที่ฉันต้องการคือสิ่งนี้:

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

หรือวิธีแก้ปัญหาอื่น ๆ ได้ง่ายอย่างที่คิด

BTW: ฉันใช้ Vim และฉันรู้ว่าฉันสามารถเพิ่ม autocommand เพื่อใช้งานบางอย่างใน BufWrite แต่นี่ไม่ใช่วิธีการแก้ปัญหาที่ฉันต้องการในตอนนี้

อัปเดต:ฉันต้องการอะไรที่เรียบง่ายละทิ้งได้ถ้าเป็นไปได้ ยิ่งไปกว่านั้นฉันต้องการให้บางสิ่งบางอย่างทำงานใน terminal เพราะฉันต้องการที่จะเห็นผลลัพธ์ของโปรแกรม (ฉันต้องการที่จะเห็นข้อความผิดพลาด)

เกี่ยวกับคำตอบ:ขอบคุณสำหรับคำตอบทั้งหมดของคุณ! พวกเขาทั้งหมดดีมากและแต่ละคนก็ใช้วิธีที่แตกต่างจากคนอื่นมาก เนื่องจากฉันต้องยอมรับเพียงคนเดียวฉันจึงยอมรับสิ่งที่ฉันใช้จริง ๆ (มันง่ายรวดเร็วและง่ายต่อการจดจำ) แม้ว่าฉันจะรู้ว่ามันไม่ได้สง่างามที่สุด


ไซต์ข้ามที่เป็นไปได้ซ้ำซ้อนของ: stackoverflow.com/questions/2972765/ … (แม้ว่าที่นี่จะอยู่ในหัวข้อ =))
Ciro Santilli 新疆改造中心中心法轮功六四事件

ฉันได้อ้างอิงก่อนที่จะข้ามไซต์ซ้ำและถูกปฏิเสธ: S;)
Francisco Tapia

4
โซลูชันของ Jonathan Hartley สร้างโซลูชันอื่น ๆ ที่นี่และแก้ไขปัญหาใหญ่ที่คำตอบที่ได้รับคะแนนสูงสุด: ขาดการแก้ไขและไม่มีประสิทธิภาพ โปรดเปลี่ยนคำตอบที่ยอมรับให้กับเขาซึ่งยังคงอยู่ใน github ที่github.com/tartley/rerun2 (หรือเพื่อแก้ปัญหาอื่น ๆ โดยไม่มีข้อบกพร่องเหล่านั้น)
nealmcb

คำตอบ:


404

ง่ายใช้inotifywait (ติดตั้งinotify-toolsแพ็กเกจการกระจายของคุณ):

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

หรือ

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

ตัวอย่างแรกนั้นง่ายกว่า แต่มีข้อเสียที่สำคัญคือมันจะพลาดการเปลี่ยนแปลงที่ทำในขณะที่inotifywaitไม่ได้ทำงาน (โดยเฉพาะในขณะที่myfileกำลังทำงาน) ตัวอย่างที่สองไม่มีข้อบกพร่องนี้ อย่างไรก็ตามโปรดระวังว่าชื่อไฟล์ไม่มีช่องว่าง หากเป็นปัญหาให้ใช้--formatตัวเลือกเพื่อเปลี่ยนผลลัพธ์ที่จะไม่รวมชื่อไฟล์:

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

ทั้งสองวิธีมีข้อ จำกัด : หากโปรแกรมบางแทนที่myfile.pyด้วยไฟล์ที่แตกต่างกันมากกว่าการเขียนที่มีอยู่myfile, inotifywaitจะตาย บรรณาธิการหลายคนทำงานอย่างนั้น

เพื่อเอาชนะข้อ จำกัด นี้ให้ใช้inotifywaitในไดเรกทอรี:

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

หรือใช้เครื่องมืออื่นที่ใช้ฟังก์ชั่นพื้นฐานเดียวกันเช่นincron (ช่วยให้คุณลงทะเบียนเหตุการณ์เมื่อไฟล์ถูกแก้ไข) หรือfswatch (เครื่องมือที่ใช้งานได้กับ Unix รุ่นอื่น ๆ โดยใช้อะนาล็อกของลินุกซ์


46
ฉันได้สรุปทั้งหมดนี้ (มีเคล็ดลับทุบตีค่อนข้างน้อย) ในsleep_until_modified.shสคริปต์ที่ใช้งานง่ายมีให้ที่: bitbucket.org/denilsonsa/small_scripts/src
Denilson Sá Maia

14
while sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; doneมันวิเศษมาก ขอขอบคุณ.
Rhys Ulerich

5
inotifywait -e delete_selfดูเหมือนว่าจะทำงานได้ดีสำหรับฉัน
Kos

3
มันง่าย แต่มีสองประเด็นสำคัญ: เหตุการณ์อาจพลาดได้ (เหตุการณ์ทั้งหมดในลูป) และการเริ่มต้นของ inotifywait จะทำในแต่ละครั้งซึ่งทำให้การแก้ปัญหานี้ช้าลงสำหรับโฟลเดอร์ recursive ขนาดใหญ่
Wernight

6
ด้วยเหตุผลบางอย่างwhile inotifywait -e close_write myfile.py; do ./myfile.py; doneจะออกโดยไม่เรียกใช้คำสั่ง (bash และ zsh) สำหรับการทำงานฉันต้องเพิ่ม|| trueเช่น: while inotifywait -e close_write myfile.py || true; do ./myfile.py; done
ideasman42

166

entr ( http://entrproject.org/ ) มอบอินเทอร์เฟซที่ใช้งานง่ายขึ้นเพื่อ inotify (และรองรับ * BSD & Mac OS X)

มันทำให้ง่ายมากในการระบุหลาย ๆ ไฟล์ที่จะดู (ถูก จำกัด โดยulimit -n), ไม่ต้องวุ่นวายกับการจัดการกับไฟล์ที่ถูกแทนที่และต้องใช้ไวยากรณ์ bash น้อยลง:

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

ฉันใช้มันในทรีโปรเจกต์ทั้งหมดของฉันเพื่อเรียกใช้การทดสอบหน่วยสำหรับโค้ดที่ฉันกำลังแก้ไขอยู่ในขณะนี้

ตั้งค่าสถานะเช่น-c(ล้างหน้าจอระหว่างการรัน) และ-d(ออกเมื่อมีการเพิ่มไฟล์ใหม่ในไดเรกทอรีที่ถูกตรวจสอบ) เพิ่มความยืดหยุ่นมากขึ้นตัวอย่างเช่นคุณสามารถทำได้:

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

ตั้งแต่ต้นปี 2561 ยังอยู่ในช่วงพัฒนาและสามารถพบได้ใน Debian & Ubuntu ( apt install entr); การสร้างจาก repo ของผู้เขียนนั้นปราศจากความเจ็บปวดในทุกกรณี


3
ไม่จัดการไฟล์ใหม่และการแก้ไข
Wernight

2
@Wernight - ณ วันที่ 7 พฤษภาคม 2014 entr มี-dธงใหม่ มันยืดยาวขึ้นเล็กน้อย แต่คุณสามารถwhile sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; doneจัดการกับไฟล์ใหม่ได้
Paul Fenney

1
entr ยังมีอยู่ใน debos repos อย่างน้อยจาก debian jessie / 8.2 บน ...
Peter V. Mørch

5
ดีที่สุดที่ฉันพบใน OS X แน่นอน fswatch คว้ากิจกรรมขี้ขลาดเกินไปและฉันไม่ต้องการใช้เวลาในการคิดออกว่าทำไม
dtc

5
มันเป็นที่น่าสังเกตว่าEntrสามารถใช้ได้บน Homebrew จึงbrew install entrจะทำงานได้ตามที่คาดไว้
jmarceli

108

ผมเขียนโปรแกรมหลามที่จะทำตรงนี้เรียกว่าเมื่อ-เปลี่ยนแปลง

การใช้งานง่าย:

when-changed FILE COMMAND...

หรือดูหลายไฟล์:

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

FILEสามารถเป็นไดเรกทอรี -rชมซ้ำกับ ใช้%fเพื่อส่งชื่อไฟล์ไปยังคำสั่ง


1
@ysangkok ใช่มันไม่ในรุ่นล่าสุดของรหัส :)
โจ้

4
พร้อมใช้งานแล้วจาก "การติดตั้ง pip เมื่อมีการเปลี่ยนแปลง" ยังคงใช้งานได้ดี ขอบคุณ
อัลฟลานาแกน

2
when-changed FILE 'clear; COMMAND'เพื่อล้างหน้าจอแรกที่คุณสามารถใช้
เดฟเจมส์มิลเลอร์

1
คำตอบนี้ดีขึ้นมากเพราะฉันสามารถทำได้บน Windows เช่นกัน และผู้ชายคนนี้ก็เขียนโปรแกรมเพื่อรับคำตอบ
Wolfpack'08

4
ข่าวดีสำหรับทุกคน! when-changedตอนนี้ข้ามแพลตฟอร์ม! ลองดู0.3.0 รีลีสล่าสุด:)
joh

52

สคริปต์นี้เป็นอย่างไร? มันใช้statคำสั่งที่จะได้รับเวลาในการเข้าถึงของไฟล์และเรียกใช้คำสั่งเมื่อใดก็ตามที่มีการเปลี่ยนแปลงในเวลาในการเข้าถึง (เมื่อใดก็ตามที่มีการเข้าถึงไฟล์)

#!/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
การไม่statใช้เวลาที่แก้ไขแล้วจะตอบได้ดีกว่า "เมื่อใดก็ตามที่ไฟล์เปลี่ยนแปลง"
Xen2050

1
การรันสถิติหลายครั้งต่อวินาทีทำให้หลายคนอ่านข้อมูลบนดิสก์หรือไม่ หรือการเรียกระบบ fstat จะทำให้แคชตอบสนองเหล่านี้โดยอัตโนมัติหรือไม่? ฉันกำลังพยายามเขียน 'grunt watch' เพื่อรวบรวมรหัส c ของฉันเมื่อใดก็ตามที่ฉันทำการเปลี่ยนแปลง
Oskenso Kashi

นี่เป็นสิ่งที่ดีถ้าคุณรู้ว่าต้องมีการดูชื่อไฟล์ล่วงหน้า ดีกว่าที่จะส่งชื่อไฟล์ไปยังสคริปต์ ยังดีกว่าถ้าคุณสามารถผ่านชื่อไฟล์จำนวนมาก (เช่น "mywatch * .py") ยังคงดีกว่าถ้ามันสามารถใช้งานซ้ำในไฟล์ในส่วนย่อยได้เช่นกันซึ่งโซลูชันอื่น ๆ ทำ
Jonathan Hartley

5
ในกรณีที่ทุกคนสงสัยเกี่ยวกับการอ่านจำนวนมากฉันได้ทดสอบสคริปต์นี้ใน Ubuntu 17.04 ด้วยการนอนหลับที่ 0.05s และ vmstat -dดูการเข้าถึงดิสก์ ดูเหมือนว่า linux จะทำงานที่ยอดเยี่ยมในการแคชสิ่งนี้: D
Oskenso Kashi

มีการพิมพ์ผิดใน "คำสั่ง" ฉันพยายามที่จะแก้ไข แต่ดังนั้นพูดว่า "แก้ไขไม่ควรน้อยกว่า 6 ตัวอักษร"
user337085

30

โซลูชันที่ใช้ Vim:

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

แต่ฉันไม่ต้องการวิธีแก้ปัญหานี้เพราะมันน่ารำคาญที่จะพิมพ์มันยากที่จะจำได้ว่าจะพิมพ์อะไรและมันก็ยากที่จะยกเลิกผลกระทบของมัน (ต้องวิ่ง:au! BufWritePost myfile.py) นอกจากนี้โซลูชันนี้จะบล็อก Vim จนกว่าคำสั่งจะดำเนินการเสร็จสิ้น

ฉันได้เพิ่มโซลูชันนี้ที่นี่เพื่อความสมบูรณ์เนื่องจากอาจช่วยผู้อื่น

ในการแสดงเอาต์พุตของโปรแกรม (และขัดจังหวะขั้นตอนการแก้ไขอย่างสมบูรณ์เนื่องจากเอาต์พุตจะเขียนทับโปรแกรมแก้ไขของคุณเป็นเวลาสองสามวินาทีจนกระทั่งคุณกด Enter) ให้ลบ:silentคำสั่ง


1
สิ่งนี้จะค่อนข้างดีเมื่อรวมกับentr(ดูด้านล่าง) - เพียงแค่ทำให้เป็นกลุ่มสัมผัสไฟล์จำลองที่ entr กำลังรับชมและปล่อยให้ entr ทำส่วนที่เหลือในพื้นหลัง ... หรือtmux send-keysถ้าคุณเกิดขึ้นในสภาพแวดล้อมดังกล่าว :)
Paul Fenney

ดี! คุณสามารถสร้างมาโครสำหรับ.vimrcไฟล์ของคุณได้
ErichBSchulz

23

หากคุณnpmติดตั้งแล้วnodemonอาจเป็นวิธีที่ง่ายที่สุดในการเริ่มต้นโดยเฉพาะอย่างยิ่งใน OS X ซึ่งเห็นได้ชัดว่าไม่ได้มีเครื่องมือที่ไม่แจ้งข้อมูล สนับสนุนการเรียกใช้คำสั่งเมื่อโฟลเดอร์เปลี่ยนแปลง


5
อย่างไรก็ตามจะดูได้เฉพาะไฟล์. js และ. coffee
zelk

6
รุ่นปัจจุบันดูเหมือนว่าจะสนับสนุนคำสั่งใด ๆ ตัวอย่างเช่น: nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/models
kek

1
ฉันหวังว่าฉันจะมีข้อมูลเพิ่มเติม แต่ osx มีวิธีการติดตามการเปลี่ยนแปลง fsevents
ConstantineK

1
ใน OS X คุณสามารถใช้Launch Daemonsด้วยWatchPathsปุ่มตามที่แสดงในลิงค์ของฉัน
Adam Johns

19

สำหรับผู้ที่ไม่สามารถติดตั้งinotify-toolsเช่นฉันนี้ควรมีประโยชน์:

watch -d -t -g ls -lR

คำสั่งนี้จะออกเมื่อมีการเปลี่ยนแปลงผลลัพธ์ls -lRจะแสดงรายการทุกไฟล์และไดเรกทอรีที่มีขนาดและวันที่ดังนั้นหากไฟล์มีการเปลี่ยนแปลงก็ควรออกจากคำสั่งตามที่มนุษย์บอกว่า:

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

ฉันรู้ว่าคำตอบนี้อาจไม่สามารถอ่านได้โดยทุกคน แต่ฉันหวังว่าจะมีใครบางคนเข้าถึงได้

ตัวอย่างบรรทัดคำสั่ง:

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

เปิดเทอร์มินัลอื่น:

~ $ echo "testing" > /tmp/test

ตอนนี้เทอร์มินัลแรกจะส่งออก 1,2,3

ตัวอย่างสคริปต์ง่าย ๆ :

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

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

5
แฮ็คที่ดี ฉันทดสอบและดูเหมือนว่าจะมีปัญหาเมื่อรายชื่อยาวและไฟล์ที่ถูกเปลี่ยนอยู่นอกหน้าจอ ปรับเปลี่ยนขนาดเล็กอาจจะมีบางอย่างเช่นนี้ watch -d -t -g "ls -lR tmp | sha1sum"
Atle

3
หากคุณดูวิธีแก้ปัญหาของคุณทุก ๆ วินาทีมันจะทำงานได้ตลอดไปและรัน MY_COMMAND เฉพาะเมื่อมีการเปลี่ยนแปลงไฟล์: watch -n1 "watch -d -t -g ls -lR && MY_COMMAND"
mnesarco

รุ่นนาฬิกาของฉัน (บน Linux watch from procps-ng 3.3.10) ยอมรับช่วงเวลาลอยตัวตามช่วงเวลาดังนั้นwatch -n0.2 ...จะทำการสำรวจทุก ๆ ห้าวินาที เหมาะสำหรับการทดสอบหน่วยย่อยมิลลิวินาทีที่มีสุขภาพดี
Jonathan Hartley

15

rerun2( บน GitHub ) เป็น Bash script 10 บรรทัดของแบบฟอร์ม:

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

บันทึกเวอร์ชัน github เป็น 'เรียกใช้อีกครั้ง' บน PATH ของคุณและเรียกใช้โดยใช้:

rerun COMMAND

มันรันคำสั่งทุกครั้งที่มีระบบไฟล์แก้ไขเหตุการณ์ในไดเรกทอรีปัจจุบันของคุณ (เรียกซ้ำ)

สิ่งหนึ่งที่อาจชอบเกี่ยวกับมัน:

  • มันใช้ inotify จึงตอบสนองได้ดีกว่าการเลือกตั้ง ยอดเยี่ยมสำหรับการเรียกใช้การทดสอบหน่วยย่อยเป็นมิลลิวินาทีหรือการแสดงผลไฟล์กราฟวิซดอททุกครั้งที่คุณกด 'บันทึก'
  • เนื่องจากมันเร็วมากคุณจึงไม่ต้องบอกให้ละเว้นกลุ่มย่อยขนาดใหญ่ (เช่น node_modules) เพื่อเหตุผลด้านประสิทธิภาพเท่านั้น
  • มันตอบสนองได้อย่างยอดเยี่ยมเป็นพิเศษเพราะมันจะโทรออกโดยไม่แจ้งให้ทราบเพียงครั้งเดียวเมื่อเริ่มต้นแทนที่จะเรียกใช้และทำให้นาฬิกาที่มีราคาแพงเกิดขึ้นทุกครั้ง
  • มันมีแค่ 12 บรรทัดของ Bash
  • เพราะมันเป็นทุบตีมันตีความคำสั่งที่คุณผ่านมันเหมือนกับว่าคุณได้พิมพ์พวกเขาที่พรอมต์ทุบตี (สมมุติว่านี่เป็นสิ่งที่น้อยกว่าถ้าคุณใช้กระสุนอื่น)
  • มันจะไม่สูญเสียเหตุการณ์ที่เกิดขึ้นในขณะที่คำสั่งกำลังดำเนินการซึ่งแตกต่างจากส่วนใหญ่แก้ปัญหา inotify อื่น ๆ ในหน้านี้
  • ในเหตุการณ์แรกมันเข้าสู่ 'ช่วงเวลาที่ตายแล้ว' เป็นเวลา 0.15 วินาทีในระหว่างที่เหตุการณ์อื่น ๆ ถูกละเว้นก่อนที่คำสั่งจะทำงานหนึ่งครั้ง นี่คือเหตุการณ์ที่เกิดขึ้นจากการเต้นรำแบบสร้าง - เขียน - ย้ายซึ่ง Vi หรือ Emacs ทำเมื่อบันทึกบัฟเฟอร์ไม่ก่อให้เกิดการประหารชีวิตหลายครั้งของชุดทดสอบที่ใช้งานช้า เหตุการณ์ใด ๆ ที่เกิดขึ้นในขณะที่ COMMAND กำลังดำเนินการอยู่จะไม่ถูกละเว้น - มันจะทำให้เกิดช่วงเวลาที่สองที่ตายแล้วและการดำเนินการที่ตามมา

สิ่งหนึ่งที่ไม่ชอบเกี่ยวกับมัน:

  • มันใช้ inotify ดังนั้นจะไม่ทำงานนอก Linuxland
  • เพราะมันใช้ inotify มันจะ barf ในการพยายามดูไดเรกทอรีที่มีไฟล์มากกว่าจำนวนสูงสุดของผู้ใช้ inotify watch ตามค่าเริ่มต้นดูเหมือนว่าจะถูกตั้งค่าไว้ที่ประมาณ 5,000 ถึง 8,000 ในเครื่องที่แตกต่างกันที่ฉันใช้ แต่เพิ่มได้ง่าย ดูhttps://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • มันล้มเหลวในการดำเนินการคำสั่งที่มีนามแฝง Bash ฉันสาบานได้ว่าสิ่งนี้เคยทำงาน โดยหลักการแล้วนี่คือ Bash ไม่ใช่การเรียกใช้คำสั่งใน subshell ฉันจึงคาดหวังว่ามันจะทำงานได้ ฉันชอบที่จะได้ยินถ้าใครรู้ว่าทำไมมันไม่ โซลูชันอื่น ๆ จำนวนมากในหน้านี้ไม่สามารถดำเนินการคำสั่งดังกล่าวได้
  • โดยส่วนตัวฉันหวังว่าฉันจะสามารถกดคีย์ในเทอร์มินัลที่กำลังทำงานอยู่เพื่อให้เกิดการเรียกใช้คำสั่งพิเศษด้วยตนเอง ฉันจะเพิ่มสิ่งนี้ได้ไหม? การเรียกใช้ 'พร้อมกันในขณะที่อ่าน -n1' ซึ่งเรียกการเรียกใช้
  • ตอนนี้ฉันได้เขียนโค้ดเพื่อล้างข้อมูลเทอร์มินัลและพิมพ์คำสั่งที่เรียกใช้งานบนการทำซ้ำแต่ละครั้ง บางคนอาจต้องการเพิ่มการตั้งค่าสถานะบรรทัดคำสั่งเพื่อปิดสิ่งนี้เป็นต้น แต่สิ่งนี้จะเพิ่มขนาดและความซับซ้อนได้หลายเท่า

นี่คือการปรับแต่ง Anwer ของ @ cychoi


2
ฉันเชื่อว่าคุณควรใช้"$@"แทน$@เพื่อให้สามารถทำงานกับอาร์กิวเมนต์ที่มีช่องว่างได้อย่างเหมาะสม แต่ในเวลาเดียวกันคุณใช้evalซึ่งบังคับให้ผู้ใช้รันใหม่จะต้องระมัดระวังเป็นพิเศษเมื่อ quoting
Denilson Sá Maia

ขอบคุณเดนิลสัน คุณได้รับตัวอย่างของการอ้างอิงที่ต้องทำอย่างระมัดระวัง? ผมเคยใช้มัน 24 ชั่วโมงที่ผ่านและไม่ได้เห็นปัญหาใด ๆ กับพื้นที่ป่านนี้มิได้ระมัดระวังอ้างอะไร - rerun 'command'เพียงแค่เรียกว่าเป็น คุณแค่บอกว่าถ้าฉันใช้ "$ @" ผู้ใช้สามารถเรียกใช้เป็นrerun command(โดยไม่ใส่เครื่องหมายอัญประกาศ?) ซึ่งดูเหมือนจะไม่เป็นประโยชน์สำหรับฉัน: โดยทั่วไปฉันไม่ต้องการให้ Bash ทำการประมวลผลคำสั่งใด ๆก่อนส่ง เพื่อวิ่งใหม่ เช่นถ้าคำสั่งมี "echo $ myvar" ดังนั้นฉันจะต้องการเห็นค่าใหม่ของ myvar ในการทำซ้ำแต่ละครั้ง
Jonathan Hartley

1
สิ่งที่ชอบrerun foo "Some File"อาจแตก แต่เนื่องจากคุณกำลังใช้ก็สามารถนำมาเขียนใหม่เป็นeval rerun 'foo "Some File"โปรดทราบว่าบางครั้งการขยายเส้นทางอาจทำให้เกิดช่องว่าง: rerun touch *.fooอาจมีการแตกและการใช้rerun 'touch *.foo'มีความหมายแตกต่างกันเล็กน้อย (การขยายเส้นทางเกิดขึ้นเพียงครั้งเดียวหรือหลายครั้ง)
Denilson Sá Maia

ขอบคุณสำหรับความช่วยเหลือ ใช่: rerun ls "some file"พักเพราะช่องว่าง rerun touch *.foo*ทำงานได้ตามปกติ แต่จะล้มเหลวหากชื่อไฟล์ที่ตรงกับ * .foo มีช่องว่าง ขอขอบคุณที่ช่วยฉันดูว่าrerun 'touch *.foo'มีความหมายแตกต่างกัน แต่ฉันสงสัยว่ารุ่นที่มีราคาเดียวคือความหมายฉันต้องการ: ผมอยากย้ำวิ่งแต่ละคนที่จะทำหน้าที่เป็นถ้าฉันพิมพ์คำสั่งอีกครั้ง - ด้วยเหตุนี้ผมต้องการที่ *.fooจะขยายในแต่ละซ้ำ . ผมจะพยายามเสนอแนะของคุณเพื่อตรวจสอบผลกระทบของพวกเขา ...
โจนาธานฮาร์ทลี่

การอภิปรายเพิ่มเติมเกี่ยวกับ PR นี้ ( github.com/tartley/rerun2/pull/1 ) และอื่น ๆ
Jonathan Hartley

12

นี่คือเชลล์สคริปต์ Bourne shell แบบง่าย ๆ ที่:

  1. รับอาร์กิวเมนต์สองตัว: ไฟล์ที่จะถูกตรวจสอบและคำสั่ง (พร้อมอาร์กิวเมนต์หากจำเป็น)
  2. คัดลอกไฟล์ที่คุณกำลังตรวจสอบไปยังไดเร็กทอรี / tmp
  3. ตรวจสอบทุกสองวินาทีเพื่อดูว่าไฟล์ที่คุณกำลังตรวจสอบนั้นใหม่กว่าสำเนาหรือไม่
  4. หากเป็นรุ่นที่ใหม่กว่าก็จะเขียนทับสำเนาด้วยต้นฉบับใหม่และดำเนินการคำสั่ง
  5. ชำระล้างตัวเองเมื่อคุณกด 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  
    

สิ่งนี้ใช้ได้กับ FreeBSD ปัญหาการพกพาอย่างเดียวที่ฉันนึกได้คือถ้า Unix อื่น ๆ ไม่มีคำสั่ง mktemp (1) แต่ในกรณีนี้คุณสามารถเขียนโค้ดชั่วคราวที่ชื่อไฟล์ temp ได้


9
การสำรวจความคิดเห็นเป็นวิธีพกพาเพียงอย่างเดียว แต่ระบบส่วนใหญ่มีกลไกการแจ้งเตือนการเปลี่ยนแปลงไฟล์ (inotify บน Linux, kqueue บน FreeBSD, ... ) คุณมีปัญหา quoting รุนแรงเมื่อคุณทำ$cmdแต่โชคดีที่ได้อย่างง่ายดายแน่นอน: คลองตัวแปรและดำเนินการcmd "$@"สคริปต์ของคุณไม่เหมาะสำหรับการตรวจสอบไฟล์ขนาดใหญ่ แต่สามารถแก้ไขได้โดยแทนที่cpด้วยtouch -r(คุณต้องการเพียงวันที่ไม่ใช่เนื้อหา) พกพาสะดวกการ-ntทดสอบนั้นต้องใช้ bash, ksh หรือ zsh
Gilles

8

มีลักษณะที่incron มันคล้ายกับ cron แต่ใช้ inotify เหตุการณ์แทนเวลา


สิ่งนี้สามารถทำงานได้ แต่การสร้างรายการ incron ค่อนข้างเป็นกระบวนการที่ใช้แรงงานเข้มข้นเมื่อเทียบกับโซลูชันอื่น ๆ ในหน้านี้
Jonathan Hartley

6

โซลูชันอื่นที่มี NodeJs, fsmonitor :

  1. ติดตั้ง

    sudo npm install -g fsmonitor
    
  2. จากบรรทัดคำสั่ง (เช่นบันทึกการตรวจสอบและ "ค้าปลีก" หากมีการเปลี่ยนแปลงไฟล์บันทึก)

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

หมายเหตุด้านข้าง: tail -F -q *.logฉันคิดว่าตัวอย่างสามารถแก้ไขได้โดย
Volker Siegel

มันเป็นเพียงการยกตัวอย่างtail -fไม่ใช่clearเทอร์มินัล
Atika

6

ดูใน Guard โดยเฉพาะกับปลั๊กอินนี้:

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

คุณสามารถตั้งค่าเพื่อดูจำนวนรูปแบบใด ๆ ในไดเรกทอรีของโครงการและดำเนินการคำสั่งเมื่อมีการเปลี่ยนแปลงเกิดขึ้น โอกาสที่ดีแม้ว่าจะมีปลั๊กอินสำหรับสิ่งที่คุณพยายามทำตั้งแต่แรก


6

หากคุณติดตั้งnodemonคุณสามารถทำสิ่งนี้ได้:

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

ในกรณีของฉันฉันแก้ไข HTML ในเครื่องและส่งไปยังเซิร์ฟเวอร์ระยะไกลของฉันเมื่อมีการเปลี่ยนแปลงไฟล์

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

6

ภายใต้ Linux:

man watch

watch -n 2 your_command_to_run

จะเรียกใช้คำสั่งทุก 2 วินาที

หากคำสั่งของคุณใช้เวลานานกว่า 2 วินาทีในการรันนาฬิกาจะรอจนกว่าจะเสร็จก่อนที่จะทำอีกครั้ง


นั่นง่ายมากแม้ว่าจะเป็นของเสีย แต่ก็ง่ายสำหรับงานพัฒนาเช่นการเปลี่ยนแปลงรูปแบบสด
Xeoncross

2
จะเกิดอะไรขึ้นเมื่อคำสั่งใช้เวลานานกว่าสองวินาทีในการเรียกใช้
สามสิบสาม

@thirtythreeforty การทดสอบอย่างรวดเร็วบน Ubuntu แสดงให้เห็นว่านาฬิกาจะรอสองวินาทีเต็มไม่ว่าจะใช้คำสั่งนานแค่ไหนก็ตาม FWIW ระยะเวลาสลีปสามารถระบุด้วย '-n' ลงไปอย่างน้อย 0.1 วินาที
Jonathan Hartley

5

Watchdogเป็นโครงการ Python และอาจเป็นเพียงสิ่งที่คุณกำลังมองหา:

แพลตฟอร์มที่รองรับ

  • Linux 2.6 (inotify)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD / BSD (kqueue)
  • Windows (ReadDirectoryChangesW พร้อมพอร์ต I / O ที่สมบูรณ์; เธรดของผู้ปฏิบัติงาน ReadDirectoryChangesW)
  • ไม่ขึ้นกับระบบปฏิบัติการ (ทำการสำรวจดิสก์สำหรับสแน็ปช็อตไดเรกทอรีและทำการเปรียบเทียบเป็นระยะ ๆ ช้าและไม่แนะนำ)

เพิ่งเขียน wrapper บรรทัดคำสั่งสำหรับมันwatchdog_exec:

ตัวอย่างการวิ่ง

ในเหตุการณ์ fs ที่เกี่ยวข้องกับไฟล์และโฟลเดอร์ในไดเรกทอรีปัจจุบันให้เรียกใช้echo $src $dstคำสั่งเว้นแต่ว่าจะมีการแก้ไขเหตุการณ์ fs จากนั้นเรียกใช้python $srcคำสั่ง

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

การใช้อาร์กิวเมนต์สั้น ๆ และ จำกัด การทำงานเฉพาะเมื่อเหตุการณ์เกี่ยวข้องกับ " main .py":

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

แก้ไข: เพิ่งพบ Watchdog มี CLI อย่างเป็นทางการที่เรียกว่าwatchmedoดังนั้นตรวจสอบด้วย


4

หากโปรแกรมของคุณสร้างบันทึก / เอาต์พุตบางประเภทคุณสามารถสร้าง Makefile ด้วยกฎสำหรับบันทึก / ผลลัพธ์ที่ขึ้นอยู่กับสคริปต์ของคุณและทำบางสิ่งเช่น

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

อีกวิธีหนึ่งคุณสามารถสร้างเป้าหมายปลอมและให้กฎนั้นเรียกสคริปต์ของคุณและสัมผัสเป้าหมายปลอม (ในขณะที่ยังขึ้นอยู่กับสคริปต์ของคุณ)


11
while sleep 1 ; do something ; donewhile true ; do something ; sleep 1 ; doneเล็กน้อยดีกว่า อย่างน้อยก็หยุดได้อย่างง่ายดายเมื่อกด Ctrl + C
Denilson Sá Maia

การเอาสลีปออกจะทำให้เกิดการยุ่ง (CPU สร้างความร้อนและยืดอายุการใช้งานแบตเตอรี่บนแล็ปท็อป) หรือไม่
Steven Lu

2
@StevenLu: ไม่การนอนหลับไม่ได้ยุ่ง ปัญหาคือว่าถ้าการนอนหลับอยู่ในร่างกาย Control-C จะฆ่าการนอนหลับและลูปจะเริ่มต้นใหม่ การใช้พลังงานในการสตาร์ทลูปจะไม่มีนัยสำคัญ ลองด้วยตัวคุณเองใน terminal คุณต้องกด Control-C ค้างไว้เพื่อให้มันทำงานได้ถ้าคุณนอนในร่างกาย
Janus Troelsen

ขวา. ฉันคิดว่าฉันพลาดไปแล้วและไม่เห็นว่าการนอนหลับยังคงมีอยู่ตามเงื่อนไขของลูป การบิดเล็กน้อยนั้นยอดเยี่ยมมาก
Steven Lu

4

swarminglogicเขียนสคริปต์เรียก watchfile.shยังสามารถใช้ได้เป็นแก่น GitHub


2
นี่เป็นสคริปต์ Bash 200 บรรทัดที่เต็มไปด้วยคุณลักษณะที่โพลstatในชื่อไฟล์ที่กำหนดรัน md5sumบนเอาต์พุตและรันคำสั่งที่กำหนดอีกครั้งหากค่านี้เปลี่ยน เพราะมันเป็นทุบตีฉันจึงสงสัยว่ามันจะทำงานได้ดีในการรันคำสั่งที่กำหนดเหมือนกับว่าคุณพิมพ์ด้วยพรอมต์ Bash (ในทางตรงข้ามคำตอบส่วนใหญ่ที่เขียนในภาษาอื่นจะไม่สามารถใช้คำสั่งได้ตัวอย่างเช่นมีนามแฝงของเชลล์เช่นll)
Jonathan Hartley

4

ดีขึ้นเมื่อคำตอบของกิลส์

รุ่นนี้ทำงานinotifywaitหนึ่งครั้งและตรวจสอบกิจกรรม (.eg:) modifyหลังจากนั้น สิ่งinotifywait นี้ไม่จำเป็นต้องถูกเรียกใช้ซ้ำอีกเมื่อพบทุกเหตุการณ์

มันเร็วและเร็ว! (แม้เมื่อตรวจสอบไดเรกทอรีขนาดใหญ่ซ้ำ)

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

นี่เป็นคำตอบที่ดีที่สุดในหน้าสำหรับผู้ใช้ Linux เท่านั้น แทนที่สิ่งที่อยู่ในลูปด้วย 'execute $ @' และผู้ใช้สามารถเรียกสคริปต์นี้ผ่านในคำสั่งของตนเองเพื่อให้ทำงานได้ มันยังทำงานได้กับคำสั่งที่มีชื่อแทนเชลล์ถ้าคุณส่งมาโดยใช้ชื่อ ". scriptname COMMAND" จะยังคงพบชื่อสคริปต์ใน PATH
Jonathan Hartley

ฉันคิดว่าคุณหมายถึงใส่ 'ในขณะที่อ่านตอบ'?
Jonathan Hartley

1
ขอขอบคุณสำหรับการชี้แจง. ขอบคุณสำหรับการวางขั้นตอนของมัน! ฉันจะลบความคิดเห็นเหล่านั้น แต่แน่นอนตอนนี้ฉันจะไม่
Jonathan Hartley

3

เพิ่มอีกเล็กน้อยทางด้านการเขียนโปรแกรม แต่คุณต้องการอะไรที่ไม่ถูกต้อง มีการใช้งานในหลายภาษาเช่นjnotifyและpyinotify

ไลบรารีนี้ให้คุณตรวจสอบไฟล์เดียวหรือทั้งไดเรกทอรีและส่งคืนเหตุการณ์เมื่อพบการกระทำ ข้อมูลที่ส่งคืนประกอบด้วยชื่อไฟล์การดำเนินการ (สร้างแก้ไขเปลี่ยนชื่อลบ) และเส้นทางไฟล์รวมถึงข้อมูลที่เป็นประโยชน์อื่น ๆ


3

สำหรับบรรดาของคุณที่กำลังมองหาโซลูชัน FreeBSD นี่คือพอร์ต:

/usr/ports/sysutils/wait_on

3

ฉันชอบความเรียบง่ายwhile inotifywait ...; do ...; doneแต่มีสองประเด็น:

  • การเปลี่ยนแปลงไฟล์ที่เกิดขึ้นระหว่างที่do ...;จะพลาด
  • ช้าเมื่อใช้ในโหมดเรียกซ้ำ

ดังนั้นฉันจึงสร้างสคริปต์ผู้ช่วยที่ใช้inotifywaitโดยไม่มีข้อ จำกัด เหล่านั้น: inotifyexec

~/bin/ผมแนะนำให้คุณวางสคริปต์นี้ในเส้นทางของคุณเหมือนใน การใช้อธิบายโดยใช้คำสั่ง

ตัวอย่าง: inotifyexec "echo test" -r .


อัปเดตสคริปต์เพื่อรองรับการจับคู่รูปแบบ regex
Wernight

ปัญหาทั้งสองจะได้รับการแก้ไขโดยใช้ inotifywait ในโหมด "--monitor" ดูคำตอบของ cychoi
Jonathan Hartley

3

ปรับปรุงโซลูชันของ Sebastianด้วยwatchคำสั่ง:

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

ตัวอย่างการโทร:

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

มันใช้งานได้ แต่ต้องระวัง: watchคำสั่งรู้จักข้อบกพร่อง (ดู man): มันตอบสนองต่อการเปลี่ยนแปลงเฉพาะใน VISIBLE ในส่วนของเทอร์มินัลของ-g CMDเอาต์พุต


2

คุณอาจจะลองสะท้อน

Reflex เป็นเครื่องมือขนาดเล็กเพื่อดูไดเร็กทอรีและรันคำสั่งอีกครั้งเมื่อไฟล์บางไฟล์เปลี่ยนแปลง เหมาะอย่างยิ่งสำหรับการเรียกใช้งานคอมไพล์ / ผ้าสำลี / ทดสอบโดยอัตโนมัติและสำหรับการโหลดแอปพลิเคชันของคุณใหม่เมื่อรหัสมีการเปลี่ยนแปลง

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

คุณสามารถพูด / อธิบายเกี่ยวกับเครื่องมือได้บ้างหรือไม่? อ่านวิธีแนะนำซอฟต์แวร์สำหรับคำแนะนำได้อย่างรวดเร็ว
bertieb

1

คำตอบ oneliner ที่ฉันใช้เพื่อติดตามการเปลี่ยนแปลงไฟล์:

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

คุณไม่จำเป็นต้องเริ่มต้น BF ถ้าคุณรู้ว่าวันแรกคือเวลาเริ่มต้น

ง่ายและพกพาได้ มีคำตอบอื่นตามกลยุทธ์เดียวกันโดยใช้สคริปต์ที่นี่ ลองดูด้วย


การใช้งาน: ฉันใช้นี้เพื่อแก้ปัญหาและเก็บตาบน~/.kde/share/config/plasma-desktop-appletsrc; ด้วยเหตุผลบางอย่างที่ทำให้ฉันไม่รู้จักSwitchTabsOnHover=false


1

ฉันใช้สคริปต์นี้เพื่อทำ ฉันใช้ inotify ในโหมดมอนิเตอร์

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

บันทึกสิ่งนี้เป็น runatwrite.sh

Usage: runatwrite.sh myfile.sh

มันจะรัน myfile.sh ทุกครั้งที่เขียน


1

สำหรับผู้ที่ใช้ OS X คุณสามารถใช้ LaunchAgent เพื่อดูเส้นทาง / ไฟล์สำหรับการเปลี่ยนแปลงและทำบางสิ่งเมื่อเกิดขึ้น FYI - LaunchControl เป็นแอพที่ดีในการสร้าง / แก้ไข / ลบ daemons / agent ได้อย่างง่ายดาย

( ตัวอย่างจากที่นี่ )

<?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

สำหรับผู้ที่ค้นพบสิ่งนี้โดย Googling สำหรับการเปลี่ยนแปลงไฟล์โดยเฉพาะคำตอบนั้นง่ายกว่ามาก (ได้รับแรงบันดาลใจจากคำตอบของ Gilles )

หากคุณต้องการทำบางสิ่งหลังจากเขียนไฟล์ไปยังไฟล์ต่อไปนี้เป็นวิธี:

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

ตัวอย่างเช่นบันทึกเป็นcopy_myfile.shและวาง.shไฟล์ลงใน/etc/init.d/โฟลเดอร์เพื่อให้มันทำงานเมื่อเริ่มต้น


แบ่งปันปัญหากับคำตอบของไจล์สว่ามันจะไม่ตอบสนองต่อการวนซ้ำทุกครั้งซึ่งอาจไม่ตอบสนองต่อการดูไดเรกทอรีที่มีขนาดใหญ่ซ้ำ ๆ ดูคำตอบของ cychoi เพื่อแก้ไขปัญหานี้
Jonathan Hartley

0

เครื่องมือ 'fido' อาจเป็นตัวเลือกอื่นสำหรับความต้องการนี้ ดูhttps://www.joedog.org/fido-home/


โปรดอ่านฉันจะแนะนำซอฟต์แวร์สำหรับเคล็ดลับเกี่ยวกับวิธีการแนะนำซอฟต์แวร์ได้อย่างไร คุณควรให้ลิงค์อย่างน้อยข้อมูลเพิ่มเติมบางอย่างเกี่ยวกับตัวซอฟต์แวร์เองและวิธีการใช้ในการแก้ปัญหาในคำถาม
DavidPostill

0

อย่างที่บางคนทำไปแล้วฉันก็เขียนเครื่องมือบรรทัดคำสั่งที่มีน้ำหนักเบาเพื่อทำสิ่งนี้ เอกสารครบถ้วนทดสอบและเป็นโมดูล

Watch-Do

การติดตั้ง

คุณสามารถติดตั้งได้ (ถ้าคุณมี Python3 และ pip) โดยใช้:

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

การใช้

ใช้ทันทีโดยวิ่ง:

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

ภาพรวมคุณสมบัติ

  • รองรับไฟล์ globbing (ใช้-w '*.py'หรือ-w '**/*.py')
  • เรียกใช้หลายคำสั่งในการเปลี่ยนแปลงไฟล์ (เพียงระบุการ-dตั้งค่าสถานะอีกครั้ง)
  • รักษารายการไฟล์ที่จะดูแบบไดนามิกหากใช้การวนรอบ ( -rเพื่อเปิดใช้งาน)
  • หลายวิธีในการ "ดู" ไฟล์:
    • เวลาแก้ไข (ค่าเริ่มต้น)
    • ไฟล์แฮช
    • ไม่สำคัญที่จะใช้งานของคุณเอง (นี่คือตัวแก้ไข ModificationTime )
  • การออกแบบแบบแยกส่วน หากคุณต้องการให้คำสั่งทำงานเมื่อมีการเข้าถึงไฟล์มันเป็นเรื่องเล็กน้อยที่จะเขียนตัวตรวจสอบของคุณเอง (กลไกที่กำหนดว่าผู้ปฏิบัติควรจะเรียกใช้)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.