2010-08-27 20:02:40 +0000 2010-08-27 20:02:40 +0000
459
459

Jak wykonać polecenie, gdy plik się zmienia?

Chcę mieć szybki i prosty sposób na wykonanie polecenia, gdy plik się zmienia. Chcę coś bardzo prostego, coś co zostawię uruchomione na terminalu i zamknę go, gdy tylko skończę pracę z tym plikiem.

Obecnie używam tego:

while read; do ./myfile.py ; done

A następnie muszę przejść do tego terminalu i wcisnąć Enter, gdy tylko zapiszę ten plik w edytorze. To, czego chcę, to coś takiego:

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

lub inne rozwiązanie tak proste, jak to.

BTW: Używam Vima i wiem, że mogę dodać autokomandę, aby uruchomić coś na BufWrite, ale nie jest to rozwiązanie, które chcę teraz.

Uaktualnij: Chcę coś prostego, w miarę możliwości do wyrzucenia. Co więcej, chcę, aby coś zostało uruchomione w terminalu, ponieważ chcę widzieć wyjście programu (chcę widzieć komunikaty o błędach).

Odpowiedzi:Dziękuję za wszystkie odpowiedzi! Wszystkie są bardzo dobre, a każda z nich ma zupełnie inne podejście niż pozostałe. Ponieważ muszę zaakceptować tylko jedną, akceptuję tę, której rzeczywiście używałem (była prosta, szybka i łatwa do zapamiętania), mimo że wiem, że nie jest najbardziej elegancka.

Odpowiedzi (37)

434
434
434
2010-08-27 20:54:55 +0000

Prosty, używając inotifywait (zainstaluj pakiet inotify-tools twojej dystrybucji):

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

lub

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

Pierwszy fragment jest prostszy, ale ma znaczną wadę: przegapi zmiany wykonane podczas gdy inotifywait nie jest uruchomiony (w szczególności podczas gdy myfile jest uruchomiony). Drugi snippet nie ma tej wady. Należy jednak pamiętać, że zakłada on, iż nazwa pliku nie zawiera białej przestrzeni. Jeśli jest to problem, użyj opcji --format, aby zmienić wyjście tak, aby nie zawierało nazwy pliku:

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

Tak czy inaczej, istnieje ograniczenie: jeśli jakiś program zastąpi myfile.py innym plikiem, zamiast zapisu do istniejącego myfile, inotifywait umrze. Wiele edytorów działa w ten sposób.

Aby pokonać to ograniczenie, użyj inotifywait w katalogu:

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

Alternatywnie, użyj innego narzędzia, które używa tych samych podstawowych funkcji, takich jak incron (pozwala na rejestrację zdarzeń, gdy plik jest modyfikowany) lub fswatch (narzędzie, które działa także na wielu innych wariantach Uniksa, używając analogu każdego wariantu do inotify Linuxa).

179
179
179
2013-10-25 09:41:16 +0000

entr http://entrproject.org/ ) zapewnia bardziej przyjazny interfejs do powiadamiania (a także obsługuje *BSD i Mac OS X).

Ułatwia określenie wielu plików do oglądania (limitowany tylko przez ulimit -n), eliminuje kłopoty związane z wymianą plików i wymaga mniej bashowej składni:

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

Używam go na całym drzewie źródłowym projektu do przeprowadzania testów jednostkowych dla kodu, który obecnie modyfikuję, i jest to już ogromny impuls dla mojej pracy.

Flagi takie jak -c (usuń ekran pomiędzy uruchomieniami) i -d (wyjdź, gdy nowy plik jest dodawany do monitorowanego katalogu) dodają jeszcze więcej elastyczności, na przykład możesz zrobić:

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

Na początku 2018 wciąż jest w fazie aktywnego rozwoju i można go znaleźć w Debianie i Ubuntu (apt install entr); budowanie z repo autora było w każdym razie bezbolesne.

112
112
112
2011-06-30 13:34:28 +0000

