Shellové skriptovanie: Asynchrónne spúšťanie príkazov v shelli

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.

2 thoughts on “Shellové skriptovanie: Asynchrónne spúšťanie príkazov v shelli

  1. 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 )…

  2. 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ť:

    • screenom
    • cez nohup [http://en.wikipedia.org/wiki/Nohup]
    • cez at [http://en.wikipedia.org/wiki/At_(Unix)]

Napísať odpoveď pre Stevo Zrušiť odpoveď

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *