Menu

Třetí cvičení (8. 3.)

Oprávnění

Oprávnění jste probrali na přednášce. Pro připomenutí se můžete podívat na jejich přehled.

Psaní skriptů

Ve chvíli, kdy by náš příkaz byl příliš komplikovaný či příliš rozsáhlý na to, abychom jej psali na příkazovou řádku, můžeme místo toho vytvořit shellový skript. V něm jsou zapsány jednotlivé příkazy, tak jako bychom je psali jeden po druhém do příkazové řádky. Pokud je každý příkaz na novém řádku, nemusíme je oddělovat středníky. První řádek skriptu je speciální. Jmenuje se shabang (též she-bang či sh-bang) a vypadá takto: #!/bin/sh. Tento řádek zodpovídá za to, že se soubor v případě spuštění bude interpretovat jako shellovský skript.

Komentáře můžeme zapisovat pomocí # – cokoliv za tímto znakem až do konce řádku je považováno za komentář. Skript můžeme spustit pomocí sh skript.sh, případně, pokud je spustitelný, pomocí ./skript.sh.

Proměnné

V příkazové řádce i v shellových skriptech můžete používat proměnné, podobně jako v jiných programovacích jazycích. Nutno podotknout, že pouze skalární proměnné, tedy žádné pole, objekty apod. Obsahem proměnné v shellu je vždy řetězec. Shell nahradí každý výskyt $xyz v příkazové řádce nebo v shellovém skriptu obsahem proměnné xyz. Pozor! Jedná se opravdu o jednoduché nahrazení. Pokud se v proměnné nachází mezera a my ji předáme příkazu, ten si ji vyloží jako dva různé parametry. Pokud tomu chceme zabránit, můžeme uzavřít proměnnou do uvozovek "$xyz", které potlačí speciální význam metaznaků jako je mezera.

Naopak, pokud chceme napsat znak $, musíme před něj napsat \ nebo jej uzavřít do apostrofů. Pokud potřebujeme zapsat proměnnou doprostřed řetězce, můžeme použít syntaxi ${xyz}. Pokud zapíšeme proměnnou, která ještě nebyla definována, shell nevyhodí chybu, ale nahradí ji prázdným řetězcem!

Do proměnné přiřadíme pomocí znaku =. Mezery před ani za = nejsou přípustné, přiřazovanou hodnotu ale můžeme uzavřít do uvozovek či apostrofů.

xyz="ahoj světe"
echo $xyz

Existuje velká hromada předdefinovaných globálních proměnných. Postupně budeme jejich seznam rozšiřovat, zatím si vystačíme s těmito:

$0      ... jméno běžícího shellového skriptu
$n      ... n-tý parametr předaný na příkazové řádce; 
            n>9 je třeba uzavřít do {}
$@      ... všechny parametry předané na příkazové řádce
$$      ... číslo běžícího procesu (shellu, nebo shellového skriptu) – PID
$USER   ... přihlášený uživatel
$HOME   ... domovský adresář
$PWD    ... aktuální adresář
$PATH   ... cesta, ve které se vyhledávají spustitelné soubory
$RANDOM ... náhodné číslo od 0 do 32767

POSIX specifikuje pouze jednočíslicové parametry $n. Podpora pro vyšší čísla je však široce implementována. Později se dozvíme, jak snadno pracovat s libovolným počtem parametrů, aniž bychom museli přistupovat ke každému zvlášť.

Substituce příkazů

Někdy by se nám hodilo uložit do proměnné výstup nějakého příkazu. K tomu nám pomůže substituce příkazů. Kdykoliv uvidí shell ve skriptu $(příkaz), příkaz provede a jeho výstup dosadí na místo uvedeného výrazu. Stejného výsledku jde docílit uzavřením příkazu do zpětných apostrofů: `příkaz`. Substituce se provede i pokud je výraz uzavřen v uvozovkách, ne však v apostrofech.

pwd=$(pwd)
    ... uloží aktuální adresář do proměnné $pwd
echo "Ahoj, $(whoami | cut -c1 | tr 'a-z' 'A-Z')."
    ... pozdraví uživatele iniciálou jeho loginu

Příklady

Zobrazit řešení