Napisałem program Pythona, aby zrobić dokładnie to samo when-changed .

Użycie jest proste:

when-changed FILE COMMAND...

Lub do oglądania wielu plików:

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

FILE może być katalogiem. Oglądaj rekurencyjnie z -r. Użyj %f, aby przekazać nazwę pliku do polecenia.

53
53
53
2013-08-20 17:12:47 +0000

A co z tym scenariuszem? Używa on polecenia stat, aby uzyskać czas dostępu do pliku i uruchamia polecenie za każdym razem, gdy następuje zmiana czasu dostępu (za każdym razem, gdy plik jest dostępny).

#!/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
30
30
30
2010-08-27 20:12:25 +0000

Rozwiązanie przy użyciu Vima:

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

Ale nie chcę tego rozwiązania, ponieważ pisanie na maszynie jest trochę denerwujące, trochę trudne do zapamiętania, co dokładnie pisać, i trochę trudne do cofnięcia jego efektów (trzeba uruchomić :au! BufWritePost myfile.py). Dodatkowo, to rozwiązanie blokuje Vima do czasu, aż komenda skończy się wykonywać.

Dodałem to rozwiązanie tutaj tylko dla kompletności, ponieważ może to pomóc innym ludziom.

Aby wyświetlić wyjście programu (i całkowicie zakłócić przepływ edycji, ponieważ wyjście zapisze się nad edytorem na kilka sekund, aż wciśniesz Enter), usuń komendę :silent.

23
23
23
2012-06-09 23:51:16 +0000

Jeśli zdarzy się, że masz zainstalowane npm , nodemon jest prawdopodobnie najłatwiejszym sposobem na rozpoczęcie pracy, szczególnie na OS X, który najwyraźniej nie posiada narzędzi inotify. Obsługuje on uruchamianie komendy w przypadku zmiany folderu.

21
21
21
2016-03-22 14:57:03 +0000

Dla tych, którzy nie potrafią zainstalować inotify-tools tak jak ja, powinno się to przydać:

watch -d -t -g ls -lR

Ta komenda zakończy się po zmianie wyjścia, ls -lR wyświetli każdy plik i katalog z jego rozmiarem i datami, więc jeśli plik zostanie zmieniony to powinno się zakończyć komendę, jak mówi człowiek:

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

Wiem, że ta odpowiedź może nie być przez nikogo odczytana, ale mam nadzieję, że ktoś do niej dotrze.

Przykład linii poleceń:

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

Otwórz inny terminal:

~ $ echo "testing" > /tmp/test

Teraz pierwszy terminal wyjdzie 1,2,3

Prosty przykład skryptu:

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

watch -d -t -g ls -lR ${DIR_TO_WATCH} && ${COMMAND}
17
17
17
2015-09-09 21:49:14 +0000

rerun2 na githubie ) jest 10-wierszowym skryptem Bash o nazwie:

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

Zapisz wersję githuba jako ‘rerun’ na swojej ścieżce i wywołaj ją używając:

rerun COMMAND

Uruchomi polecenie COMMAND za każdym razem, gdy pojawi się zdarzenie modyfikacji systemu plików w twoim bieżącym katalogu (rekurencyjne. )

