2009-03-27 14 views
39

W this answer do innego question, powiedziano mi, żeDlaczego nie mogę używać kontroli zadań w skrypcie bash?

w skryptach nie mają kontroli pracy (i próbuje go włączyć jest głupie)

Jest to pierwszy raz, kiedy "Słyszałem o tym i przeglądałem sekcję bash.info o sterowaniu zadaniami (rozdział 7), nie znajdując żadnej wzmianki o żadnym z tych twierdzeń. [Aktualizacja: Strona człowiek jest trochę lepiej, wymieniając „typowe” użytkowanie, ustawienia domyślne, a zacisk I/O, ale nie prawdziwy powód dlaczego kontrola pracy jest szczególnie niewskazane dla skryptów.]

Więc dlaczego czy praca polegająca na kontroli pracy opartej na skryptach, i co sprawia, że ​​jest to zła praktyka ("głupi")?

Edit: Skrypt w pytaniu rozpoczyna proces tła, rozpoczyna się drugi proces w tle, a następnie próbuje umieścić pierwszy proces z powrotem na planie tak, że ma normalne terminala I/O (jakby uruchomić bezpośrednio) który następnie może zostać przekierowany na adres spoza skryptu. Nie można tego zrobić w procesie w tle.

Jak zauważyłem przez accepted answer do drugiego pytania, istnieją inne skrypty, które rozwiązują ten konkretny problem bez próby kontroli pracy. W porządku. A skandaliczny skrypt używa zakodowanej pracy numer — Oczywiście źle. Ale próbuję zrozumieć, czy kontrola pracy jest zasadniczo skazana na zagładę. nadal wydaje się jak może to mógł praca ...

+0

Dodaj prosty przykład pokazujący, w jaki sposób można znaleźć to, czego nie można łatwo wykonać bez kontroli pracy. – dwc

Odpowiedz

37

Co miał na myśli to, że kontrola zadaniem jest domyślnie wyłączony w trybie nieinteraktywnym (czyli w skrypcie.)

Z bash strona man:

JOB CONTROL 
     Job control refers to the ability to selectively stop (suspend) 
     the execution of processes and continue (resume) their execution at a 
     later point. 
     A user typically employs this facility via an interactive interface 
     supplied jointly by the system’s terminal driver and bash. 

i

set [--abefhkmnptuvxBCHP] [-o option] [arg ...] 
     ... 
     -m  Monitor mode. Job control is enabled. This option is on by 
       default for interactive shells on systems that support it (see 
       JOB CONTROL above). Background processes run in a separate 
       process group and a line containing their exit status is 
       printed upon their completion. 

Kiedy sa id „jest głupie” miał na myśli, że nie tylko:

  1. jest kontrola pracy oznaczało głównie dla ułatwienia kontroli interaktywne (natomiast skrypt może pracować bezpośrednio z PID), ale również
  2. cytuję jego oryginalną odpowiedź , ... polega na tym, że nie uruchomiłeś żadnych innych zadań wcześniej w skrypcie, co jest złym założeniem, aby uczynić. Co jest całkiem poprawne.

UPDATE

W odpowiedzi na Twój komentarz: Tak, nikt nie powstrzyma cię od stosowania kontroli pracy w skrypcie bash - nie ma twarde etui do siłą wyłączaniaset -m (czyli tak, Kontrola pracy ze skryptem będzie działać, jeśli tego chcesz.) Pamiętaj, że w końcu, zwłaszcza w skryptach, zawsze istnieje więcej niż jeden sposób na skórze kota, ale niektóre sposoby są bardziej przenośne, bardziej niezawodne, ułatwiają obsługiwać przypadki błędów, analizować dane wyjściowe itp.

Szczególne okoliczności mogą, ale nie muszą uzasadniać inaczej niż to, co lhunath (i inni użytkownicy) uznają za "najlepsze praktyki".

+2

+1 Dokładnie szczegółowe. Kontrola zadań jest funkcją, która sprawia, że ​​zadania związane z obsługą (interaktywnym) są wygodniejsze. Nie ma powodu, dla którego ktokolwiek mógłby chcieć tego w skryptach, ponieważ można po prostu zachować PIDy procesów w tle i * czekać * lub * zabić * je. – lhunath

+0

Dzięki! Dziwne, że strona man ma lepsze informacje niż plik bash.info. –

+1

OK, otrzymuję *, że numer zakodowanej pracy jest złym pomysłem. Nie ma problemu. Ale słowa takie jak "* domyślnie * dla interaktywnego" i "użytkownik * zwykle * używają" i "oznaczały * głównie * dla" wszystkie wskazują mocno, że jest * jakiś * ezoteryczny przypadek użycia dla kontroli pracy w skrypcie. W przeciwnym razie ustawienie -m powinno zawieść w skryptach. –

1

Bash obsługuje kontrolę zadań, jak mówisz. W pisaniu skryptów powłoki często zakłada się, że nie można polegać na tym, że masz bash, ale masz waniliową powłokę Bourne'a (sh), która historycznie nie miała kontroli pracy.

