Mam skrypt powłoki, który odczytuje ze standardowego wejścia . W rzadkich przypadkach nikt nie będzie gotowy do wprowadzenia danych, a skrypt musi upłynąć limit czasu . W przypadku przekroczenia limitu czasu skrypt musi wykonać kod czyszczenia. Jak najlepiej to zrobić?
Skrypt ten musi być bardzo przenośny , w tym do systemów unixowych XX wieku bez kompilatora C oraz do urządzeń wbudowanych z uruchomionym busyboxem, więc nie można polegać na Perlu, bash, dowolnym języku kompilowanym, a nawet pełnym POSIX.2. W szczególności $PPID
, read -t
i pułapki idealnie POSIX nie są dostępne. Zapisywanie do pliku tymczasowego jest również wykluczone; skrypt może działać, nawet jeśli wszystkie systemy plików są zamontowane tylko do odczytu.
Żeby było trudniej, chcę, aby skrypt był dość szybki, gdy nie upłynie limit czasu. W szczególności używam również skryptu w systemie Windows (głównie w Cygwin), gdzie rozwidlenie i exec są szczególnie niskie, więc chcę ograniczyć ich użycie do minimum.
W skrócie, mam
trap cleanup 1 2 3 15
foo=`cat`
i chcę dodać limit czasu. Nie mogę wymienić cat
z read
zabudowy. W przypadku przekroczenia limitu czasu chcę wykonać tę cleanup
funkcję.
Tło: ten skrypt zgaduje kodowanie terminala, drukując jakieś 8-bitowe znaki i porównując pozycję kursora przed i po. Początek skryptu testuje, że stdout jest podłączony do obsługiwanego terminala, ale czasami środowisko leży (np. plink
Ustawia, TERM=xterm
nawet jeśli jest wywoływaneTERM=dumb
). Odpowiednia część skryptu wygląda następująco:
text='Éé' # UTF-8; shows up as Ãé on a latin1 terminal
csi='␛['; dsr_cpr="${csi}6n"; dsr_ok="${csi}5n" # ␛ is an escape character
stty_save=`stty -g`
cleanup () { stty "$stty_save"; }
trap 'cleanup; exit 120' 0 1 2 3 15 # cleanup code
stty eol 0 eof n -echo # Input will end with `0n`
# echo-n is a function that outputs its argument without a newline
echo-n "$dsr_cpr$dsr_ok" # Ask the terminal to report the cursor position
initial_report=`tr -dc \;0123456789` # Expect ␛[42;10R␛[0n for y=42,x=10
echo-n "$text$dsr_cpr$dsr_ok"
final_report=`tr -dc \;0123456789`
cleanup
# Compute and return initial_x - final_x
Jak mogę zmodyfikować skrypt, aby jeśli tr
nie odczytał żadnych danych wejściowych po 2 sekundach, został zabity, a skrypt wykonuje cleanup
funkcję?
Odpowiedzi:
A co z tym:
To znaczy: uruchom komendę produkującą dane wyjściowe i
sleep
w tej samej grupie procesów, dla nich tylko grupy procesów. Którekolwiek polecenie zwróci pierwsze, zabija całą grupę procesów.Czy ktoś mógłby się zastanawiać: Tak, rura nie jest używana; jest ominięty przy użyciu przekierowań. Jedynym celem tego jest, aby powłoka uruchamiała dwa procesy w tej samej grupie procesów.
Jak zauważył Gilles w swoim komentarzu, nie zadziała to w skrypcie powłoki, ponieważ proces skryptu zostałby zabity wraz z dwoma podprocesami.
Jednym ze sposobów¹ wymuszenia uruchomienia polecenia w oddzielnej grupie procesów jest uruchomienie nowej interaktywnej powłoki:
Ale mogą być z tym zastrzeżenia (np. Kiedy stdin nie jest tty?). Przekierowanie stderr ma za zadanie pozbyć się komunikatu „Zakończony”, gdy interaktywna powłoka zostanie zabita.
Testowane z
zsh
,bash
idash
. Ale co z oldies?B98 sugeruje następującą zmianę, działającą na Mac OS X, z GNU bash 3.2.57 lub Linux z dash:
-
1. inne niż te,
setsid
które wydają się niestandardowe.źródło
kill 0
ostatecznie zabija również wywołującego skrypt. Czy istnieje przenośny sposób na wtłaczanie potoku do własnej grupy procesów?setprgp()
bezsetsid
teraz :-(Jesteś już w pułapce 15 (wersja numeryczna
SIGTERM
, którakill
wysyła, o ile nie podano inaczej), więc powinieneś już iść. To powiedziawszy, jeśli patrzysz na pre-POSIX, pamiętaj, że funkcje powłoki również mogą nie istnieć (pochodzą z powłoki Systemu V).źródło
cat
wyjście. Już trochę eksperymentowałem, ale jak dotąd nie znalazłem niczego, z czego jestem zadowolony.$!
, myślę, że niektóre z tych maszyn, których używam rzadko nie mają kontroli zadań, są$!
powszechnie dostępne?bash
specyficznych rzeczy, które kiedyś zrobiłem, w tym nadużycia-o monitor
. Kiedy o tym pisałem, myślałem o naprawdę pradawnych muszlach (zadziałało w wersji 7). To powiedziawszy, myślę, że możesz zrobić jedną z dwóch innych rzeczy: (1) tło „cokolwiek” iwait $!
, lub (2) również wysłaćSIGCLD
/SIGCHLD
... ale na wystarczająco starych maszynach to drugie nie istnieje lub nie można go przenosić (pierwszym z nich jest System III / V, ten drugi BSD i V7 nie miały żadnego).$!
wraca co najmniej do V7 i na pewno wyprzedza cośsh
takiego, co wiedziało cokolwiek na temat kontroli zadań (tak naprawdę przez długi czas/bin/sh
na BSD nie kontrolował pracy; musiałeś biec,csh
aby ją zdobyć - ale$!
był tam ).$!
.Chociaż coretuils od wersji 7.0 zawiera polecenie limitu czasu, o którym wspomniałeś w niektórych środowiskach, które go nie mają. Na szczęście pixelbeat.org ma napisany skrypt przekroczenia limitu czasu
sh
.Używałem go już kilkakrotnie i działa bardzo dobrze.
http://www.pixelbeat.org/scripts/timeout ( Uwaga: Poniższy skrypt został nieco zmodyfikowany w stosunku do tego na pixelbeat.org, zobacz komentarze poniżej tej odpowiedzi.)
źródło
</dev/stdin
jest zakazem.</dev/tty
umożliwiłby odczyt z terminala, co jest wystarczające dla mojego przypadku użycia.Co z (ab) użyć do tego NC?
Lubić;
Lub zwinięte w jedną linię poleceń;
Ostatnia wersja, choć wyglądająca dziwnie, faktycznie działa, kiedy testuję ją na systemie Linux - moim zdaniem przypuszcza się, że powinna ona działać na każdym systemie, a jeśli nie, to pewna odmiana przekierowania wyjścia może rozwiązać problem przenośności. korzyścią tutaj jest to, że nie są zaangażowane żadne procesy w tle.
źródło
nc
na jakimś starym Uniksie, ani na wielu wbudowanych Linuksach.Innym sposobem uruchomienia potoku we własnej grupie procesów jest uruchomienie
sh -c '....'
w pseudo-terminalu za pomocąscript
polecenia (które domyślnie stosujesetsid
funkcję).źródło
script
na jakimś starym Uniksie, ani na wielu wbudowanych Linuksach.Odpowiedź w https://unix.stackexchange.com/a/18711 jest bardzo miła.
Chciałem osiągnąć podobny wynik, ale bez konieczności jawnego wywoływania powłoki ponownie, ponieważ chciałem wywołać istniejące funkcje powłoki.
Za pomocą bash można wykonać następujące czynności:
Załóżmy, że mam już funkcję powłoki
f
:Wykonując to widzę:
źródło
źródło