Rzeczy, które mogą się w nim podobać:

  • Używa inotify, więc jest bardziej responsywny niż polling. Wspaniały do uruchamiania submilisekundowych testów jednostkowych, lub renderowania plików w kropkach grafów, za każdym razem gdy trafisz ‘zapisz’.
  • Ponieważ jest tak szybki, nie musisz się kłopotać mówieniem mu, żeby ignorował duże subdiry (jak węzły _moduły) tylko ze względu na wydajność.
  • Jest super responsywny, bo tylko raz wywołuje inotifywait, przy uruchamianiu, zamiast go uruchamiać i ponosić kosztowne uderzenie ustanawiania zegarków, przy każdej iteracji.
  • To tylko 12 linii Basha
  • Ponieważ jest to Bash, interpretuje komendy, które wydajesz dokładnie tak, jakbyś je wpisywał w prompu Bash'a. (Prawdopodobnie jest to mniej fajne, jeśli używasz innej powłoki)
  • Nie traci zdarzeń, które zdarzają się podczas wykonywania polecenia, w przeciwieństwie do większości innych rozwiązań inotify na tej stronie.
  • Przy pierwszym zdarzeniu, wprowadza ‘martwy okres’ na 0.15 sekundy, podczas którego inne zdarzenia są ignorowane, zanim polecenie zostanie wykonane dokładnie raz. Jest to tak, że pośpiech zdarzeń spowodowanych przez taniec create-write-move, który robi Vi lub Emacs podczas zapisywania bufora, nie powoduje wielu pracochłonnych wykonań prawdopodobnie wolno działającego pakietu testowego. Zdarzenia, które wystąpią podczas wykonywania polecenia nie są ignorowane - spowodują one drugi martwy okres i późniejsze wykonanie.

Rzeczy, które mogą się nie podobać:

  • Używa inotify, więc nie będzie działać poza Linukslandem.
  • Ponieważ używa inotify, nie będzie próbował oglądać katalogów zawierających więcej plików niż maksymalna liczba użytkowników inotify. Domyślnie jest ona ustawiona na około 5,000 do 8,000 na różnych maszynach, z których korzystam, ale jest łatwa do zwiększenia. Zobacz https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Nie wykonuje on poleceń zawierających aliasy Bash'a. Mógłbym przysiąc, że to kiedyś działało. W zasadzie, ponieważ jest to Bash, nie wykonujący komendy w podpowłoce, oczekiwałbym, że to zadziała. Chętnie bym to usłyszał, jeśli ktoś wie, dlaczego tak nie jest. Wiele innych rozwiązań na tej stronie również nie potrafi wykonać takich komend.
  • Osobiście chciałbym móc wcisnąć klucz w terminalu, w którym jest uruchomiony, aby ręcznie wywołać dodatkowe wykonanie komendy. Czy mógłbym dodać to w jakiś sposób, po prostu? Równocześnie działająca pętla ‘while read -n1’, która również wywołuje execute?
  • W tym momencie zakodowałem ją, aby wyczyścić terminal i wypisać wykonywaną komendę na każdej iteracji. Niektórzy ludzie mogą chcieć dodać flagi linii poleceń, aby wyłączyć takie rzeczy, itp. Ale to zwiększyłoby wielokrotnie rozmiar i złożoność.

Jest to udoskonalenie anweru @cychoi.

12
12
12
2010-08-27 21:23:29 +0000

Oto prosty skrypt powłoki Bourne'a, który:

  1. Używa dwóch argumentów: pliku, który ma być monitorowany i polecenia (z argumentami, jeżeli to konieczne)
  2. Kopiuje monitorowany plik do katalogu /tmp
  3. Sprawdza co dwie sekundy czy monitorowany plik jest nowszy niż kopia
  4. Jeśli jest nowszy nadpisuje kopię nowszym oryginałem i wykonuje polecenie
  5. Czyści się po sobie po naciśnięciu Ctr-C

To działa na FreeBSD. Jedynym problemem związanym z przenośnością, jaki mogę sobie wyobrazić jest to, że jakiś inny Uniks nie posiada komendy mktemp(1), ale w tym przypadku można po prostu zakodować nazwę pliku temp.

8
8
8
2014-08-08 22:20:09 +0000

jeśli masz zainstalowany nodemon , to możesz to zrobić:

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

W moim przypadku edytuję html lokalnie i wysyłam go na mój zdalny serwer, gdy plik się zmieni.

nodemon -w <watch directory> -x "scp filename jaym@jay-remote.com:/var/www" -e ".html"
8
8
8
2010-08-27 20:12:59 +0000

