Reklama

zonerbooks.cz | zoner.cz | czechia.com | regzone.cz | inshop.cz | inmail.cz | zonerpress.cz | zonerantivirus.com | zonerama.cz

interval.cz

Ruby po kapkách (8.) - slepovací jazyk

30. 04. 2009 | Dalibor Šrámek | Ruby | Komentáře: 1

V minulém díle jsme si ukázali, jak vytvořit jednoduchý program. Mohli bychom říct skript, protože Ruby nám nevnucuje žádnou povinnou strukturu či náležitosti. V zásadě stačí napsat několik volání metod za sebe a jednoduchý program je hotov. Toto je záměr návrhu, protože Matz chtěl vytvořit jazyk stejně silný a vhodný pro použití systémovými administrátory jako je jazyk Perl. Pojďme se podívat, na některé typické vlastnosti, které administrátor může v denní praxi potřebovat, a jak je v Ruby používat.

Jednou z možností je používat Ruby jako takzvaný "glue" (doslova "lepidlo") jazyk. To může znamenat třeba skript, který postupně spouští různé další programy a skripty a zajišťuje jejich vzájemnou komunikaci a součinnost. Jako jednoduchý příklad si představme, že máme stroj s nějakým unixovým operačním systémem a potřebujeme ukončit procesy určitého uživatele. Náš OS k tomu nemá vhodný příkaz, ale zato máme nainstalované Ruby. K dispozici ovšem máme standardní příkaz ps, který vypisuje seznam souborů, a příkaz kill, který danému procesu zasílá (bez dalších parametrů) signál TERM. K příkazu ps použijeme doplňkové parametry, abychom získali výpis všech procesů ve formátu se jménem uživatele. Parametrem příkazu kill bude identifikátor procesu - PID. Zkrácená ukázka následuje.

ara:~$ ps -jax
USER   PID   PPID   PGID  SID   JOBC  STAT  code  TIME     COMMAND
root   645   1      645   645   0     Is    ??    0:00.03  /sbin/devd
root   662   1      662   662   0     Ss    ??    0:19.25  /sbin/routed -q
root   717   1      717   717   0     Ss    ??    0:26.76  /usr/sbin/syslogd -l /v
pgsql  963   1      963   963   0     Ss    ??    12:49.11 /usr/local/bin/postgres
dali   25462 25460  25462 25462 0     Ss    p0    0:00.04  -bash (bash)
dali   25478 25462  25478 25462 1     R+    p0    0:00.01  ps -jax

Prvním úkolem bude dostat výpis z ps do Ruby skriptu, abychom s ním mohli dále pracovat. Jako správní administrátoři použijeme způsob, který je nejjednodušší a nejvíce nám šetří prsty. Vypadat by to mohlo takhle.

ps = `ps -jxa`
puts ps.class

Řetězec v obrácených apostrofech je interpretem použit jako příkaz, který je spuštěn v příslušném příkazovém procesoru (shellu) v rámci OS (funguje to i na MS Windows, kde ale není k dispozici příkaz ps). Textový výstup spuštěného procesu je interpretem zachycen a uložen jako objekt String (což se dozvíte, když uvedený fragment kódu spustíte). Referenci na objekt máme v proměnné ps. Nyní potřebujeme procházet řetězec po jednotlivých řádcích a na každém řádku nás zajímá první a druhý sloupeček. Skript upravíme například následovně.

ps = `ps -jxa`
ps.each_line do |line|
  fields = line.squeeze(' ').split(' ').first(2)
  puts fields.join(', ')
end

Využíváme zde povětšinou technik, které se objevily v minulých dílech. Nový je iterátor each_line třídy String, který (jak již název napovídá) předává do bloku postupně každý řádek. Ten nejdříve zpracujeme metodou squeeze, která pro každý zadaný znak nahradí v řetězci násobný výskyt pouze jedním znakem. V našem případě tak odstraníme nadbytečné mezery. Navazuje rozdělení řetězce na pole a vybrání prvních dvou prvků tohoto pole. Výsledek si pro kontrolu vypíšeme (ukázka je opět krácena).

ara:~$ ruby killuser.rb
USER, PID
root, 645
root, 662

Jméno uživatele, jehož procesy chceme ukončit, předáme skriptu jako parametr příkazové řádky. K dispozici ho budeme mít jako první prvek (s indexem 0) speciálního pole ARGV. Pro řádky, kde první pole odpovídá zadanému jménu, spustíme příkaz kill. Tentokrát k tomu použijeme metodu system, která opět předává parametry ke spuštění shellu, ale nezachycuje výstup.

ps = `ps -jxa`
ps.each_line do |line|
  fields = line.squeeze(' ').split(' ').first(2)
  if fields[0] == ARGV[0]
    puts "Budu volat kill #{fields[1]}"
    system("kill", fields[1].to_s)
  end
end

Pokud nejste přihlášeni jako uživatel root, můžete skript bez obav vyzkoušet se jménem nějakého jiného uživatele. Díky omezením v právu zasílat signály procesům skončí spuštěné příkazy chybou. Namísto spouštění externího příkazu kill bychom mohli použít knihovní metodu stejného jména: Process.kill("SIGTERM", pid), kde bychom v proměnné pid měli PID procesu. Malinko předběhneme, abychom si řekli, že Process je v tomto případě název modulu, v němž si můžete vyhledat v dokumentaci různé další metody související s procesy. Prozatím se ještě podívejme na to, že metoda system vrací pravdivostní hodnotu podle toho, zda se zadaný příkaz podaří úspěšně spustit a zda skončí úspěšně (s návratovým kódem 0).

