# Úvod do UNIXu (SWI095x1h), 10. cvičení, 2005-04-27
# SU1, Malá Strana, MFF UK
#
# Řešení jsou odladěná na FreeBSD 4.11-RELEASE, případné konkrétní
# situace se týkají stroje mail.kolej.
#
# $DevNullCZ: cviceni-10.reseni.txt,v 1.5 2005/04/27 16:23:04 jp Exp $
#

#---------------------------------------------------------------------
# příkaz test
#---------------------------------------------------------------------

(t1) použití, konvence '[', použití s operátory '&&' a '||', rozdíl
mezi '[ $a = b ]' a '[ "$a" = b ]', nutná mezera před ukončovací ']'

(t2) bez použití příkazu 'if' napište test, zda daný soubor existuje
a je to regulární soubor; pokud ano, vypište 'ok', pokud ne, vypište
'failed'

## file=xxx; [ -f "$file" ] && echo ok || echo failed

(t3) obdobně si zkuste otestovat, že zadaný řetězec je prázdný, že
je neprázdný, že zadané číslo je rovno 1 nebo 2 (použijte '-o' v
rámci příkazu test), že daný soubor existuje a není prázdný, ...

## string=xxx; [ -z "$string" ] && echo zero || echo not zero
##
## string=xxx; [ -n "$string" ] && echo not zero || echo zero
##
## n=1; [ "$n" -eq 1 -o "$n" -eq 2 ] && echo ok || echo failed
##
## f=xxx; [ -s "$f" ] && echo non empty || echo empty or nonexistent
##

#---------------------------------------------------------------------
# zpracování řádky (input interpretation)
#---------------------------------------------------------------------

(ii1) základní kroky při zpracování příkazové řádky, rozdíl mezi sh
a bash, ...

# http://www.mpi-sb.mpg.de/~uwe/lehre/unixffb/quoting-guide.html

(ii2) konkrétní kroky při zpracování příkazové řádky:

# cd /etc; configs="*.conf"; ls $configs

## note: zpracování expanzních znaků až po interpretaci proměnných
## je důvod, proč tento příkaz vypisuje všechny konfigurační soubory

(ii3) ,,blank interpretation''. Vysvětlení na konkrétních
příkladech:

# echo `ls`
# echo "`ls`"

odebrání koncových znaků nové řádky:

# echo `echo -e 'a\n\n\n\n'`
# echo "`echo -e 'a\n\n\n\n'`"
# echo "`echo -e 'a\n\n\n\nb'`"

(ii4) zpracování proměnných (není rekurzívní!!!) náhrada příkazů
pomocí ``, zpracování znaků v rámci ``

# rekurzitiva:          a='$b'; b=c; echo $a
# proměnné uvnitř ``:   a=1; echo `a=2; echo $a`

když chci v rámci `` vypsat řetězec "$a":

# note: uvnitř `` se dá použít zpětné lomítko pro escape znaků '$',
# '`' a '\'

## viz 'man bash' (v manu pro /bin/sh to bohužel není, i když to
## platí i pro něj):
##
## When the old-style backquote form of substitution  is  used,  backslash
## retains  its  literal  meaning except when followed by $, `, or \.  The
## first backquote not preceded by a backslash terminates the command sub-
## stitution.   When using the $(command) form, all characters between the
## parentheses make up the command; none are treated specially.

# a=1; echo `a=2; echo \$a`
# a=1; echo `a=2; echo \\$a`

je tedy možné vnořovat znaky zpětných uvozovek:

# echo `echo \`echo x\``

(ii5) do proměnné x uložte obsah souboru, jehož jméno je obsaženo v
souboru y

## echo zzz > z
## echo z > y
## x=`cat \`cat y\``
## zzz

(ii6) bez toho, aniž byste to zkusili, určete jaký výstup budou mít
následující příkazy:

# a=b; b=c; echo $$a
# a=b; b=c; echo \$$a
# a=b; b=c; echo \$\$a
# echo `echo \\\\`
# echo `echo \\\\$`

## zde se nevypíše $ jak by se mohlo čekat, protože `` sice
## ,,vrátí'' řetězec \$, ale v krocích uvedeného www odkazu
## následují už pouze kroky 3-5, tedy znak zpětného lomítka se již
## nebere jako escape znak !!!

