Ak ste klasickí windowsáci, čo sa k vzdialenému shellu pripájate cez Putty, a máte pocit, že shell nezvláda multitasking, nie je to pravda. Už od nepamäti máte možnosť spúšťať dlhotrvajúce úlohy asynchrónne, teda na pozadí: to znamená, že po ich spustení nebude shell čakať na dobehnutie, ale rovno vám umožní ďalšiu prácu.
Zoberme si hlúpy príklad, ktorý vygeneruje súbor so stomiliónom náhodných bajtov:
head -c 100000000 < /dev/urandom > random.dat
Beh tohto príkazu chvíľu potrvá nejaký čas, počas ktorého nemôžeme pracovať so shellom. (OK, môžeme, môžeme si otvoriť ďalší terminál / session v Putty.).
Asynchrónne spúšťanie príkazu dosiahneme uvedením ampersandu:
head -c 100000000 < /dev/urandom > random.dat &
Celý príkaz sa spustí v subshelli na pozadí od tejto chvíle sa považuje za úlohu (job). Hlavným viditeľným výsledkom sú dve čísla: prvé udáva číslo úlohy a druhé je číslo procesu, v rámci ktorého úloha beží. Nasledovný výpis indikuje prvú úlohu spustenú na pozadí s ID procesu 11227:
[1] 11227
Po tomto výpise môžeme ihneď ďalej pracovať so shellom.
Ak úloha dobehne, shell nás na to upozorní (nie však hneď, ale až potom, čo doň odošleme prvý príkaz vykonaný po dobehnutí úlohy). Tvar výpisu je:
[1]+ Done head -c 100000000 < /dev/urandom > random.dat
Úloha č. 1 je od tejto chvíle v stave Done
: teda korektne dobehnutá.
Úlohy na pozadí a štandardný výstup
Pozor na to, že úlohy spúšťané asynchrónne zdieľajú terminál s hlavným shellom! Ak úloha zapisuje na štandardný výstup a spustíte ju na pozadí, začne sa jej výstup miešať s tým, čo zadávate v termináli, čo môže spôsobiť značný chaos, ba dokonca až nemožnosť rozumne zadávať príkazy. (V takom prípade môžete skúsiť odstreliť úlohu pomocou Ctrl + C
).
Rada starej matere: príkazy spúšťané na pozadí musia mať presmerovaný štandardný výstup do súboru — tradične do /dev/null
.
V ukážke vyhľadáme na disku všetky súbory s príponou .java
. Štandardný výstup presmerujeme do javafiles.txt
a chybové hlášky (napr. o odopretí prístupu) presmerujeme do čiernej diery /dev/null
:
find / -name *.java 2> /dev/null > javafiles.txt &
Príkazy pre prácu s úlohami na pozadí
Zoznam úloh jobs
Ak chceme získať zoznam úloh, stačí použiť príkaz jobs
. Ukážkovým výstupom je:
[2]- Stopped seq 10000000 > seq.dat
[3]+ Stopped sleep 1
[4] Done seq 10000000 > seq.dat
[5] Running while true; do
V tomto prípade máme rozpracované štyri úlohy, s identifikátormi 2, 3, 4 a 5. Znak +
indikuje implicitnú, resp. naposledy spustenú úlohu (tá má zmysel v príkazoch fg
) a znak -
zase ukazuje na predposlednú úlohu, ktorú sme spustili na pozadí. V pravom stĺpci vidieť príkazy shellu, ktoré zodpovedajú úlohe na pozadí.
Úloha sa môže nachádzať v rozličných stavoch:
- running indikuje aktívnu a bežiacu úlohu.
- done poukazuje na dobehnutú a skončenú úlohu
- stopped označuje úlohu, ktorá bola pozastavená na pozadí, ale možno ju znovu rozbehnúť.
Presun úlohy na popredie fg
Ak chceme presunúť niektorú úlohu na popredie, použijeme fg
(foreground). Bez parametra sa na popredie dostane naposledy spustená úloha (v jobs
označená plusom):
fg
Alternatívne môžeme uviesť aj číslo úlohy:
fg 2
Presun úlohy z popredia na pozadie Ctrl + Z
, bg
Ak spustíme úlohu, ktorá trvá dlhšie než sme pôvodne čakali, môžeme ju z popredia preniesť na pozadie: inými slovami, môžeme ju dodatočne vyhlásiť za asynchrónne spustenú.
Skúsme nasledovný nezmyselný príkaz: v nekonečnom cykle nerobme nič, presnejšie, v každej iterácii cyklu zaspíme na 1 sekundu.
yes 1 | xargs sleep
Príkaz yes 1
začne donekonečna vypisovať
1
1
1
...
Následne pomocou xargs
zoberieme každú jednotku zo štandardného vstupu a použijeme ju ako parameter pre sleep
. Ekvivalentný zápis by bol:
while true; do sleep 1; done
Spusťme túto úlohu klasickým spôsobom… a samozrejme sa nebude diať nič viditeľné. Úloha pobeží na popredí.
Stlačením Ctrl + Z
ju pozastavíme a odsunieme na pozadie. Výpis bude:
^Z
[7]+ Stopped yes 1 | xargs sleep
Úlohe bolo pridelené číslo 7 a bola pozastavená. To isté uvidíme aj vo výpíse jobs
.
Úlohu, ktorá je na pozadí, ale pozastavená, môžeme rozbehnúť pomocou bg
:
bg
Týmto spustíme na pozadí úlohu, ktorá bola naposledy stopnutá. Ak chceme rozbehnúť inú stopnutú úlohu, uvedieme jej číslo ako parameter bg
:
bg 7
Výpisom bude:
[7]+ yes 1 | xargs sleep &
Všimnite si ampersand! Znamená to, že úloha beží na pozadí. Vo výpise jobs
uvidíme:
[7] Running yes 1 | xargs sleep &
Úloha sa odteraz tvári presne tak, ako keby sme ju explicitne spustili na pozadí:
yes 1 | xargs sleep &
Zabitie úlohy kill
Úloha na pozadí beží v samostatnom procese. Ak ju chceme predčasne ukončiť, použijeme starý známy kill
. Nemusíme však pátrať po čísle procesu, stačí vedieť číslo úlohy. Následne sa naň odkážeme cez percento. Spusťme:
kill %7
Zdanlivo sa nič nestalo, ale po vyvolaní jobs
uvidíme:
[7] Terminated yes 1 | xargs sleep
Úloha je v stave terminated
, teda „odstrelená”, čo indikuje jej predčasné ukončenie. Viac sa o ňu nemusíme starať, a dokonca pri ďalšom spustení jobs
ju už neuvidíme.
Užitočné, až na jeden problém… Ako sa po prerušení ssh spojenia dostanem k bežiacim jobom ?
Doteraz som to riešil pomocou screen-u ( prípadne tmux-u )…
Popravde, tam by sa žiadalo napísať, že takto spúšťané príkazy naozaj neprežijú odhlásenie z SSH.
Dá sa to riešiť:
nohup
[http://en.wikipedia.org/wiki/Nohup]at
[http://en.wikipedia.org/wiki/At_(Unix)]