Spójrz na incron . Jest podobny do crona, ale używa inotify events zamiast czasu.

6
6
6
2014-07-09 13:16:10 +0000

Zajrzyj do Guard, w szczególności z tym pluginem: https://github.com/hawx/guard-shell

Możesz ustawić go tak, aby oglądał dowolną ilość wzorców w katalogu swojego projektu i wykonywał polecenia w przypadku wystąpienia zmian. Jest duża szansa nawet na to, że jest dostępny plugin do tego, co próbujesz zrobić w pierwszej kolejności.

6
6
6
2014-01-22 14:55:46 +0000

Inne rozwiązanie z NodeJs, * fsmonitor ** :

  1. W sumie

  2. Z linii poleceń (przykład, monitor logów i “detal” jeśli jeden plik logu ulegnie zmianie)

5
5
5
2015-12-28 10:44:18 +0000
  • Watchdog ** jest projektem Pythona, i może być tym czego szukasz:

Obsługiwane platformy

  • Linux 2. 6 (inotify)
  • Mac OS X (FSE events, kqueue)
  • FreeBSD/BSD (kqueue)
  • Windows (ReadDirectoryChangesW z portami I/O; ReadDirectoryChangesW z wątkami roboczymi)
  • OS-independent (ankietowanie dysku pod kątem zrzutów katalogów i porównywanie ich okresowo; wolne i niezalecane)

Właśnie napisałem dla niego opakowanie linii poleceń * watchdog_exec **:

Przykładowe wykonanie

Na zdarzeniu fs dotyczącym plików i folderów w bieżącym katalogu, wykonaj polecenie echo $src $dst, chyba że zdarzenie fs zostanie zmodyfikowane, a następnie wykonaj polecenie python $src.

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

Używając krótkich argumentów, i ograniczając się do wykonania tylko wtedy, gdy zdarzenia dotyczą “ main.py”:

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

EDIT: Właśnie znaleziono, że Watchdog ma oficjalny CLI o nazwie watchmedo, więc sprawdź również to.

5
5
5
2012-07-02 16:36:51 +0000

Pod Linuksem:

man watch

watch -n 2 your_command_to_run

Będzie wykonywał komendę co 2 sekundy.

Jeśli twoja komenda trwa dłużej niż 2 sekundy, zegarek odczeka, aż zostanie wykonana przed wykonaniem jej ponownie.

4
4
4
2010-08-27 20:37:52 +0000

Jeśli Twój program generuje jakiś rodzaj logu/wyjścia, możesz utworzyć plik Makefile z regułą dla tego logu/wyjścia, która zależy od Twojego skryptu i zrobić coś takiego jak

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

Alternatywnie, możesz utworzyć fałszywy cel i mieć regułę dla niego zarówno wywołać swój skrypt i dotknąć fałszywego celu (choć nadal zależy to od Twojego skryptu).

4
4
4
2014-09-15 23:24:58 +0000

swarminglogic napisał skrypt o nazwie watchfile.sh , również dostępny jako GitHub Gist .

4
4
4
2015-06-17 18:03:51 +0000

Ulepszona po odpowiedź Gillesa .

Ta wersja działa inotifywait raz, a następnie monitoruje zdarzenia ( np.: modify). Tak, że inotifywait _nie musi być powtórzony przy każdym napotkanym zdarzeniu.

Jest szybki i szybki!(nawet przy rekurencyjnym monitorowaniu dużego katalogu)

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
3
3
3
2010-08-27 20:05:59 +0000

Trochę więcej po stronie programowania, ale chcesz coś w rodzaju inotify . Istnieją implementacje w wielu językach, takie jak jnotify i pyinotify .

Ta biblioteka pozwala na monitorowanie pojedynczych plików lub całych katalogów i zwraca zdarzenia po wykryciu akcji. Zwracane informacje obejmują nazwę pliku, akcję (tworzenie, modyfikację, zmianę nazwy, usunięcie) oraz ścieżkę do pliku, a także inne przydatne informacje.

