Zwykle po edycji skryptu wszystkie uruchomione skrypty są podatne na błędy.
O ile rozumiem, bash (także inne powłoki?) Stopniowo odczytuje skrypt, więc jeśli zmodyfikowałeś plik skryptu zewnętrznie, zaczyna on odczytywać niewłaściwe rzeczy. Czy jest jakiś sposób, aby temu zapobiec?
Przykład:
sleep 20
echo test
Jeśli wykonasz ten skrypt, bash przeczyta pierwszy wiersz (powiedzmy 10 bajtów) i przejdzie w tryb uśpienia. Po wznowieniu skrypt może mieć inną zawartość, zaczynając od 10-tego bajtu. Mogę być w środku linii w nowym skrypcie. W ten sposób działający skrypt zostanie uszkodzony.
\n
może załatwić sprawę? Może()
zrobi to podpowłoka ? Nie mam z tym dużego doświadczenia, proszę o pomoc!sleep 20 ;\n echo test ;\n sleep 20
a ja zacznę go edytować, może źle się zachowywać. Na przykład bash może odczytać pierwsze 10 bajtów skryptu, zrozumiećsleep
polecenie i przejść do trybu uśpienia. Po wznowieniu w pliku będzie inna zawartość, zaczynając od 10 bajtów.Odpowiedzi:
Tak, powłoki, a
bash
zwłaszcza starają się czytać plik po jednym wierszu na raz, więc działa tak samo, jak podczas korzystania z niego interaktywnie.Zauważysz, że gdy plik nie jest widoczny (jak potok),
bash
odczytuje nawet jeden bajt na raz, aby mieć pewność, że nie przeczyta on\n
znaku. Gdy plik jest widoczny, optymalizuje się, odczytując jednocześnie pełne bloki, ale należy szukać później\n
.Oznacza to, że możesz robić takie rzeczy jak:
Lub pisz skrypty, które same się aktualizują. Nie byłbyś w stanie tego zrobić, gdyby nie dał ci takiej gwarancji.
Teraz rzadko zdarza się, że chcesz robić takie rzeczy i, jak się okazało, funkcja ta przeszkadza częściej niż jest przydatna.
Aby tego uniknąć, można spróbować i upewnij się, że nie należy zmodyfikować plik w miejscu (na przykład zmodyfikować kopię, i przenieść kopię w miejscu (jak
sed -i
lubperl -pi
a niektórzy redaktorzy zrobić na przykład)).Lub możesz napisać skrypt:
(zwróć uwagę, że ważne jest, aby
exit
znajdować się w tym samym wierszu co}
; chociaż możesz również umieścić go w nawiasach klamrowych tuż przed zamknięciem).lub:
Powłoka będzie musiała przeczytać skrypt do momentu, aż
exit
zacznie cokolwiek robić. Dzięki temu powłoka nie będzie ponownie czytać ze skryptu.Oznacza to jednak, że cały skrypt będzie przechowywany w pamięci.
Może to również wpłynąć na parsowanie skryptu.
Na przykład w
bash
:Wyprowadziłby kod U + 00E9 zakodowany w UTF-8. Jeśli jednak zmienisz go na:
\ue9
Zostanie poszerzona w charset, który był w rzeczywistości w tym czasie, że rozkaz został przeanalizowany, która w tym przypadku jest przedexport
komenda jest wykonywana.Zauważ też, że jeśli użyjesz polecenia
source
aka.
, z niektórymi powłokami, będziesz miał ten sam problem z plikami źródłowymi.Nie dzieje się tak w przypadku,
bash
gdysource
komenda odczytuje plik w całości przed jego interpretacją. Jeśli piszeszbash
specjalnie, możesz z tego skorzystać, dodając na początku skryptu:(Nie polegałbym jednak na tym, ponieważ można sobie wyobrazić, że przyszłe wersje
bash
mogłyby zmienić to zachowanie, które może być obecnie postrzegane jako ograniczenie (bash i AT&T ksh są jedynymi powłokami podobnymi do POSIX, które zachowują się tak daleko, o ile są w stanie powiedzieć) aalready_sourced
trik jest nieco kruchy, ponieważ zakłada, że zmienna nie znajduje się w środowisku, nie wspominając już o tym, że wpływa na zawartość zmiennej BASH_SOURCE)źródło
}; exec true
. W ten sposób nie ma wymagań dotyczących nowych linii na końcu pliku, co jest przyjazne dla niektórych edytorów (takich jak emacs). Wszystkie testy, o których mogłem myśleć, działają poprawnie}; exec true
}; exit
? Tracisz także status wyjścia.. script
użycia polecenia kropki ( ).Musisz po prostu usunąć plik (tzn. Skopiować go, usunąć, zmienić nazwę kopii z powrotem na pierwotną nazwę). W rzeczywistości można skonfigurować wiele edytorów, aby zrobili to za Ciebie. Kiedy edytujesz plik i zapisujesz w nim zmieniony bufor, zamiast nadpisania pliku zmieni on nazwę starego pliku, utworzy nowy i umieści nową zawartość w nowym pliku. Dlatego każdy uruchomiony skrypt powinien kontynuować bez problemów.
Korzystając z prostego systemu kontroli wersji, takiego jak RCS, który jest łatwo dostępny dla vimów i emacsa, zyskujesz podwójną zaletę posiadania historii zmian, a system kontroli powinien domyślnie usunąć bieżący plik i odtworzyć go w poprawnych trybach. (Oczywiście uważaj na twarde linkowanie takich plików).
źródło
Najprostsze rozwiązanie:
W ten sposób bash przeczyta cały
{}
blok przed jego wykonaniem orazexit
dyrektywa upewni się, że nic nie zostanie odczytane poza blokiem kodu.Jeśli nie chcesz „uruchamiać” skryptu, ale „źródła”, potrzebujesz innego rozwiązania. Powinno to wtedy działać:
Lub jeśli chcesz bezpośredniej kontroli nad kodem wyjścia:
Voilà! Ten skrypt można bezpiecznie edytować, pozyskiwać i uruchamiać. Nadal musisz się upewnić, że nie zmodyfikujesz go w tych milisekundach, gdy jest on początkowo czytany.
źródło
Dowód koncepcji. Oto skrypt, który sam się modyfikuje:
widzimy zmienioną wersję wydruku
Wynika to z faktu, że ładowanie bash utrzymuje uchwyt pliku do otwarcia na skrypt, więc zmiany w pliku będą widoczne natychmiast.
Jeśli nie chcesz aktualizować kopii w pamięci, odłącz oryginalny plik i zastąp go.
Jednym ze sposobów jest użycie sed -i.
dowód koncepcji
Jeśli używasz edytora do zmiany skryptu, włączenie funkcji „zachowaj kopię zapasową” może być wszystkim, czego potrzeba, aby edytor zapisał zmienioną wersję do nowego pliku zamiast zastąpić istniejący.
źródło
bash
nie otwiera pliku za pomocąmmap()
. Wystarczy uważnie czytać jedną linię w razie potrzeby, tak jak wtedy, gdy otrzymuje polecenia z urządzenia końcowego, gdy jest interaktywne.Zawijanie skryptu w bloku
{}
jest prawdopodobnie najlepszą opcją, ale wymaga zmiany skryptów.byłaby drugą najlepszą opcją (zakładając, że tmpfs ) wadą jest to, że psuje 0 $, jeśli skrypty tego używają.
użycie czegoś podobnego
F=test.sh; tail -n $(cat "$F" | wc -l) "$F" | bash
jest mniej idealne, ponieważ musi przechowywać cały plik w pamięci i psuje 0 USD.należy unikać dotykania oryginalnego pliku, aby ostatnia modyfikacja nie blokowała odczytu blokad odczytu i twardych łączy. w ten sposób możesz pozostawić otwarty edytor podczas uruchamiania pliku, a rsync nie będzie niepotrzebnie sprawdzać sumy pliku dla kopii zapasowych i funkcji dowiązań twardych zgodnie z oczekiwaniami.
zastąpienie pliku podczas edycji działałoby, ale jest mniej niezawodne, ponieważ nie jest możliwe do wyegzekwowania przez inne skrypty / użytkowników / lub ktoś może zapomnieć. I znowu zerwałoby twarde linki.
źródło
tac test.sh | tac | bash