Pozor! Veškeré příklady týkající se oprávnění provádějte v adresáři /tmp nebo v jeho podadresářích. Ve vašem domovském adresáři nebudou kvůli afs oprávnění fungovat tak, jak očekáváte. Spustitelné skripty ale můžete vytvářet i ve svém domovském adresáři.

  1. Vytvořte tři soubory s jednoduchou databází. Soubor knihy:
    fhdn;Frank Herbert;Duna
    grrm;George R. R. Martin;Hra o trůny
    tpbk;Terry Pratchett;Barva kouzel
    tplf;Terry Pratchett;Lehké fantastično
    

    A soubor vypujcky:

    fhdn;pk;12.12.2014
    grrm;mb;3.1.2015
    tpbk;jm;24.9.2014
    

    A soubor ctenari:

    jm;Jan Musílek
    mb;Martin Babka
    mh;Martin Hykl
    pk;Petr Kučera
    
  2. Vypište pro každou půjčenou knihu jméno autora, jméno knihy a datum výpůjčky (v tomto pořadí).
  3. join -t\; knihy vypujcky | cut -d\; -f 2,3,5
    
  4. Vypište pro každou půjčenou knihu jméno knihy a jméno čtenáře, který ji má vypůjčenou (v tomto pořadí).
  5. join -t\; knihy vypujcky | sort -t\; -k 4 | 
      join -t\; -1 4 -2 1 - ctenari | cut -d\; -f 4,6
    
  6. Vytvořte adresář a v něm soubor. Nastavte jejich práva tak, aby nikdo (včetně vlastníka) nemohl vypsat obsah adresáře, ale aby všichni mohli vypsat obsah souboru (pokud znají jeho název).
    mkdir adresar
    touch adresar/soubor
    chmod 311 adresar
    chmod a+r adresar/soubor
    
  7. Prozkoumejte práva adresáře /tmp, vyjádřete jeho práva v číselném zápisu a řekněte co by platilo pro soubory uvnitř, kdyby jeho práva byla nastavena na 777.
    Práva v číselném zápisu jsou 1777 (je nastaven sticky bit). Pokud by byla práva 777, kdokoliv by mohl mazat soubory uvnitř adresáře, i když není jejich vlastníkem.
  8. Napište skript, který vypíše Hello, world! a nastavte mu právo na spuštění.
    #!/bin/sh
    echo Hello, world!
    
    chmod a+x helloworld.sh
    
    • Připojte se všichni na stejný stroj pomocí ssh login@u-pl0.ms.mff.cuni.cz.
    • Vytvořte adresář /tmp/cv3-vas_login a přesuňte se do něj.
    • Vytvořte soubor databaze a nastavte mu právo pro čtení a zápis ze strany vlastníka. Ostatní nesmí mít k tomuto souboru žádná práva (aby si nepřečetli tajná data). Poté do něj zapište na první tři řádky nějaké obecné známé informace, na další řádky poté tajná, citlivá data.
    • Poté vytvořte skript ctecka.sh, který vypíše první tři řádky souboru databaze (tedy řádky s veřejně přístupnými informacemi). Nastavte práva tak, aby jej mohl spustit kdokoliv a aby se jeho prostřednictvím skutečně dostal k prvním třem řádkům souboru databaze (ale ne k celému jeho obsahu).
    • Stáhněte si a zkompilujte skript ctecka.c pomocí následujících příkazů:
      curl "http://kam.mff.cuni.cz/~stinovlas/unix/materialy/ctecka.c" 
           > ctecka.c
      gcc ctecka.c -o ctecka
      
      Nastavte binárce ctecka stejná práva jako skriptu ctecka.sh.
    • Ověřte si navzájem, že se můžete dostat k veřejným datům ostatních, ale k tajným ne (jak pomocí ctecka.sh, tak pomocí ctecka.

    touch databaze
    chmod 600 databaze
    
    cat > ctecka.sh
    #!/bin/sh
    head -n3 databaze
    Ctrl-D
    
    chmod 4755 ctecka.sh
    

    Klíčem je tedy nastavení setuid bitu. Postup výše by měl fungovat, ale nefunguje. Je tomu tak proto, že na Linuxových systémech je nastavení setuid bitu na shellovém skriptu považováno za bezpečnostní riziko a proto je ignorováno. Kdybyste místo toho vytvořili zdrojový kód v jazyce C, který by také vypisoval první tři řádky databaze, ten přeložili a nastavili mu setuid bit, vše by fungovalo jak má (vyzkoušeli jsme si na cvičení).

  9. Když už jste všichni přihlášeni na stejný stroj, vypište seznam přihlášených uživatlů pomocí who a pomocí w (to není v POSIXu). Zjistěte jaké jsou mezi nimi rozdíly.
    Oba příkazy vypisují seznam přihlášených uživatelů. who vypisuje odkud je uživatel přihlášen, w co v danou chvíli dělá.
  10. Pokecejte si s kamarádem pomocí příkazu write login.
    talk ani write v labu nefungovaly, příkaz jsme přeskočili
  11. Napište skript, který vás pozdraví – vypíše Ahoj login, dáš si nápoj? kde za login dosaďte uživatelské jméno a za nápoj první parametr zadaný skriptu na příkazové řádce.
    #!/bin/sh
    echo "Ahoj $USER, dáš si $1?"
    
    Nebo s použitím printf:
    #!/bin/sh
    printf "Ahoj %s, dáš si %s?\n" "$USER" "$1"
    

Příklady pro pokročilé

  1. Napište skript, který s vámi bude hrát hádání čísel – bude si myslet náhodné číslo a vždy když zadáte svůj tip, tak vám napoví zda je menší nebo větší. Pokud se trefíte, tak vám pogratuluje a skončí. Náhodné číslo můžete získat např. z proměnné $RANDOM.