Ruby po kapkách (8.) – slepovací jazyk
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.
Mohlo by vás také zajímat
-
Vlastní web: Jak nainstalovat WordPress?
24. června 2024
Nejnovější
-
Výkonný a kompaktní: ASOME Max Studio s výjimečným poměrem cena/výkon
11. listopadu 2024 -
Šokující data od Microsoftu: Kyberútoky rostou o stovky procent!
8. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024 -
OpenAI představilo novou funkci ChatGPT Search
6. listopadu 2024
foo
Lis 26, 2009 v 23:22Mož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