3
3
3
2012-11-11 21:48:31 +0000

Dla tych z Państwa, którzy szukają rozwiązania FreeBSD, oto port:

/usr/ports/sysutils/wait_on
3
3
3
2014-04-29 13:56:13 +0000

Podoba mi się prostota while inotifywait ...; do ...; done, ale ma dwie kwestie:

  • Zmiany w plikach zachodzące podczas do ...; będą braknąć
  • wolno podczas używania w trybie rekurencyjnym

Dlatego też stworzyłem skrypt pomocniczy, który używa inotifywait bez tych ograniczeń: inotifyexec

Sugeruję umieścić ten skrypt na swojej ścieżce, jak w ~/bin/. Użycie jest opisane przez samo uruchomienie komendy.

Przykład: inotifyexec "echo test" -r .

3
3
3
2016-04-12 14:53:44 +0000

Ulepszone rozwiązanie Sebastiana z komendą 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

Przykład wywołania:

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

Działa, ale uważaj: komenda watch ma znane błędy (patrz człowiek): reaguje tylko na zmiany VISIBLE w końcówkach wyjścia -g CMD.

2
2
2
2017-03-01 20:14:14 +0000

Możesz spróbować reflex .

Reflex to małe narzędzie do oglądania katalogu i ponownego uruchamiania komendy, gdy niektóre pliki ulegną zmianie. Jest to świetne narzędzie do automatycznego uruchamiania zadań kompilacji/lint/testowania oraz do ponownego ładowania aplikacji po zmianie kodu.

# Rerun make whenever a .c file changes
reflex -r '\.c$' make
1
1
1
2017-04-05 07:38:43 +0000

Mam do tego GIST i użycie jest całkiem proste

watchfiles <cmd> <paths...>

https://gist.github.com/thiagoh/5d8f53bfb64985b94e5bc8b3844dba55

1
1
1
2017-06-03 12:10:57 +0000

Jak kilka innych osób, napisałem również lekkie narzędzie wiersza poleceń, aby to zrobić. Jest ono w pełni udokumentowane, przetestowane i modułowe.

Watch-Do

Install

You can install it (if you have Python3 and pip) using:

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

Use

Use it immediately by running:

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

Features Overview

  • Obsługuje globbing plików (użyj -w '*.py' lub -w '**/*.py')
  • Wykonuje wiele poleceń na zmianę pliku (wystarczy ponownie podać flagę -d)
  • Dynamicznie utrzymuje listę plików do obejrzenia, jeśli globbing jest używany (-r aby to włączyć)
  • Wiele sposobów na “obejrzenie” pliku:
  • Modification time (domyślnie)
  • File hash
  • Trywialna implementacja własnego pliku (jest to ModificationTime watcher)
  • Modułowa konstrukcja. Jeśli chcesz, aby komendy były uruchamiane, gdy plik jest dostępny, trywialne jest napisanie własnego watchera (mechanizm, który określa, czy wykonawca powinien być uruchamiany).
1
1
1
2016-02-26 19:10:05 +0000

Używam do tego skryptu. I’m using inotify in monitor-mode

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

Save this as runatwrite.sh

Usage: runatwrite.sh myfile.sh

it will run myfile.sh at each write.

1
1
1
2015-09-09 19:36:04 +0000

Napisałem program Pythona, aby zrobić dokładnie to, nazwany rerun .

UPDATE: Ta odpowiedź to skrypt Pythona, który bada zmiany, co jest przydatne w pewnych okolicznościach. Dla linuksowego skryptu Bash, który używa inotify, zobacz moją drugą odpowiedź, poszukaj na tej stronie ‘rerun2’.

Instalacja dla Pythona2 lub Pythona3 za pomocą:

pip install --user rerun

i użycie jest bardzo proste:

rerun "COMMAND"