irb(main):001:0> system('echo', 'Hello')
Hello
=> true
irb(main):002:0> system('neexistuje', 'Hello')
=> false

Přímo PID naposledy spuštěného externího procesu a jeho návratový kód najdeme v globální proměnné $?. Jako příklad této funkcionality si udělejme malý skript, který vypíše hlášku v případě nedostupnosti daného počítače. Dostupnost budeme testovat příkazem ping s parametrem, který stanoví, že se bude testovat pouze jedním ICMP paketem.

available = system("ping -c1 #{ARGV[0]} > /dev/null 2>&1")
if available
  puts 'Dostupný'
else
  puts 'Nedostupný'
end

V tomto případě je parametr metodě system předán v jednom kusu s interpolací názvu stroje, protože jinak by kvůli způsobu zpracování parametrů interpretem neprošlo přidané přesměrování výstupu příkazu ping. Tento výstup nás nezajímá, chceme pouze návratový kód. Použití může vypadat takto.

ara:~$ ruby rping.rb localhost
Dostupný
ara:~$ ruby rping.rb hurvinek
Nedostupný

Poněkud složitějším příkladem by mohlo být čekání na to, až bude daný stroj dostupný a vyvolání nějaké akce, jakmile se to stane. Jednoduchá verze (a v určitém ohledu chybná!) takového skriptu je zde.

loop do
  available = system("ping -c1 #{ARGV[0]} > /dev/null 2>&1")
  if available
    puts 'Stroj je dostupný'
    # akce
    break
  end
  sleep 60
end

Tento program běží po spuštění v nekonečné smyčce dané metodou loop a blokem, který ji předáváme. Pokud dojde k tomu, že je stroj dostupný, vykoná se požadovaná akce a smyčka se opustí příkazem break. V opačném případě se čeká zadaný počet vteřin pomocí volání metody sleep a následuje další cyklus. V tomto jednoduchém návrhu se ale skýtá potenciální nekonečný běh programu. Nerozeznáváme totiž situaci, kdy spuštění ping skončí chybou z nějakého jiného důvodu, než že cílový stroj je nedostupný. Například, pokud se nepodaří převést zadané doménové jméno na IP adresu, bude v proměnné available stále hodnota false a program nikdy neskončí. Zde by nám pomohlo další testování proměnné $?. Návratové kódy příkazu ping nalezneme v dokumentaci nebo si je otestujeme.

irb(main):002:0> system('ping -c1 localhost')
PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 codel=64 time=0.119 ms
--- localhost ping statistics ---
1 packets transmicodeed, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.119/0.119/0.119/0.000 ms
=> true
irb(main):003:0> $?
=> #<Process::Status: pid=26444,exited(0)>
irb(main):004:0> system('ping -c1 x')
ping: cannot resolve x: Unknown host
=> false
irb(main):005:0> $?
=> #<Process::Status: pid=26446,exited(68)>
irb(main):006:0> system('ping -c1 hurvinek')
PING hurvinek.doma (192.168.1.23): 56 data bytes
--- hurvinek.doma ping statistics ---
1 packets transmicodeed, 0 packets received, 100.0% packet loss
=> false
irb(main):007:0> $?
=> #<Process::Status: pid=26449,exited(2)>

Čili na mém počítači je kód 0 v případě dostupnosti cílového počítače, 2 v případě nedostupnosti a 68 v případě nemožnosti převést doménové jméno. V proměnné je objekt. Pokud se chceme dostat skutečně jen k číslu návratového kódu, musíme použít $?.exitstatus. Doplnění kontroly na návratový kód ponecháme jako cvičení.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.


Reklama


Další aktuální články na interval.cz

Tematicky související články

Dejte vědět i ostatním o článku

Komentáře ke článku

Přidat nový komentář

foo

Autor komentáře: foo

Datum vložení: 26. Listopad 2009, 23:22:33

Možná to autor někde v předchozím dílu napsal, ale opakování matka moudrosti, takže si pamatujte, že string v 'apostrofech' se bere jako jednoduchý řetězec a jsou povoleny maximálně zpětná lomítka, zatímco řetězce v "uvozovkách" jsou určeny k dalšímu zpracování tj. #{VAR}..

Já jsem samozřejmě použil system('ping -n 1 #{ARGV[0]} > NUL') ;-D

Zpět na začátek komentářů | Zpět na začátek článku

Přidat nový komentář

Jméno a e-mail jsou nepovinné. Příspěvky obsahující odkaz jsou moderovány.

Zoner AntiVirus Free pro Android
zabezpečte si svůj smartphone, zdarma
Profesionální eshop Zoner inShop od 990 Kč.
Reklama
Reklama

Syndikace

hledáme nové autory | redakce interval.cz | reklama na interval.cz

© ZONER software, a.s., všechna práva vyhrazena, interval.cz dodržuje právní předpisy o ochraně osobních údajů. Powered by WordPress.