W dzisiejszych czasach ciężko mi sobie wyobrazić system, w którym szczerze jesteś ograniczony do prawdziwej skorupy Bourne'a. Większość systemów "/bin/sh zostanie połączona z bash. Mimo to jest to możliwe. Jedną rzeczą, jaką można zrobić, to zamiast określania

#!/bin/sh 

można zrobić:

#!/bin/bash 

To i dokumentacja, by jasno skrypt potrzebuje bash.

+0

W systemie Ubuntu/bin/sh nie jest połączony z Bash. Potrzebujesz #!/Bin/bash. – emk

5

Kontrola zadań jest użyteczna tylko w przypadku korzystania z powłoki interaktywnej, tzn. Wiesz, że stdin i stdout są podłączone do urządzenia końcowego (/ dev/pts/* w systemie Linux). W takim przypadku warto mieć coś na pierwszym planie, coś innego w tle itp.

Skrypty z drugiej strony nie mają takiej gwarancji. Skrypty mogą być wykonywane i można je uruchamiać bez dołączonego terminala. W tym przypadku nie ma sensu tworzenie procesów na pierwszym planie lub w tle.

Można jednak uruchamiać inne polecenia nieinteraktywnie w tle (dodając "&" do wiersza poleceń) i przechwytywać ich PID za pomocą $!. Następnie używasz kill, aby je zabić lub zawiesić (symulując Ctrl-C lub Ctrl-Z na terminalu, to powłoka była interaktywna). Możesz także użyć wait (zamiast fg), aby poczekać na zakończenie procesu w tle.

+0

"Fg 1" było przeznaczone specjalnie do wywołania stdin i stdout procesu & d, aby ponownie dołączyć do interaktywnej sesji terminalowej. Osoba wywołująca skrypt (osoba lub inny skrypt) może wtedy zdecydować, czy je przekierować. –

0

Prawdopodobnie o/t ale dość często używam nohup, gdy ssh do serwera na długo działającej pracy, więc jeśli wyloguję się, zadanie wciąż się kończy.

Zastanawiam się, czy ludzie mylą się zatrzymując i rozpoczynając od głównej interaktywnej powłoki i odradzając procesy w tle? Polecenie wait pozwala ci odrodzić się wiele rzeczy, a następnie czekać, aż wszystkie się zakończą, i jak powiedziałem, używam nohup przez cały czas. Jest bardziej skomplikowany i bardzo niedostateczny - sh obsługuje również ten tryb. Zajrzyj do instrukcji.

pan także dostał

kill -STOP pid 

ja dość często, że jeśli chcę, aby zawiesić aktualnie działa sudo, jak w:

kill -STOP $$ 

Ale biada, jeśli masz skoczył do skorupy z edytora - wszystko to po prostu usiądzie.

I mają tendencję do używania pamięciowy -KILL itp ponieważ istnieje niebezpieczeństwo wpisując

kill - 9 pid # note the space 

aw dawnych czasach można czasami przynieść maszynę w dół, ponieważ byłoby to init, zabić!

+0

OT ale interesujące informacje. Bardzo bardzo OT: czy twoje imię jest wymawiane tak samo jak "ryba"? –

+1

Tak, nazywam się Fish i często używam ghoti i zobaczę, czy ktoś zauważy! – Ghoti

0

pracy działają w bashu

Ale, ... trzeba uważać na zrodził personelu jak:

ls -1 /usr/share/doc/ | while read -r doc ; do ... done 

pracy będzie miał inny kontekst na każdej stronie |

pomijając ten może być używany zamiast za chwilę:

for `ls -1 /usr/share/doc` ; do ... done 

ta powinna wykazać, jak korzystać z pracy w skrypcie ... z adnotacją, że moja uwaga jest skomentował ... Real (dunno dlaczego że zachowanie)

#!/bin/bash 


for i in `seq 7` ; do (sleep 100) & done 

jobs 

while [ `jobs | wc -l` -ne 0 ] ; do 

    for jobnr in `jobs | awk '{print $1}' | cut -d\[ -f2- |cut -d\] -f1` ; do 
     kill %$jobnr 
    done 
    #this is REALLY ODD ... but while won't exit without this ... dunno why 
    jobs >/dev/null 2>/dev/null 
done 

sleep 1 
jobs 
27

kontrola Praca z bg i fg jest przydatna tylko w powłokach interakcyjnych. Ale & w połączeniu z wait jest również przydatny w skryptach.

W systemach wieloprocesorowych tworzenie stanowisk w tle może znacznie poprawić wydajność skryptu, np. w skryptach budowania gdzie chcesz rozpocząć co najmniej jeden kompilator na CPU lub obrazów procesowych przy użyciu narzędzi ImageMagick równolegle itp

Poniższy przykład biegnie aż do 8 równolegle gcc do kompilacji wszystkie pliki źródłowe w tablicy:

#!bash 
... 
for ((i = 0, end=${#sourcefiles[@]}; i < end;)); do 
    for ((cpu_num = 0; cpu_num < 8; cpu_num++, i++)); do 
     if ((i < end)); then gcc ${sourcefiles[$i]} & fi 
    done 
    wait 
done 

Nie ma w tym nic "głupiego". Ale będziesz potrzebować polecenia wait, które czeka na wszystkie zadania w tle, zanim skrypt będzie kontynuowany. Identyfikator PID ostatniego zadania w tle jest przechowywany w zmiennej $!, więc możesz także wait ${!}. Zauważ także polecenie nice.

Czasami taki kod jest przydatna w makefile:

buildall: 
    for cpp_file in *.cpp; do gcc -c $$cpp_file & done; wait 

To daje dużo lepszą kontrolę niż make -j.

Należy zauważyć, że & jest terminatorem liniowym, takim jak ; (napisać command& nie command&;).

Mam nadzieję, że to pomoże.

+2

dla czytników: 'wait' pozwala także na wiele pidów, np.'czekaj 3940 4001 4012 4024', ale zaczekaj, aż * wszystkie * z nich zakończą, zanim przejdziesz dalej. – zamnuts

+0

Niedawno skompilowałem pliki w formacie Emacs/Lisp _.el_ do _.elc_ w skrypcie. Po 'wait' wszystko Emacssen skończył, ale nie wszystkie pliki _.elc_ były tam. Ja dodatkowo musiałem na nie czekać, jak w 'while while [[! -e $ plik]]; robić :; done'. Zdarzyło się to pod Windows/Cygwin, ale sądzę, że może się to zdarzyć w każdym systemie plików. Więc poza plikami Makefile, jeśli pliki są wymagane, lepiej nie ufaj 'wait'. –

4

Może być przydatne włączenie sterowania zadaniami w skrypcie w celu ustawienia pułapek na SIGCHLD na . Sekcja JOB CONTROL w podręczniku mówi:

Powłoka uczy się natychmiast po każdym zmianie stanu pracy. Normalnie, bash czeka do momentu, w którym zostanie wyświetlony monit przed zgłoszeniem zmian statusu zadania, tak aby nie zakłócać żadnych innych danych wyjściowych. Jeśli opcja jest włączona opcja -b wbudowanego polecenia set, bash natychmiast zgłasza te zmiany . Każda pułapka na SIGCHLD jest wykonywana dla każdego potomka , które kończy.

(podkreślenie moje)

Weźmy następujący skrypt, jako przykład:

[email protected]:~$ cat children.bash 
#!/bin/bash 

set -m 
count=0 limit=3 
trap 'counter && { job & }' CHLD 
job() { 
    local amount=$((RANDOM % 8)) 
    echo "sleeping $amount seconds" 
    sleep "$amount" 
} 
counter() { 
    ((count++ < limit)) 
} 
counter && { job & } 
wait 
[email protected]:~$ chmod +x children.bash 
[email protected]:~$ ./children.bash 
sleeping 6 seconds 
sleeping 0 seconds 
sleeping 7 seconds 

Uwaga: CHLD wychwytujący wydaje się być uszkodzony jak bash 4,3

W bashie 4.3 możesz użyć 'wait -n', aby osiągnąć to samo, jednak:

[email protected]:~$ cat waitn.bash 
#!/home/dualbus/local/bin/bash 

count=0 limit=3 
trap 'kill "$pid"; exit' INT 
job() { 
    local amount=$((RANDOM % 8)) 
    echo "sleeping $amount seconds" 
    sleep "$amount" 
} 
for ((i=0; i<limit; i++)); do 
    ((i>0)) && wait -n; job & pid=$! 
done 
[email protected]:~$ chmod +x waitn.bash 
[email protected]:~$ ./waitn.bash 
sleeping 3 seconds 
sleeping 0 seconds 
sleeping 5 seconds 

Można argumentować, że istnieją inne sposoby, aby to zrobić w bardziej przenośny sposób, czyli bez CHLD lub czekać -n:

[email protected]:~$ cat portable.sh 
#!/bin/sh 

count=0 limit=3 
trap 'counter && { brand; job & }; wait' USR1 
unset RANDOM; rseed=123459876$$ 
brand() { 
    [ "$rseed" -eq 0 ] && rseed=123459876 
    h=$((rseed/127773)) 
    l=$((rseed % 127773)) 
    rseed=$((16807 * l - 2836 * h)) 
    RANDOM=$((rseed & 32767)) 
} 
job() { 
    amount=$((RANDOM % 8)) 
    echo "sleeping $amount seconds" 
    sleep "$amount" 
    kill -USR1 "$$" 
} 
counter() { 
    [ "$count" -lt "$limit" ]; ret=$? 
    count=$((count+1)) 
    return "$ret" 
} 
counter && { brand; job & } 
wait 
[email protected]:~$ chmod +x portable.sh 
[email protected]:~$ ./portable.sh 
sleeping 2 seconds 
sleeping 5 seconds 
sleeping 6 seconds 

Więc podsumowując, jest ustawiony -m nie użyteczne w skryptach, ponieważ jedyną interesującą cechą skryptów jest możliwość pracy z SIGCHLD. Są też inne sposoby osiągnięcia tego samego celu: albo krótszy (czekaj -n), albo bardziej przenośny (wysyłając sygnały samodzielnie).

Powiązane problemy