Komenda jest oczekiwana jako pojedynczy argument, a nie sekwencja oddzielonych spacjami argumentów. Dlatego zacytuj ją tak, jak pokazano na rysunku, co redukuje dodatkową ucieczkę, którą musiałbyś dodać. Po prostu wpisz polecenie tak, jak byś je wpisał w linii poleceń, ale otoczony cudzysłowami.

Domyślnie obserwuje wszystkie pliki w lub pod bieżącym katalogiem, pomijając rzeczy takie jak znane dirs kontroli źródeł, .git, .svn, itp.

Flagi opcjonalne zawierają ‘-i NAME’, które ignorują zmiany nazw plików lub katalogów. Można to podawać wielokrotnie.

Ponieważ jest to skrypt w języku Python, musi on uruchomić polecenie jako podproces, a my używamy nowej instancji aktualnej powłoki użytkownika do interpretacji ‘COMMAND’ i decydowania o tym, który proces ma być faktycznie uruchomiony. Jednakże, jeżeli twoje polecenie zawiera aliasy powłoki i tym podobne, które są zdefiniowane w .bashrc, nie zostaną one załadowane przez podpowłokę. Aby to naprawić, możesz podać flagę ‘-I’, aby użyć podpowłoki interaktywnej (zwanej też ‘login’). Jest to wolniejsze i bardziej podatne na błędy niż uruchamianie zwykłej powłoki, ponieważ musi ona generować twój .bashrc.

Używam jej z Pythonem 3, ale ostatnio sprawdziłem, że ponowne uruchamianie nadal działa z Pythonem 2.

Dwuwarstwowy miecz jest taki, że używa pollingu zamiast inotify. Z drugiej strony, oznacza to, że działa na każdym systemie operacyjnym. Plus, jest to lepsze niż niektóre inne rozwiązania pokazane tutaj, jeśli chodzi o uruchamianie danej komendy tylko raz na kilka zmian w systemie plików, a nie raz na jeden zmodyfikowany plik, podczas gdy w tym samym czasie uruchamia ona komendę po raz drugi, jeśli jakieś pliki ponownie się zmienią podczas uruchamiania komendy.

Z drugiej strony, polling oznacza, że istnieje opóźnienie od 0.0 do 1.0 sekundy, i oczywiście jest powolny w monitorowaniu bardzo dużych katalogów. Mimo to, nigdy nie spotkałem się z projektem na tyle dużym, że jest to nawet zauważalne, tak długo jak używasz ‘-i’ do ignorowania dużych rzeczy, takich jak twój wirtualnyenv i węzeł :-).

Hmmm. rerun jest dla mnie niezbędny od lat - w zasadzie używam go osiem godzin dziennie do przeprowadzania testów, przebudowywania plików kropkowych podczas ich edycji, itp. Ale teraz przychodzi mi to pisać tutaj, jasne jest, że muszę przejść na rozwiązanie, które wykorzystuje inotify (nie używam już Windows ani OSX) i jest napisane w Bashu (więc działa z aliasami bez dodatkowych manipulacji).

1
1
1
2015-09-01 04:53:52 +0000

Odpowiedź onelinera, której używam do śledzenia zmian w plikach:

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

Nie musisz inicjować BF, jeśli wiesz, że pierwsza randka jest godziną rozpoczęcia.

To jest proste i przenośne. Jest jeszcze jedna odpowiedź oparta na tej samej strategii przy użyciu skryptu tutaj. Spójrz również.

  • *

Użycie: Używam tego do debugowania i pilnowania ~/.kde/share/config/plasma-desktop-appletsrc; to z jakiegoś nieznanego powodu ciągle traci mój SwitchTabsOnHover=false

1
1
1
2016-03-22 15:33:56 +0000

Osoby używające systemu OS X mogą użyć programu LaunchAgent, aby obserwować ścieżkę/plik pod kątem zmian i zrobić coś, gdy to nastąpi. FYI - LaunchControl to dobra aplikacja do łatwego tworzenia/modyfikowania/usuwania demonów/agentów.