(ii7) rozdíl mezi těmito příkazy:

# for i in "`ls`"; do echo $i; done
# for i in "`ls`"; do echo "$i"; done

## uvědomte si, že v obou případech cykly iterují pouze jednou!! Jak
## si to můžete jednoduše ověřit?

#---------------------------------------------------------------------
# eval
#---------------------------------------------------------------------
(e1) motivace, použití, ...

(e2) mějme definici dvou proměnných, a=b; b=c. Vypište obsah
proměnné 'b' pomocí 'eval' a proměnné 'a'.

## eval echo \$$a
##
## klasický quoting se uplatňuje i zde, uvědomte si proto, proč toto
## je rozdíl:
## 
## eval echo '\$$a'

(e3) konkrétní příklad použití eval z praxe. FreeBSD používá pro
definici statického routingu v /etc/rc.conf následující konstrukci,
kde proměnná 'static_routes' určuje existenci dalších proměnných:

# static_routes="vladislavka square cihelna osadni"
# route_vladislavka="82.119.254.128/28 -hopcount 1 82.119.254.2"
# route_square="82.119.254.144/29 -hopcount 1 82.119.254.18"
# route_cihelna="82.119.254.152/30 -hopcount 1 82.119.254.6"
# route_osadni="82.119.254.160/27 -hopcount 1 82.119.254.30"

a výstupem mají být tyto příkazy (stačí je vypsat tak, jak by byly
spuštěny):

route add 82.119.254.128/28 -hopcount 1 82.119.254.2
route add 82.119.254.144/29 -hopcount 1 82.119.254.18
...

Napište kód, který toto zajistí. Pro nastavení proměnných je buď
přímo vložte do svého skriptu, nebo lépe je uložte napr. do souboru
'rc.conf' a ve svém skriptu jej pak načtěte pomocí '. rc.conf'.

## viz přímo na mail.kolej ve startup skriptu /etc/rc.network:
##
## if [ -n "${static_routes}" ]; then
##      for i in ${static_routes}; do
##              eval route_args=\$route_${i}
##              route add ${route_args}
##      done
## fi      

#---------------------------------------------------------------------
# ještě ke quotingu
#---------------------------------------------------------------------
(q1) pozor třeba i při použití SSH, například tyto dva příkazy jsou
velmi rozdílné, přestože se většinou zachovají stejně:

# ssh jp@xxx echo *.conf
# ssh jp@xxx echo \*.conf

#---------------------------------------------------------------------
# while a čtení z pajpy
#---------------------------------------------------------------------
(w1) shell pro každý příkaz v koloně vytváří vlastní subshell; pro
poslední příkaz to většinou platí také, někdy ne. Uživatel se s tím
setká typicky u přesměrování vstupu do složených příkazů:

# a=b; echo c | while read i; do a=$i; echo $a; done; echo $a
# c
# b

Tedy takto nelze obecně získat největší UID ze souboru /etc/passwd:

# max=0; grep -v ^# /etc/passwd | cut -f3 -d: | \
#  while read i; do [ "$i" -gt "$max" ] && max=$i; done; echo $max

Dá se to obejít malým trikem, který nechávám na prostudování doma
(viz 'man sh' a "UNIX FAQ's" part 3):

# max=0
# exec 0>&9
# exec </etc/passwd
#
# while read line; do 
#    expr "$line" : "^#" >/dev/null && continue
#    n=`echo $line | cut -f3 -d:`
#    [ "$n" -gt "$max" ] && max=$n
# done
# 
# echo $max
# exec 9>&0

#---------------------------------------------------------------------
# příklady
#---------------------------------------------------------------------
(a1) napište funkci, která pro zadaný parametr (číslo) určí, zda
běží nebo neběží proces s daným PID. Funkce vrací hodnotu podle
konvence shellu, tedy 0 pokud běží (true), 1 když ne (false).
Použití by pak mělo být možné například takto:

# is_pid_running 1111 && echo ok || echo shit

## is_pid_running ()
## {
##   [ ! "$1" ] && return 1
##   ps -o pid -ax | grep "\<${1}\>" >/dev/null && return 0 || return 1;
## }