przykład wzięty stąd )

<?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
0
0
2019-11-26 15:53:22 +0000

Jeśli nie chcesz instalować w tym celu niczego nowego, oto mały skrypt powłoki, który możesz umieścić na swojej ścieżce (np. pod $HOME/bin). Uruchamia on polecenie, gdy dostarczony jeden lub więcej plików zostanie zmieniony. Na przykład:

$ onchange './build' *.txt
#!/bin/sh
cmd="$1"; shift
files="$@"
changed() { tar -c $files | md5sum; } # for on every save use: `stat -c %Z $files`
while true; do
  if ["$(changed)" != "$last"]; then
    last="$(changed)"
    $cmd
  fi
  sleep 1
done

Bełkotuje, a następnie skasuje zawartość plików i/lub katalogów, więc nie uruchomi się za każdym razem, gdy kompulsywnie trafisz na CTRL-S (lub wpiszesz :w), ale tylko wtedy, gdy coś się rzeczywiście zmieni. Zauważ, że będzie sprawdzał każdą sekundę, więc nie dołączaj za dużo, bo twój komputer może się spowolnić. Jeśli chcesz, aby uruchamiał się na każdym zapisie, użyj stat na stałe (zobacz komentarz). Ponadto, dla maca md5sum jest nazywany md5, jeśli dobrze pamiętam.

Mała sztuczka: W momencie, gdy chcesz go użyć, prawdopodobnie będziesz chciał powtórzyć ostatnie polecenie, które właśnie wykonałeś, ale w kółko. Możesz użyć skrótu !!, aby ‘wstrzyknąć’ ostatnią komendę do tej:

$ onchange "!!" *.txt
0
0
0
2018-04-12 18:32:28 +0000

Opis

Spojrzy na plik ze zmianami i wykona dowolną komendę (łącznie z dalszymi argumentami) została podana jako drugie stwierdzenie.** Wyczyści również ekran i wydrukuje czas ostatniego wykonania. Uwaga: możesz sprawić, aby funkcja była bardziej (lub mniej) reaktywna, zmieniając liczbę sekund, przez które funkcja powinna spać po każdym cyklu pętli.

Przykładowe użycie

watch_file my_file.php php my_file.php

Ta linia będzie oglądać plik php my_file.php i uruchamiać interpreter php, gdy tylko się zmieni.

Definicja funkcji

function watch_file (){

### Set initial time of file
LTIME=`stat -c %Z $1`
printf "&00133c"
echo -e "watching: $1 ---- $(date '+%Y-%m-%d %H:%M:%S')\n-------------------------------------------\n"
${@:2}

while true
do
   ATIME=`stat -c %Z $1`

   if [["$ATIME" != "$LTIME"]]
   then
    printf "&00133c"
    echo -e "watching: $1 ---- $(date '+%Y-%m-%d %H:%M:%S')\n-------------------------------------------\n"
    ${@:2}
    LTIME=$ATIME
   fi
   sleep 1
done
}

Credit

Jest to w zasadzie bardziej ogólna wersja odpowiedzi VDR.

0
0
0
2018-03-19 20:30:21 +0000

Basic usage

Here is a solution that does not require installing more software and works out of the box.

tail -q --follow=name myfile.txt | head -n 0

This command exits under the following conditions:

  • A line is added to myfile.txt after the command is run
  • The myfile.txt is replaced with another after the command is run

You say you are using vim, and vim will replace the file on save. Przetestowałem to z vimem.

Możesz zignorować wyjście tej komendy, może ona wspomnieć coś w stylu:

tail: ‘myfile.txt’ został zastąpiony; po zakończeniu nowego pliku

Zaawansowane użycie

Możesz połączyć to z timeout aby zwrócić true lub false. Możesz użyć go w ten sposób:

timeout 5s bash -c ‘tail -q –follow=name pipe 2> /dev/null | head -n 0’ && echo changed ||| echo timeout

Discussion

tail uses inotify under the hood. W ten sposób otrzymujesz to fantazyjne asynchroniczne zachowanie bez żadnych ankiet. Prawdopodobnie istnieje jakiś inny standardowy program uniksowy, który używa inotify, który możemy wykorzystać bardziej elegancko.

Czasami te komendy wychodzą od razu, ale jeśli uruchomisz je od razu po raz drugi, wtedy działają tak, jak reklamowane. Zrobiłem gdzieś błąd off-by-one, proszę pomóż mi to poprawić.

Na RHEL mogę użyć:

timeout 5s sh -c ‘gio monitor pipe | head -n 0’ && echo changed || echo timeout

Ale nie jestem pewien czy to jest przenośne.

0
0
0
2019-01-28 16:51:42 +0000

find może zrobić trick.

while true; do
    find /path/to/watched/file -ctime 1s | xargs do-what-you-will
done

find -ctime 1s drukuje nazwę pliku, jeśli była zmieniona w ostatnim 1s.

0
0
0
2015-05-13 15:21:33 +0000

Dla osób, które znajdą to w Googlingu dla zmian w pliku particular, odpowiedź jest o wiele prostsza (inspirowana przez odpowiedź Gillesa ).

Jeśli chcesz zrobić coś po, do którego konkretny plik został napisany, oto jak:

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

Zapisz to jako, na przykład, copy_myfile.sh i umieść plik .sh w folderze /etc/init.d/, aby uruchomić go podczas startu.

0
0
0
2019-03-21 11:33:37 +0000

Miałem trochę inną sytuację. Ale czuję, że może się to przydać komuś czytającemu to pytanie.

Musiałem zostać powiadomiony, gdy plik dziennika zmienił rozmiar, ale nie było to konieczne natychmiast. A w przyszłości mogą to być dni lub tygodnie, więc nie mogłem używać inotify (który i tak nie był zainstalowany/aktywowany na tym serwerze) w linii poleceń (nie chciałem używać nohup lub podobnego). Postanowiłem więc uruchomić skrypt bash na cron, aby sprawdzić

Skrypt zapisuje rozmiar obserwowanego pliku w pliku tekstowym i przy każdym uruchomieniu cron sprawdza, czy ta wartość się zmieniła i wysyła do mnie ostatnią linię, jeśli zmieniono

#!/bin/bash
FILE_TO_WATCH="/path/to/log_file.log"
FILESIZE_FILE="/path_to/record.txt"
SUBJECT="Log file 'log_file.log' has changed"
MAILTO="info@example.com"
BODY="Last line of log file:\n"
LAST_LINES=1

# get old recorded file size from file
OLD_FILESIZE=$(cat "${FILESIZE_FILE}")
# write current file size into file
stat --printf="%s" "${FILE_TO_WATCH}" > "${FILESIZE_FILE}"
# get new recorded file size from file
NEW_FILESIZE=$(cat "${FILESIZE_FILE}")

if ["${OLD_FILESIZE}" != "${NEW_FILESIZE}"]; then
    echo -e "${BODY}"$(tail -${LAST_LINES} ${FILE_TO_WATCH}) | mail -s "${SUBJECT}" "${MAILTO}"
fi
0
0
0
2019-11-23 23:41:53 +0000

Sprawdź https://github.com/watchexec/watchexec .

watchexec jest prostym, samodzielnym narzędziem, które obserwuje ścieżkę i uruchamia komendę, gdy wykryje modyfikacje.

Przykład

Obserwuj wszystkie pliki JavaScript, CSS i HTML w aktualnym katalogu i wszystkich podkatalogach w celu dokonania zmian, uruchamiane po wykryciu zmiany:

$ watchexec --exts js,css,html make

0
0
0
2017-03-20 13:52:53 +0000

Narzędzie “fido” może być jeszcze jedną opcją na taką potrzebę. Patrz https://www.joedog.org/fido-home/