Základní informace
- Cvičící: Petr Kučera <kucerap@ktiml.mff.cuni.cz>, místnost S304
- Termín: Úterý 12:20 -- 13:50 SU2
Užitečné odkazy
- Stránky k předmětu
- Lokální kopie poslední specifikace UNIXu
- Stránky se specifikací UNIXu
- stránky UNIXové laboratoře na Malé Straně
- stránka o právech v u-labech
- Shell script checker
Zápočet
Po každém cvičení zadám domácí úkoly, jejichž řešení budu očekávat do začátku dalšího cvičení. Pokud na konci budete mít alespoň 2/3 bodů z těchto úkolů, tak dostanete zápočet. Pokud budete mít alespoň 1/3 bodů (ale méně než 2/3) z těchto úkolů, budete muset k získání zápočtu vypracovat ještě pár domácích úkolů navíc, které vám zadám na konec (jejich množství a obtížnost bude přímo úměrné chybějícímu počtu bodů). Za úkoly z každého cvičení bude možno dostat 3 body, i když ze začátku bude pár jednodušších úkolů a ke konci bude třeba jen jeden, ale těžší. Kdo získá méně než 1/3 bodů, zápočet ode mne nedostane.
V úkolech je možné používat jen prostředky probrané do cvičení, ke kterému se vztahují (včetně). Pokud si všimnu, že někdo odevzdal kopii jiného řešení, nedostane za tento úkol žádné body, stane-li se to podruhé, nedostane ode mne zápočet.
Úkoly mi odevzdávejte e-mailem, nejpozději do začátku následujícího cvičení. Zadání úkolů jsou napsána vždy u odpovídajícího cvičení (tj. aktuální úkoly jsou vždy dole na stránce).
Aktuální počet získaných bodů si můžete průběžně kontrolovat v následující tabulce. Aby se váš záznam v tabulce objevil, napište mi přezdívku, kterou zde chcete mít uvedenou.
Přezdívka | 1. cvičení | 2. cvičení | 3. cvičení | 4. cvičení | 5. cvičení | 6. cvičení | 7. cvičení | 8. cvičení | 9. cvičení | 10. cvičení | 11. cvičení | 12. cvičení | 13. cvičení | Doplňkové | Součet (akt. limit: 26) |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Aspect | 3 | 3 | 3 | 2 | 2 | 2 | 2 | 2 | 3 | 3 | 2 | 27 | |||
Asterix | 2,5 | 3 | 2,5 | 3 | 2 | 1,5 | 3 | 3 | 1 | 1,5 | 3 | 1 | 27 | ||
Aurinko | 3 | 3 | 3 | 2 | 2 | 1 | 1 | 0 | 15 | ||||||
cervenej_teletubbies | 3 | 3 | 3 | 3 | 1 | 2 | 3 | 3 | 2 | 1 | 3 | 27 | |||
cvrček | 3 | 3 | 3 | 2 | 3 | 2 | 16 | ||||||||
joldway | 3 | 3 | 3 | 3 | 2 | 2 | 2 | 3 | 1 | 2 | 3 | 27 | |||
Kaťa | 2,5 | 3 | 3 | 1,5 | 2,5 | 1,5 | 1 | 1 | 2 | 1,5 | 1,5 | 6 | 27 | ||
Kuba | 2,5 | 3 | 3 | 3 | 3 | 2,5 | 3 | 2,5 | 1 | 2 | 3 | 28,5 | |||
marzi | 3 | 3 | 3 | 3 | 3 | 1,5 | 2 | 18,5 | |||||||
ninja_in_pyjama | 3 | 3 | 3 | 3 | 12 | ||||||||||
Obelix | 3 | 2,5 | 2,5 | 2,5 | 1 | 1,5 | 2 | 15 | |||||||
Pepa | 2,5 | 3 | 3 | 3 | 3 | 1,5 | 3 | 3 | 3 | 2 | 3 | 3 | 33 | ||
Petr | 3 | 2,5 | 3 | 3 | 3 | 3 | 2 | 3 | 3 | 2 | 2,5 | 30 | |||
princesslea | 2,5 | 3 | 2,5 | 2 | 1 | 1,5 | 2 | 14,5 | |||||||
R | 3 | 3 | 3 | 3 | 1 | 2,5 | 3 | 2,5 | 2 | 2 | 3 | 3 | 31 | ||
ufon | 3 | 3 | 2 | 2 | 2 | 1,5 | 3 | 1 | 3 | 20,5 |
Některé vlastnosti u-labů
Je dobré znát některé vlastnosti UNIXového labu na Malé Straně. Doporučuji proto prohlédnout si stránky UNIXové laboratoře na Malé Straně. Zmíním alespoň ty rozdíly, které mi připadají nejznatelnější:
- Domovské adresáře jsou umístěny AFS filesystému, kde nefungují standardní UNIXová práva (viz výše uvedená stránka, případně stránka o právech v u-labech)
- Pokud si tedy chcete zkoušet příkaz chmod a věci související se
standardním UNIXovým systémem práv, čiňte tak v adresáři
/tmp
na tom kterém stroji, tam fungují tím standardním způsobem. /etc/passwd
neobsahuje informace o vašich uživatelských účtech, protože ty jsou skladovány v dynamické databázi jinde a jinak, pokud potřebujete informace o uživatelských účtech ve formátu/etc/passwd
, dostanete je příkazemgetent
následovně:
getent passwd
případně můžete přidat parametr specifikující, co vás zajímá, viz téžgetent --help
.- Totéž platí i pro
/etc/group
agetent group
. - Příkaz
find
si s filesystémem afs (nejspíš stále) nerozumí, nepoužívejte jej proto raději na síťových discích. Hledat pomocífind
můžete v lokálních adresářích jako/tmp, /usr, /etc
a podobně, ale raději ne ve vašem domovském adresáři. (Výsledek vyhledávání tam nelze zaručit.)
Obsah cvičení
1. cvičení (17. února 2015)
Základní příkazy, ovládání shellu.
Trochu podrobněji:
- Editace příkazové řádky: Ctrl-a, Ctrl-e, Ctrl-h, Ctrl-r, kurzorové šipky i nahoru a dolů, Ctrl-d, Ctrl-k (smaže text od pozice kurzoru do konce řádku).
-
Příkazy:
man, ls, cd, pwd, cat, more, less
(není standardní), head, tail, wc, mkdir, rmdir, cp, mv, rm, echo.
- Shellová expanze: *, ?, [výčet], [!výčet], apostrofy a uvozovky, soubory s názvy začínajícími tečkou.
Příklady:
- Ve svém domovském adresáři vytvořte adresář ADR
- Do tohoto adresáře okopírujte soubory z adresáře /usr/include, jejichž názvy začínají znakem m, končí příponou .h a kromě toho v názvu obsahují číslici.
- V adresáři ADR vytvořte podadresář PODADRESAR.
- Prvních pět řádek z každého z okopírovaných souborů uložte do souboru ~/ADR/PODADRESAR/prvnichpet.
- Poslední řádky souborů v ~/ADR uložte do souboru ~/ADR/PODADRESAR/posledni.
- Soubory v ~/ADR/PODADRESAR spojte do jednoho souboru ~/ADR/PODADRESAR/prvniaposledni.
- Smažte soubory v ~/ADR/PODADRESAR kromě prvniaposledni.
- Do souboru ~/ADR/PODADRESAR/pocet uložte počet souborů a adresářů v ~/ADR.
- Vypište dlouhé informace o adresáři ~/ADR/PODADRESAR, ne jeho obsah.
- Vymažte obsah souboru ~/ADR/PODADRESAR/prvniaposledni bez toho, abyste smazali tento soubor.
- Do souboru ~/ADR/PODADRESAR/prvniaposledni přidejte řádku, jež obsahuje jen znak hvězdičky, tj *.
- Smažte ~/ADR i se všemi soubory a podadresáři v něm umístěnými.
-
(Ukázat řešení předchozích příkladů)
#!/bin/sh
# Ve svém domovském adresáři vytvořte adresář ADR
mkdir ~/ADR
# Do tohoto adresáře okopírujte soubory z adresáře
# /usr/include, jejichž názvy začínají znakem 'm', končí
# příponou '.h' a kromě toho v názvu obsahují číslici.
cp /usr/include/m*[0-9]*.h ~/ADR
# V adresáři ADR vytvořte podadresář PODADRESAR.
mkdir ~/ADR/PODADRESAR
# Prvních pět řádek z každého z okopírovaných souborů uložte do
# souboru ~/ADR/PODADRESAR/prvnichpet.
head -n 5 ~/ADR/m*[0-9]*.h >~/ADR/PODADRESAR/prvnichpet
# Poslední řádky souborů v ~/ADR uložte do souboru
# ~/ADR/PODADRESAR/posledni.
tail -n 1 ~/ADR/m*[0-9]*.h >~/ADR/PODADRESAR/posledni
# Soubory v ~/ADR/PODADRESAR spojte do jednoho souboru
# ~/ADR/PODADRESAR/prvniaposledni.
cat ~/ADR/PODADRESAR/* >~/ADR/PODADRESAR/prvniaposledni
# Smažte soubory v ~/ADR/PODADRESAR kromě prvniaposledni.
rm ~/ADR/PODADRESAR/prvnichpet
rm ~/ADR/PODADRESAR/posledni
# Do souboru ~/ADR/PODADRESAR/pocet uložte počet souborů a
# adresářů v ~/ADR.
ls ~/ADR | wc -l >~/ADR/PODADRESAR/pocet
# Vypište dlouhé informace o adresáři ~/ADR/PODADRESAR,
# ne jeho obsah.
ls -ld ~/ADR/PODADRESAR
# Vymažte obsah souboru ~/ADR/PODADRESAR/prvniaposledni bez
# toho, abyste smazali tento soubor.
cp /dev/null ~/ADR/PODADRESAR/prvniaposledni
# Do souboru ~/ADR/PODADRESAR/prvniaposledni přidejte řádku, jež obsahuje
# jen znak hvězdičky, tj *.
echo '*' >>~/ADR/PODADRESAR/prvniaposledni
# Smažte ~/ADR i se všemi soubory a podadresáři v něm umístěnými.
rm -r ~/ADR
Domácí úkoly:
- Napište příkaz, který vypíše všechny názvy souborů ve vašem domovském adresáři, které začínají tečkou „.“ a obsahují alespoň 2 znaky. [1 bod]
- Napište příkaz, který vypíše poslední řádku z každého souboru v adresáři /usr/include, jehož název končí „.h“. [1 bod]
- Napište příkaz, kterým okopírujete všechny soubory z adresáře /usr/include/, jejichž jména nezačínají číslicí, ale číslici v názvu obsahují, do vašeho domovského adresáře. [1 bod]
2. cvičení (24. února 2015)
Ještě základní příkazy a ovládání shellu.
Trochu podrobněji:
- Příkazy:
date
,tee
,touch
,ssh
,scp
. - Přesměrování a roury: <, >,
>>, 2>, 2>>, |,
/dev/null
.
Příklady:
- Dokončete příklady z minulého týdne.
- Vypište řádky číslo 11-20 ze souboru
/etc/passwd
.
(Ukázat řešení)#!/bin/sh
head -n20 /etc/passwd | tail -n10 - Vytvořte soubor se jménem „-f“, který bude obsahovat jednu řádku s
aktuálním datem. Poté tento soubor smažte.
(Ukázat řešení)#!/bin/sh
# Vytvoření souboru -f s datem:
date >-f
# Smazání souboru -f:
rm -- -f - Vytvořte soubor „pevne datum“ (tj.
„pevne<mezera>datum“) jehož čas poslední modifikace
bude nastaven na 13:30 1.2. 2009.
(Ukázat řešení)#!/bin/sh
touch -t 200902011330 "pevne datum"
# nebo
touch -d "2009-02-01 13:30:00" "pevne datum" - Do svého domovského adresáře okopírujte soubor
/etc/passwd, kopie tohoto souboru se bude jmenovat
ucty a bude mít čas poslední modifikace shodný s časem
poslední modifikace /etc/passwd.
(Ukázat řešení)#!/bin/sh
cp -p /etc/passwd ucty - Nastavte datum poslední modifikace souboru ucty (ve
vašem domovském adresáři) na čas
poslední modifikace souboru /etc/group.
(Ukázat řešení)#!/bin/sh
touch -r /etc/group ucty - Napište příkaz, který vypíše obsah adresáře /usr/bin podle jmen sestupně podle abecedy
do souborů
bina a binb a
navíc jej vypíše na obrazovku stránkovaně (pomocí less nebo
more)
(Ukázat řešení)#!/bin/sh
ls -r /usr/bin | tee bina binb | more - Rozmyslete si, co se děje při následujících příkazech:
mv soubor /dev/null
cp soubor /dev/null
cat soubor >/dev/null
- Vypište deset největších souborů a pak deset nejmenších souborů v
adresáři
/etc
(Ukázat řešení)#!/bin/sh
ls -S /etc | head
ls -rS /etc | head - V adresáři
/usr/bin
najděte soubor, který byl modifikován naposledy.
(Ukázat řešení)#!/bin/sh
# V adresáři /usr/bin najděte soubor, který byl modifikován
# naposledy.
ls -lrt /usr/bin | head -n 1
Domácí úkoly:
- Vypište počet skupin v systému (řádků v /etc/group) [1 bod]
- Napište příkaz, kterým nastavíte datum a čas poslední modifikace souboru na 11.3.1993 15:04. [1 bod]
-
Vypište jméno druhého největšího souboru v adresáři
/usr/bin
. [1 bod]
3. cvičení (7. března 2014)
Práva a jednoduché utility.
Příkazy:
who
,
who am i
, id
, mesg
, write
,
talk
, mail
(mailx
),
last
(tento příkaz není ve specifikaci a není proto
standardní)
chmod
, chgrp
, chown
, file
,
df
, du
, umask
.
Pozor, na ulabech
zkoušejte práva výhradně v adresáři /tmp
, na ostatních je
systém afs a práva tam fungují úplně jinak.
Příklady:
- Napište příkaz, který vypíše vaše aktuální UID.
(Ukázat řešení)#!/bin/sh
id -u - Napište příkaz, který vypíše seznam skupin, v nichž se
nacházíte.
(Ukázat řešení)#!/bin/sh
id -Gn - Pošlete obsah souboru
/etc/group
mejlem příkazemmail
.
(Ukázat řešení)#!/bin/sh
mail -s "Obsah /etc/group" adresa </etc/group - Vyzkoušejte si příkaz
df
ke zjištění obsazeného místa na disku s různými parametry (-k a -P, můžete si zkusit i další, ale všimněte si, že kromě -t nejsou už další parametry ve specifikaci). - Vyzkoušejte si příkaz
du
na adresáři/tmp
nebo na vašem domovském adresáři s různými parametry (-k
,-s
,-x
). - Vytvořte jednoduchý skript a přidělte mu práva pro spuštění.
(Ukázat řešení)#!/bin/sh
cat >skript.sh <<EOF
#!/bin/sh
echo Ahoj světe
EOF
chmod a+x skript.sh - Vytvořte soubor, který může vlastník spustit, číst a psát do něj,
členové skupiny jej mohou číst a psát do něj,
ostatní jej mohou jen spustit.
(Ukázat řešení)#!/bin/sh
cat >skript.sh <<EOF
#!/bin/sh
echo Ahoj světe
EOF
chmod 0761 /tmp/skript.sh
# Ekvivalentně například:
chmod u+rwx,g=u-x,o=u-rw /tmp/skript.sh
# nebo
chmod u=rwc,g=rw,o=x /tmp/skript.sh - Zkuste vytvořit skript, který nelze číst, ale má práva na spuštění, zkuste jej spustit.
Proč to u shellového skriptu nejde?
(Ukázat řešení)#!/bin/sh
cat >/tmp/skript.sh <<EOF
#!/bin/sh
echo Ahoj světe
EOF
chmod a+x-r /tmp/skript.sh
/tmp/skript.sh
# Aby mohl skript shell spustit, musí jej být schopen přečíst,
# k čemuž musí mít oprávnění. - Vytvořte adresář, do kterého může kdokoli přejít a přidávat soubory, ale
jen vlastník smí vypsat obsah adresáře.
(Ukázat řešení)#!/bin/sh
mkdir /tmp/adresar
chmod 0755 /tmp/adresar
#Případně ekvivalentně
chmod u+rwx,go=u-w /tmp/adresar - Vytvořte adresář, do něhož lze přejít, nelze číst jeho obsah a lze
v něm přidávat soubory. Vytvořte v tomto adresáři soubor, k němuž
budete mít práva jen ke čtení a zkuste ho smazat.
(Ukázat řešení)#!/bin/sh
mkdir /tmp/adresar
chmod 0300 /tmp/adresar
# Případně ekvivalentně
chmod u+wx-r,go-rwx /tmp/adresar
touch /tmp/adresar/soubor
chmod 0400 /tmp/adresar/soubor
# Případně ekvivalentně
chmod u+r-wx,go-rwx /tmp/adresar/soubor
rm -f /tmp/adresar/soubor - Okopírujte /etc/passwd do /tmp jednak pomocí
cp
, jednak pomocícp -p
a koukněte se pomocíls -l
, jaký je mezi těmito kopiemi rozdíl. - Spusťte si vimtutor cs
Domácí úkoly
-
Napište příkaz, kterým všem souborům v podstromu adresáře
/tmp/adresar
přidáte právo ke čtení všem členům skupiny a současně právo ke čtení zruší ostatním (tj. těm, kdo nejsou vlastníci ani členové skupiny). [1 bod] -
Vytvořte soubor a přidělte mu práva tak, aby jej mohl vlastník
spustit, číst i zapisovat do něj, členové skupiny jej mohli spustit a
číst, ostatní jej mohou jen číst. V příkazu
chmod
použijte jednak symbolický zápis práv [1 bod] a jednak octalový zápis práv [1 bod].
4. cvičení (10. března 2015)
Jednoduché utility.
Příkazy:
ln
, diff
, cut
,
paste
, tr
,
sort
.
Příklady:
- Vytvořte ve svém domovském adresáři link na
/etc/passwd
, zkuste nejprve vytvořit hard link a poté soft link (rozmyslete si, proč hard link vytvořit nelze). Vyzkoušejte, jak se přils -l
zobrazí soft link.
(Ukázat řešení)#!/bin/sh
# Hard link
ln /etc/passwd ~
# Soft link
ln -s /etc/passwd ~ - Vytvořte hard link v
/tmp
na jiný soubor v/tmp
(případně si tam nějaký nakopírujte, hard link bude mít pochopitelně jiné jméno). Vyzkoušejte si výpis ls -l a všimněte si, jak se změnilo číslo v druhém sloupečku. Pomocíls -i
zkontrolujte, že obě kopie mají stejné číslo inode. - Stáhněte si soubor
calories.csv
, jde o soubor ve formátu CSV, kde je jako oddělovače sloupců použito středníku. - V souboru
calories.csv
nahraďte uvozovky pomocí apostrofů.
(Ukázat řešení)#!/bin/sh
tr \" \' <calories.csv >/tmp/calories.a.csv
mv /tmp/calories.a.csv calories.csv - Ze souboru
calories.csv
odstraňte apostrofy (stejně jako dále předpokládám, že soubor byl změněn předchozími příklady, tedy zde jsou po předchozím příkladu apostrofy na místě původních uvozovek).
(Ukázat řešení)#!/bin/sh
tr -d \' <calories.csv >/tmp/calories.a.csv
mv /tmp/calories.a.csv calories.csv - Ze souboru
calories.csv
vypište jen první sloupec, tj. sloupec se jmény potravin.
(Ukázat řešení)#!/bin/sh
cut -f1 -d\; calories.csv - V souboru
calories.csv
nahraďte velká písmena malými, ale jen v prvním sloupci. (Postup: Vyříznutí prvního sloupce pomocícut
, náhrada pomocítr
, připojení zpět pomocípaste
(Ukázat řešení)#!/bin/sh
cut -f1 -d\; calories.csv >/tmp/prvni
cut -f2- -d\; calories.csv >/tmp/ostatni
tr '[:upper:]' '[:lower:]' /tmp/prvni | paste -d\; - /tmp/ostatni >calories.csv
rm /tmp/prvni /tmp/ostatni - V souboru
calories.csv
nahraďte čárky pomocí pomlčky a pomocídiff
se podívejte, které řádky byly změněny.
(Ukázat řešení)#!/bin/sh
tr "," "-" <calories.csv | diff calories.csv - - Soubor
calories.csv
seřaďte podle množství calorií sestupně, první řádka s hlavičkou musí zůstat na svém místě.
(Ukázat řešení)#!/bin/sh
# Uložíme hlavičku
head -n 1 calories.csv >/tmp/hlavicka
# Zbytek seřadíme a přidáme
tail -n +2 calories.csv | sort -t\; -k4,4nr >>/tmp/hlavicka
mv /tmp/hlavicka calories.csv - Soubor
calories.csv
seřaďte podle trojice (protein, karbohydráty, tuk) vzestupně. První řádek s hlavičkou musí zůstat na svém místě.
(Ukázat řešení)#!/bin/sh
# Uložíme hlavičku
head -n 1 calories.csv >/tmp/hlavicka
# Zbytek seřadíme a přidáme
tail -n +2 calories.csv | sort -t\; -k7,7n -k6,6n -k5,5n >>/tmp/hlavicka
mv /tmp/hlavicka calories.csv - Ze souboru
calories.csv
vypište počet různých použitých jednotek (druhý sloupec).
(Ukázat řešení)#!/bin/sh
# V prvním řešení rozlišujeme i mezi dvěma jednotkami s různým
# množstvím (tj. např. 1 Cup a 2 Cup jsou brány jako různé)
tail -n +2 calories.csv | sort -t\; -k2,2 -u | wc -l
# Pokud bychom chtěli brát dvě jednotky s různým množstvím jako
# shodné (tj. nechceme například rozlišovat mezi 1 Cup a 2 Cup),
# pak můžeme postupovat následovně:
tail -n +2 calories.csv | cut -f2 -d\; | cut -f2- -d" " | sort -u | wc -l - Soubor
calories.csv
upravte tak, aby poslední tři sloupce byly v opačném pořadí (tj. ve výsledku bude nejprve množství proteinů, pak karbohydrátů a pak tuků, použijtecut
apaste
).
(Ukázat řešení)#!/bin/sh
cut -f5 -d\; calories.csv >/tmp/paty
cut -f6 -d\; calories.csv >/tmp/sesty
cut -f7 -d\; calories.csv >/tmp/sedmy
cut -f1-4 -d\; calories.csv | paste -d\; - /tmp/sedmy /tmp/sesty /tmp/paty >/tmp/novy
mv /tmp/novy calories.csv
rm /tmp/paty /tmp/sesty /tmp/sedmy - Z výstupu
ls -l
vyberte pouze 9 znaků s právy.
(Ukázat řešení)#!/bin/sh
ls -l | cut -c2-10 - Předpokládejte, že máte tři soubory,
scitanec1
,scitanec2
asoucet
, každý z nich obsahuje stejný počet řádků s čísly. Sestavte tyto řádky do tvaru
scitanec1+scitanec2=soucet
.
(Ukázat řešení)#!/bin/sh
paste -d+= scitanec1 scitanec2 soucet - Vypište seznam souborů v aktuálním adresáři ve tvaru
„velikost jméno“, seznam vytvořte úpravou výpisu
ls -l
als
.
(Ukázat řešení)#!/bin/sh
ls -l | tail -n +2 | tr -s " " | cut -d " " -f 5 >/tmp/velikosti
ls | paste -d " " /tmp/velikosti -
rm /tmp/velikosti - Vypište loginy ze souboru
/etc/passwd
po pěti na řádek, na každém řádku jsou oddělené čárkami.
(Ukázat řešení)#!/bin/sh
cut -d: -f1 </etc/passwd | paste -d"," - - - - -
# Alternativně a ekvivalentně lze:
cut -d: -f1 </etc/passwd | paste -s -d",,,,\n" -
Domácí úkoly
- V souboru
calories.csv
nahraďte každý název potraviny (tj. první sloupec) jediným znakem „-“. Ostatní sloupce zůstanou beze změny. [1 bod] - Vypište dvojice uid:login za každého uživatele v
/etc/passwd
. [1 bod] - Určete počet různých států, které se vyskytují v souboru
ip-by-country.csv
. [1 bod]
5. cvičení (17. března 2015)
Příkazy:
comm
, join
, split
, find
.
Příklady:
- Vyzkoušejte si následující příkaz
tr -c '\n' '-' </etc/passwd
tr
je potřeba mít obě množiny stejně velké, jinak není chování specifikované.) Očekávaného chování dosáhnete pomocítr -c '\n' '[-*]' </etc/passwd
- Stáhněte si soubory countrycodes_en.csv a kodyzemi_cz.csv. Obojí jsou soubory ve formátu CSV, kde je jako oddělovače sloupců použito středníku.
- Ze souborů
countrycodes_en.csv
akodyzemi_cz.csv
vytvořte výpis zemí se dvěma sloupci oddělenými rovnítkem ve tvaru
český název=anglický název (bez uvozovek).
(Ukázat řešení)#!/bin/sh
tr -d '"' <kodyzemi_cz.csv | tail -n +2 | sort -t \; -k1,1 >/tmp/kody_s
tr -d '"' <countrycodes_en.csv | tail -n +2 | sort -t \; -k4,4 >/tmp/codes_s
join -t\; -1 1 -2 4 -o 1.4,2.1 /tmp/kody_s /tmp/codes_s | tr \; = >preklad
rm /tmp/kody_s /tmp/codes_s - Vypište názvy souborů, které se nacházejí
/usr/bin
i v/bin
.
(Ukázat řešení)#!/bin/sh
ls /usr/bin | sort >/tmp/usr_bin
ls /bin | sort | comm -12 - /tmp/usr_bin
rm /tmp/usr_bin - Vypište čísla skupin, která jsou v
/etc/group
, ale nejsou použita v/etc/passwd
.
(Ukázat řešení)#!/bin/sh
cut -d: -f4 </etc/passwd | sort > /tmp/uid
cut -d: -f3 </etc/group | sort | comm -23 - /tmp/uid
rm /tmp/uid - Rozdělte soubor na kusy po pěti řádcích a pak jej zase spojte do
jednoho souboru.
(Ukázat řešení)#!/bin/sh
# Budeme dělit soubor /etc/passwd
# Předpokládáme přitom, že /etc/passwd obsahuje jen tolik záznamů,
# aby se vešly do souborů s dvoupísmennými identifikátory názvu.
split -l5 /etc/passwd kusypasswd
# Spojený soubor uložíme do aktuálního adresáře, protože k zápisu do
# /etc nemáma práva
cat kusypasswd* >passwd
rm kusypasswd* - Vypište loginy ze souboru
/etc/passwd
do deseti řádků, na každém řádku jsou loginy oddělené mezerami.
(Ukázat řešení)#!/bin/sh
# Předpokládáme, že /etc/passwd obsahuje jen tolik záznamů,
# abychom si vystačili s dvoupísmennými identifikátory souborů.
cut -d":" -f1 /etc/passwd | split -l10 - loginy
paste -d" " loginy*
rm loginy* - Napište příkaz, který vypíše všechny (pro vás viditelné) soubory nebo
adresáře, které mají ve jménu podřetězec 'bin' v podstromu aktuálního
adresáře.
(Ukázat řešení)#!/bin/sh
find . -name "*bin*" "(" -type d -o -type f ") - Napište příkaz, který vypíše počet všech (pro vás viditelných) adresářů
v podstromu
/etc
.
(Ukázat řešení)#!/bin/sh
find /etc -type d | wc -l - Vypište všechny symbolické linky z adresáře
/etc
, nezahrnujte celý podstrom, jen to, co je přímo v adresáři/etc
.
(Ukázat řešení)#!/bin/sh
find /etc -type l '!' -path "/etc/*/*"
# nebo
find /etc/* -prune -type l - Z podstromu adresáře
/usr/bin
vypište soubory, na které ukazují alespoň tři hardlinky.
(Ukázat řešení)#!/bin/sh
find /usr/bin -links +3 - V podstromu adresáře
/tmp
najděte všechny soubory, které jsou větší než sto kilobyte a jsou čitelné pro všechny.
(Ukázat řešení)#!/bin/sh
find /tmp -size +100000c -perm -a+r
Domácí úkoly
- Na základě obsahu souborů countrycodes_en.csv a kodyzemi_cz.csv vypište názvy států, které se jmenují stejně v angličtině jako v češtině. [1 bod]
- V podstromu aktuálního adresáře najděte soubory, které mají nulovou velikost. [1 bod]
- V podstromu adresáře
/usr/include
najděte soubory, jejichž jméno začíná řetězcem "std" a nekončí příponou ".h". [1 bod]
6. cvičení (24. března 2015)
Příkazy: find
,
xargs
, grep
.
Příklady:
- V podstromu adresáře
/usr/include
najděte soubory, které jsou v podstromu adresářů v hloubce alespoň 2 a nejvýš 3.
(Ukázat řešení)#!/bin/sh
find /usr/include -path "/usr/include/*/*" ! -path "/usr/include/*/*/*/*"
# Efektivnější řešení s pomocí prune:
find /usr/include -path "/usr/*/*/*" -prune -o -path "/usr/*/*" - V adresáři
/etc
najděte soubory, které jsou novější než/etc/passwd
.
(Ukázat řešení)#!/bin/sh
find /etc -newer /etc/passwd - V podstromu adresáře
/bin
najděte soubory, které vlastní root a jsou spustitelné jen pro vlastníka a členy skupiny, nikoli pro ostatní.
(Ukázat řešení)#!/bin/sh
find /bin -user root -perm -ug+x ! -perm -o+x - V adresáři
/etc
najděte soubory, které patří skupiněstunnel
a pro každý vypište dlouhé informace pomocíls -l
.
(Ukázat řešení)#!/bin/sh
find /etc -group stunnel -exec ls -l {} + - Zkuste rozdíl mezi
echo $PATH; echo "$PATH"; echo '$PATH';
- Zkuste rozdíl mezi
echo *; echo "*"; echo '*';
- Vypište seznam cest obsažených v
$PATH
s tím, že bude každá na samostatném řádku a ve výpisu nebudou obsažený oddělovací znaky „:“.
(Ukázat řešení)#!/bin/sh
echo $PATH | tr ":" "\n" - Vypište dlouhý výpis adresářů obsažených v
$PATH
, lépe řečeno dlouhé informace o nich, ne jejich obsah.
(Ukázat řešení)#!/bin/sh
echo $PATH | tr ":" "\n" | xargs -I{} ls -ld {} - Vytvořte soubor, který obsahuje 3 řádky:
a b c d e f g h i
a na něm zkuste následující příkazy:cat soubor | xargs echo cat soubor | xargs echo - cat soubor | xargs -n 2 echo - cat soubor | xargs -I{} echo "-{}-" "-{}-" cat soubor | xargs -I{} -n 2 echo "-{}-" "-{}-"
- Rozdělte soubor
/etc/passwd
na části po pěti řádcích a potom tyto části poskládejte v opačném pořadí do jiného souboru, (to jest nejprve jde posledních pět řádků v pořadí jako v/etc/passwd
, potom předposledních pět atd.)
(Ukázat řešení)#!/bin/sh
split -l5 /etc/passwd kusypasswd
ls -r kusypasswd* | xargs cat >passwd
rm kusypasswd* - Zjistěte, ve kterých adresářích uložených v proměnné
$PATH
se vyskytuje programawk
.
(Ukázat řešení)#!/bin/sh
echo $PATH | tr ':' '\n' | xargs -I{} find {}/awk -prune 2>/dev/null - Ze souboru calories.csv vyberte
řádky, v nichž název potraviny obsahuje alespoň tři znaky.
(Ukázat řešení)#!/bin/sh
grep '^"[^;]\{3,\}";' calories.csv - Ze souboru calories.csv vyberte
řádky, v nichž název potraviny obsahuje znak, který není alfanumerický
(tj. nepatří do třídy
[:alnum:]
).
(Ukázat řešení)#!/bin/sh
grep '^"[^;]*[^[:alnum:];][^;]*";' calories.csv
# Případně
grep -v '^"[[:alnum:]]*";' calories.csv - Ze souboru calories.csv vyberte
řádky, jejichž množství je měřeno jednotkou „Piece“ nebo „Cake“.
(Ukázat řešení)#!/bin/sh
grep -e '^[^;]*;[[:digit:].]* Piece;' \
-e '^[^;]*;[[:digit:].]* Cake;' calories.csv
# Případně
grep -E '^[^;]*;[[:digit:].]* (Piece|Cake);' calories.csv - Z podstromu adresáře
/usr/include
vyberte soubory, jejichž názvy obsahují 5-7 znaků, nepočítáme-li příponu, která má být „.h“. (Použijtegrep
na výstup příkazufind
.)
(Ukázat řešení)#!/bin/sh
find /usr/include -name "*.h" | grep '/[^/]\{5,7\}.h' - Z výpisu
ls -l
vyberte řádky, u nichž má vlastník nastavena stejná práva jako skupina i jako ostatní. (Případně si odpovídající soubor například v/tmp
vytvořte.)
(Ukázat řešení)#!/bin/sh
ls -l | grep '.\(...\)\1\1' - Ze souboru kodyzemi_cz.csv
vyberte řádky, v nichž dvouznakový kód je shodný s prvními dvěma znaky
trojznakového kódu země.
(Ukázat řešení)#!/bin/sh
grep '^[[:digit:]]*;\(..\).;\1;' kodyzemi_cz.csv - Ze souboru kodyzemi_cz.csv
vyberte naopak řádky, v nichž dvouznakový kód není shodný s prvními dvěma znaky
trojznakového kódu země.
(Ukázat řešení)#!/bin/sh
grep -v '^[[:digit:]]*;\(..\).;\1;' kodyzemi_cz.csv - Vypište soubory, které se vyskytují v podstromu adresáře
/usr/include
a obsahují řetězec „printf“.
(Ukázat řešení)#!/bin/sh
find /usr/include -exec grep -q printf {} \; -print
Domácí úkoly
- Vypište názvy souborů, které se nacházejí v adresáři
/usr/bin
(jen přímo, nikoli v podadresáři, takže stačíls
) a o kterých příkazfile
vypíše, že jde o shellový skript (výpisfile
obsahuje „POSIX shell script“ jako popis souboru,file
vypisuje ve tvaru soubor: popis). [1 bod] - Vypište všechny uživatele (jen loginy), kteří mají v
/etc/passwd
uvedenou primární skupinu s číslem 200. [1 bod] - Vypište řádky s titulky
<h
i>
, které jsou v daném html souboru a které se vejdou na jeden řádek (tj. na jednom řádku je jak počáteční tag, tak ukončující tag, mezi tím může být cokoli), předpokládejte syntakticky správný vstupní soubor. [1 bod]
7. cvičení (31. března 2015)
Příkazy:
sed
, nl
.
- Pomocí sedu vyberte z výpisu
ls -l
jen sloupec s velikostí souboru.
(Ukázat řešení v sedu)#!/bin/sh
ls -l | sed '1d; s/^\([^ ]* *\)\{5\} .*$/\1/'
(Ukázat řešení v edu)#!/bin/sh
# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
ls -l > /tmp/lsl.$$
ed /tmp/lsl.$$<<\KONEC
2,$g/^/s/^\([^ ]* *\)\{5\} .*$/\1/
%p
Q
KONEC
rm /tmp/lsl.$$ - Pomocí sedu přetvořte řádky
/etc/passwd
do podobyuid (login)
.
(Ukázat řešení v sedu)#!/bin/sh
sed 's/^\([^:]*\):[^:]*:\([^:]*\).*$/\2 (\1)/' /etc/passwd
(Ukázat řešení v edu)#!/bin/sh
# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
# Pokud chceme na konci výsledek uložit do souboru, pak tam bude w a q.
cp /etc/passwd /tmp/passwd.$$
ed /tmp/passwd.$$<<\KONEC
g/^/s/^\([^:]*\):[^:]*:\([^:]*\).*$/\2 (\1)/
%p
Q
KONEC
rm /tmp/passwd.$$ - Z výstupu
nl /etc/passwd
vypište jen sudé řádky.
(Ukázat řešení v sedu)nl /etc/passwd | sed -n 'n; p'
(Ukázat řešení v edu)#!/bin/sh
# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
# Příkaz g provádí následující příkazy na všechny řádky.
# V rámci něj:
# 1) +1p vypíše následující řádku a
# 2) .s/^// odznačí (následující) řádku tak, že ji g vynechá.
# poslední řádku vynecháváme (předpokládáme, že soubor obsahuje aspoň
# dvě řádky), protože u ní by +1 lezlo ven ze souboru.
#
# Kdybychom použili s///, tak místo prázdného regexpu by g doplnilo
# svůj regexp a došlo by k vymazání části sudých řádek. Pokud se neukládají
# změny, tak to nevadí.
nl /etc/passwd > /tmp/passwd.$$
ed /tmp/passwd<<\KONEC
1,$-1g/^/+1p\
.s/^//
Q
KONEC
rm /tmp/passwd.$$ - Na začátky lichých řádek souboru
/etc/passwd
vložte znak „l“, na začátky sudých řádek vložte znak „s“.
(Ukázat řešení v sedu)#!/usr/bin/sed -f
# Toto je skript pro sed, použití pomocí sed -f
# Volání sed -f <skript> /etc/passwd
s/^/l/
n
s/^/s/
(Ukázat řešení v edu)#!/bin/sh
# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
#
# První příkaz přidá znak „l“ na začátek každé řádky.
# Druhý příkaz změní znak „s“ na začátku každé sudé řádky.
# Poté je vypsán výsledek a ed ukončen bez uložení změn.
ed /etc/passwd <<\KONEC
g/^/s/^/l/
1,$-1g/^/+1s/^l/s/
%p
Q
KONEC - Na začátky desáté až dvacáté řádky souboru
/etc/passwd
přidejte znak#
.
(Ukázat řešení v sedu)#!/bin/sh
sed '10,20s/^/#/' /etc/passwd
(Ukázat řešení v edu)#!/bin/sh
# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
#
# Změna se neukládá (do /etc/passwd by to nebyl dobrý nápad) a
# výsledek se jenom vypíše na standardní výstup.
ed /etc/passwd <<\KONEC
10,20s/^/#/
%p
Q
KONEC - Před první řádku vstupního souboru vložte řádku s
#!/bin/sh
.
(Ukázat řešení v sedu)#!/usr/bin/sed -f
# Toto je skript pro sed, použití pomocí sed -f
1i\
#!/bin/sh
(Ukázat řešení v edu)# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
ed vstupnisoubor <<\KONEC
1i
#!/bin/sh
.
w
q
KONEC - Napište skript pro sed, který odstraní ze shellového skriptu
komentáře, přičemž
- Pokud jde o první řádku souboru a pokud tato řádka má formát
^#!.*
, pak tento komentář neodstraňujte. - Řádky, kde před
#
není nic než mezery, odstraňte úplně. - Pokud se před
#
vyskytují nějaké jiné znaky, odstraňte z řádky vše od prvního výskytu znaku#
(včetně) do konce řádky.
#!/usr/bin/sed -f
# Toto je skript pro sed, použití pomocí sed -f
# Přeskočení první řádky, pokud jde o komentář typu #!
1{
/^#!/n
}
# Smazání řádek, kde před # nic není
/^[[:space:]]*#/d
# Odstranění komentáře z ostatních řádek (ignoruje fakt, že # se může
# vyskytovat i v rámci řetězce v uvozovkách a pod., kdybychom toto
# chtěli kontrolovat, bylo by to výrazně komplikovanější).
s/#.*$//
(Ukázat řešení v edu)#!/bin/sh
# Nejprve vyřídíme vše kromě prvního řádku.
# - První příkaz smaže řádky, které před # nic nemají.
# - Druhý příkaz odstraní z ostatních řádků vše od # dál.
# Poté vyřešíme první řádek.
# - Nejprve vymažeme 1. řádek, pokud obsahuje komentář, který před # nic
# nemá a který napak za # má nějaký znak, který není !.
# - Poté smažeme 1. řádek, pokud obsahuje jen #, za nímž ani před nímž
# nic není. Pokud předchozí příkaz proběhl úspěšně, dostane se sem 2.
# řádek, ale z něj jsme již komentáře odstranili, a tak se nám to
# nemůže splést a další příkazy se pro něj nevykonaly. Z tohoto
# důvodu jsme také nejprve řešili řádky od 2. do posledního.
# - Nakonec z prvního řádku odstraníme vše od prvního # do konce, pokud
# nemá #! na začátku.
# Všude je nutné používat g nebo v, protože jinak by s při neúspěšné
# substituci hlásilo chybu.
# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
ed vstupnisoubor.sh <<\KONEC
2,$g/^[[:space:]]*#/d
2,$g/#/s/#.*//
1g/^[[:space:]]*#[^!]/d
1g/^[[:space:]]*#$/d
1v/^#!/s/#.*$//
w
q
KONEC - Pokud jde o první řádku souboru a pokud tato řádka má formát
- Napište skript pro sed, který otočí pořadí řádek na vstupu.
(Ukázat řešení v sedu)#!/bin/sh
# Vyzkoušíme na nl /etc/passwd, aby bylo vidět opačné pořadí.
nl /etc/passwd | sed -n 'G; $p; h'
(Ukázat řešení v edu)#!/bin/sh
# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
# Zpětné lomítko je za příkazem i nutné proto, že jde o příkaz v rámci
# příkazu g, jinak by tam nebylo potřeba.
ed /etc/passwd <<\KONEC
g/^/m0
%p
Q
KONEC - Mezi každé dvě řádky souboru
/etc/passwd
vložte prázdnou řádku.
(Ukázat řešení v sedu)#!/bin/sh
# $b zařídí, že se nebude nic přidávat za poslední řádku (při
# poslední řádce se skočí na konec skriptu bez vykonání příkazu a).
# Příkaz a pak provádí samotné přidání nové řádky za aktuální.
sed '$b; a\
' /etc/passwd
# Ekvivalentně lze i pomocí i (adresace zabezpečuje, že se nic nepřidá
# před první řádku):
sed '2,$i\
' /etc/passwd
(Ukázat řešení v edu)#!/bin/sh
# Všimněte si, že řetězec KONEC je uvozen zpětným lomítkem, to
# proto, aby shell neprováděl expanzi v textu, který za ním píšeme.
# Zpětné lomítko je za příkazem i nutné proto, že jde o příkaz v rámci
# příkazu g, jinak by tam nebylo potřeba.
ed /etc/passwd <<\KONEC
2,$g/^/i\
\
.
%p
Q
KONEC - Napište skript pro sed, který na každé řádce nahradí jednořádkové
komentáře typu
/* ... */
komentářem typu//...
, uvažte, že na řádku může být těch jednořádkových komentářů víc a že kolem komentářů je i obyčejný text. Z jednoho řádku tak vznikne víc řádek, protože za každým komentářem//
musíte odřádkovat.
(Ukázat řešení)#!/usr/bin/sed -f
# Toto je skript pro sed, použití pomocí sed -f
: zacatek
s%\(.*\)/\*\(.*\)\*/\(.*\)%\1//\2\
\3%
t zacatek
Domácí úkoly
- Pomocí sedu ve vstupním textovém souboru nahraďte výskyty znaků '&', '<', '>' pomocí jejich HTML entit ('&' pomocí '&', '<' pomocí '<' a '>' pomocí '>'). [1 bod]
- Pomocí sedu ve vstupním souboru spojte vždy dvě následující řádky do jedné (tj. odstraňte odřádkování mezi nimi). Ve výsledku tedy bude spojena 1. s 2. řádkou, 3. se 4. řádkou, 5. s 6. řádkou, atd. [1 bod]
- Předpokládejte, že řádky vstupního souboru obsahují jen znaky '(' a ')' (tj. na každé řádce je řetězec vyhovující regulárnímu výrazu „[()]*“), vytvořte skript pro sed, který zkontroluje, zda je řetězec na řádce správně uzávorkovaný. Pokud ano, nahradí řádku řetězcem 'ano', v opačném případě řetězcem 'ne'. [1 bod]
8. cvičení (7. dubna 2015)
Příkazy:
ed
, ex
.
Další: Proměnné a expanze, přesměrování, poziční a speciální parametry ($#, $0, $n, ${n}, shift, "$@", set -, $?, $$, $!), kombinace příkazů pomocí &&, složený příkaz a vyvolání subshellu, zpětné apostrofy a $().
- Pomocí edu vypište posledních deset řádek souboru
/etc/passwd
.
(Ukázat řešení)#!/bin/sh
ed /etc/passwd <<\KONEC
$-9,$p
Q
KONEC - Předchozí příklady pro sed (příklady z 7. cvičení kromě příkladu č. 10) vyřešte pomocí edu. Řešení najdete u každého příkladu.
- Napište skript, který očekává dva parametry, název souboru a
číslo. Ze zadaného souboru vymaže řádku se zadaným číslem (např. pomocí
ed
u).
(Ukázat řešení)#!/bin/sh
ed "$1" <<KONEC
${2}d
w
q
KONEC - Vytvořte skript, který dostane n+1 parametrů, první obsahuje
číslo x mezi 1 a n, skript vypíše parametr s číslem
(x+1). Tj. například,
skript 2 a b c
vypíšeb
, tedy ten třetí parametr (ale druhý, když budete počítat oda
). (Použijteshift
s parametrem.)
(Ukázat řešení)#!/bin/sh
shift $1
echo $1
Domácí úkoly
- Napište příkazy edu, které ve vstupním souboru přesune řádky začínající znakem „#“ na konec souboru. [1 bod]
- Uvažte vstupní soubor, který obsahuje text, kde odstavce jsou odděleny prázdným řádkem. Pomocí edu proveďte spojení každého odstavce do jedné řádky. (Poznámka: Ke spojení dvou řádek do jedné lze použít v edu příkaz „j“.) [1 bod]
- Napište skript, který dělá totéž, co
cp
, ale s opačným pořadím parametrů (cíl je jako první, nezapomeňte, že obecně cp přijímá n+1 parametrů s tím, že je-li n>1, pak poslední parametr cp musí být adresář. ) [1 bod].
9. cvičení (14. dubna 2015)
Příkazy: printf
, read
, řídící struktury (for
,
while
, if
, case
, a pod.),
printf
, expr
, test
,
dirname
, basename
.
Další: Proměnné a expanze, přesměrování, poziční a speciální parametry ($#, $0, $n, ${n}, shift, "$@", set -, $?, $$, $!), kombinace příkazů pomocí &&, složený příkaz a vyvolání subshellu, zpětné apostrofy a $().
- Napište skript, který očekává jeden až dva číselné parametry.
První číselný parametr pak zarovná na počet míst zadaných druhým
číselným parametrem. Pokud druhý parametr není zadaný, implicitně se
rozumí 5. (Použijte
printf
a expanzi pomocí${proměnná:-slovo}
a podobné.)
(Ukázat řešení)#!/bin/sh
printf "%${2:-5}d\n" $1 - Co vypíší následující příkazy:
a="ahoj"; { echo $a ; a="cau" ; echo $a ; } ; echo $a
b="ahoj"; ( echo $b ; b="cau" ; echo $b ; ) ; echo $b
x="ahoj"; x="cau" sh -c 'echo $x'; echo $x
y="ahoj"; y="cau"; sh -c 'echo $y'; echo $y
z="ahoj"; sh -c 'echo $z'
export u="ahoj"; sh -c 'echo $u' - Rozmyslete si, co dělá následující příkaz:
a=1;b=2; { a=LEVY; echo $a >&2; } |\
{ b=PRAVY; echo $b; tr 'A-Z' 'a-z'; }; echo "A=$a,B=$b"a=1;b=2; { a=LEVY; echo $a; } |\
{ b=PRAVY; echo $b; tr 'A-Z' 'a-z'; }; echo "A=$a,B=$b" -
Rozmyslete si, co dělá následující příkaz, co se vypíše do out a co do
err a proč.
{ { { echo 1aaa;echo 2bbb >&2;} 3>&1 1>&2 2>&3 |\
{ tr 'a-z' 'A-Z'; echo 2tr >&2;} 2>&1; } \
3>&1 1>&2 2>&3; }>out 2>err -
Co vypíší následující příkazy na obrazovku?
sh -c "exit 1" | sh -c "exit 2"
echo $?
{ { { sh -c "exit 1"; echo $? >&3; } |\
sh -c "exit 2" >&4; } 3>&1 | { read e; exit $e; }; } 4>&1
echo $?
{ { { sh -c "exit 1"; chyba=$?; } |\
sh -c "exit 2" >&4; } 3>&1 | { exit $chyba; }; } 4>&1
echo $? -
Spočítá následující skript správně počet řádků souboru
/etc/passwd
?pocet=0;
cat /etc/passwd | while read x
do
pocet=`expr $pocet + 1`
done
echo $pocetcat /etc/passwd | {
pocet=0
while read x
do
pocet=`expr $pocet + 1`
done
echo $pocet
}pocet=0
while read x
do
pocet=`expr $pocet + 1`
done </etc/passwd
echo $pocetpocet=0
while read x </etc/passwd
do
pocet=`expr $pocet + 1`
done
echo $pocet - Napište skript, který dostane v parametrech seznam souborů zadaný
cestami, které můžou být i relativní. Ke každému souboru vypíše řádek
s absolutní cestou.
(Ukázat řešení)#!/bin/sh
# Je-li část in ... u for cyklu vynechaná, doplní se in "$@"
for x
do
adresar=$(dirname "$x")
adresar=$(cd $adresar && pwd)
echo $adresar/$(basename $x)
done - Napište skript, který přejmenuje všechny soubory v aktuálním
adresáři na soubory se týmž jménem, kde jsou velká písmena změněná
na malá. (Například soubor 'ZALOHA.ZIP' by přejmenoval na
'zaloha.zip')
(Ukázat řešení)#!/bin/sh
for x in *[A-Z]*
do
mv "$x" "`echo $x | tr '[:upper:]' '[:lower:]'`"
done - Napište skript, který přejmenuje všechny soubory v aktuálním
adresáři s příponou ".jpeg" na soubory s týmž jménem, ale příponou
".jpg". (Zkuste využít expanze
${proměnná%přípona}
,sed
, nebodirname
abasename
.)
(Ukázat řešení)#!/bin/sh
# Varianta pomocí expanze (není ve standardu moc dlouho)
for x in *.jpeg
do
mv "$x" "${x%jpeg}jpg"
done
# Varianta pomocí sedu:
for x in *.jpeg
do
mv "$x" "$(echo $x | sed 's/\.jpeg$/.jpg/')"
done
# Varianta pomocí basename a dirname.
for x in *.jpeg
do
mv "$x" "$(dirname "$x")/$(basename "$x" .jpeg).jpg"
done
Domácí úkoly
- Napište skript, který očekává tři parametry, soubor, číslo n a znak c. Pokud třetí parametr chybí, za znak c je považován tabelátor (c=
- Napište skript, který očekává dva parametry, oba obsahují cestu k
adresářům, zdrojovému a cílovému, přičemž cílový dosud neexistuje. Skript zduplikuje adresářovou
strukturu do cílového adresáře. Soubory v adresářích nekopíruje a
ignoruje je, vytváří jen kopii adresářové struktury. Například
volání
skript /etc ~/etc
by v domovském adresáři vytvořilo podadresář etc, pod ním by byl stejný podstrom adresářů jako je v/etc
(bez kopií souborů v nich obsažených). [2 body]
\t
),
pokud chybí i druhý parametr, uvažuje se navíc n=1.
Skript potom provede cyklický posun doleva řádek podle položek oddělených
znakem c s krokem n. Tj. například při volánískript /etc/passwd 2 :
by každou řádku tvaru
login:*:uid:gid:jméno:domovský adresář:shell
upravil na řádku tvaru
uid:gid:jméno:domovský adresář:shell:login:*
Upravený soubor vypisuje skript na standardní výstup, soubor zadaný v parametru neupravuje. [1 bod]
10. cvičení (21. dubna 2015)
- Napište skript, který dostane tři parametry, všechny číselné, a
vypíše na standardní výstup čísla oddělená mezerami počínaje $1, konče
$2 a s krokem $3. Pokud skript dostane jen dva parametry, pak se
implicitně použije krok 1.
(Ukázat řešení)#!/bin/sh
if [ $# -lt 2 ] || [ $# -gt 3 ]
then
echo Špatný počet parametrů
exit 1
fi
krok=${3:-1}
x=$1
while [ $x -le $2 ]
do
echo $x
x=`expr $x + $krok`
done - Totéž jako předchozí příklad, ale čísla budou každé na zvláštní
řádce a budou zarovnaná na počet míst daný délkou nejdelšího čísla
(tj. délkou $2). Čísla budou zarovnaná nulami na začátku. Tj.
například
skript 0 10 2
by vypsal:00 02 04 06 08 10
(Ukázat řešení)#!/bin/sh
if [ $# -lt 2 ] || [ $# -gt 3 ]
then
echo Špatný počet parametrů
exit 1
fi
krok=${3:-1}
x=$1
delka=${#2}
while [ $x -le $2 ]
do
printf "%0${delka}d\n" $x
x=`expr $x + $krok`
done - Napište skript, který přejmenuje všechny soubory v aktuálním
adresáři s příponou jpg na soubory s touž příponou ale s čísly místo
názvů, kde čísla budou zarovnaná na tolik míst, kolik je třeba pomocí
nul. (Využijte toho, co jste už udělali v jednom z předchozích
příkladů.) Tj. například pokud jsou v adresáři soubory alice.jpg,
bob.jpg cyril.jpg a daniel.jpg, přejmenoval by je popořadě skript na
1.jpg, 2.jpg, 3.jpg a 4.jpg. Pokud by těchto souborů bylo alespoň
deset ale méně než 100, byly by pojmenovány jako 01.jpg, 02.jpg,
03.jpg, 04.jpg, ...
(Ukázat řešení)#!/bin/sh
x=1
pocet=`ls ./*.jpg | wc -l`
# Tohle je pro případ, že by tam byl nějaký soubor s příponou *.jpg a
# číselným názvem, mohlo by to selhat.
for soubor in *.jpg
do
mv "$soubor" "x$soubor"
done
for soubor in *.jpg
do
mv "$soubor" `printf "%0${#pocet}d\n" $x`.jpg
x=`expr $x + 1`
done - Napište skript, který očekává na standardním vstupu řádky ve
tvaru:
a+b=c
kde a, b a c jsou čísla, skript načte pro každou řádku tato tři čísla a zkontroluje, jestli je rovnost správná. Pokud není správná, ohlásí chybu s číslem řádky, na které k ní došlo. Na závěr vypíše celkový počet chyb. (Doporučení: Použijte shellový while cyklus sread a b c
a vhodnou volbou IFS (+=)).
(Ukázat řešení)#!/bin/sh
IFS="+="
radka=0
while read a b c
do
radka=$(expr $radka + 1)
if [ "$(expr "$a" + "$b")" -ne "$c" ]
then
printf "Chyba na řádce %d (%d+%d<>%d).\n" $radka $a $b $c
fi
done
printf "Počet zkontrolovaných řádek: %d\n" $radka - Napište skript, který na vstupu očekává řádky tvaru:
cil : z1 z2 .... zn
kde cil, z1, z2, ..., zn jsou názvy souborů. Ze vstupu vypíše skript ty cíle, které jsou starší než některý ze souborů z1, ..., zn na témže řádku (závislosti). (Parametr -nt příkazu test není standardní, použijte find s parametrem -newer.)
(Ukázat řešení)#!/bin/sh
# Řešení by nefungovalo, kdybychom chtěli v názvech souborů povolit
# mezery, ale pak bychom museli uvažovat nějaké backslashování mezer,
# proto tento případ pomíjíme.
while read cil dvojtecka soubory
do
set $soubory
if [ "$(find "$@" -newer "$cil" -prune)" ]
then
echo $cil
fi
done
# Alternativní řešení pomocí ls -t:
while read cil dvojtecka soubory
do
if [ "$(ls -t "$cil" $soubory | head -n 1)" != "$cil" ]
then
echo $cil
fi
done
Domácí úkol
-
Napište
csvcut
, který bude příjímat dva a více parametrů s následujícím významem: 1. parametr je znak oddělovačedelim
, 2. parametr je řetězec specifikující sloupec v CSV souboru a třetí a další parametry obsahují názvy souborů (pokud chybí, bude se číst standardní vstup). Skript předpokládá, že vstupní soubory jsou CSV soubory s oddělovačem daným prvním parametrem ($delim
). Navíc předpokládá, že první řádek každého souboru obsahuje hlavičku. Skript vyřízne z každého vstupního souboru sloupec, jehož název v hlavičkovém řádku je daný druhým parametrem, přičemž ignoruje případné uvozovky kolem názvu. Například volánícsvcut ';' Measure calories.csv
by ze souborucalories.csv
vyřízlo druhý sloupec (tj. bylo by ekvivalentnícut -d ';' -f 2 calories.csv
).csvcut ';' Food calories.csv
by z téhož souboru vyřízlo první sloupec (byť jsou v názvu v hlavičce uvozovky kolem Food).
11. cvičení (28. dubna 2015)
Příkazy:
trap
, ps
, kill
,
sleep
, date
, eval
.
- Napište skript, který bude očekávat vstup na standardním vstupu,
načtený vstup pouze zopakuje na svůj standardní výstup. Pokud skript
dostane signál SIGKILL (9), tak samozřejmě skončí, ale pokud dostane
signál SIGTERM (15), SIGQUIT (3), či SIGINT (2) (to je ten, který je
poslán pomocí Ctrl-c), neskončí a jen vypíše číslo toho signálu, který
takto obdržel. Při SIGINT navíc vypíše naposledy vypsanou řádku vstupu.
(Ukázat řešení)#!/bin/sh
trap "echo 15" TERM
trap "echo 3" QUIT
trap 'echo 2; echo $x' INT
while read x
do
echo $x
done - Napište skript, který dostane signál a jméno programu, poté pošle
signál všem procesům daného programu. Můžete předpokládat, že se
program jmenuje normálně a název neobsahuje žádné divné znaky (mezeru
ale obsahovat může). (Něco jako
killall
, alekillall
není standardní příkaz. Doporučuji podívat se na parametr-o
příkazups
. Dále se hodí třebased
axargs
)
(Ukázat řešení)#!/bin/sh
[ $# -eq 2 ] || {
echo "Očekávám dva parametry, číslo signálu a název programu";
exit 1
}
signal="$1"
program="$2"
ps -e -o pid= -o comm= |\
sed -n 's/^ *\([[:digit:]]\{1,\}\) \{1,\}'"$program"'$/\1/p' |\
xargs kill -$signal
# Případně totéž pomocí while cyklu:
ps -e -o pid= -o comm= | while read pid cmd
do
if [ "$cmd" == "$2" ]
then
kill -$signal $pid
fi
done - Napište skript, který dostane jeden číselný parametr s počtem
sekund. Skript potom počká zadaný počet sekund a skončí.
Pokud skript obdrží signál SIGINT (2 - Ctrl-C), tak vypíše, kolik
sekund už od začátku uběhlo. Pokud obdrží signál SIGQUIT (3 - Ctrl-\),
tak vypíše, kolik ještě zbývá sekund do konce, ale svůj běh nezastaví.
Pokud skript obdrží signál SIGTERM (15), vypíše, kolik uběhlo sekund od
začátku, kolik zbývá do konce a skončí. Návratová hodnota skriptu je
buď 0, pokud skončil ve správný čas, nebo zbývající počet sekund,
pokud skončil kvůli SIGTERM.
Nepoužívejtedate +%s
, %s není standardní součást formátovacího řetězce pro date. Použijtesleep
v cyklu.
(Ukázat řešení)#!/bin/sh
die() {
echo "$1"
exit 1;
}
# Vystačíme si s hodinami, minutami a sekundami,
# předpokládáme tedy, že interval bude kratší než den a že v čase
# čekání nebude půlnoc.
now() {
vzorec=$(date +"%H \* 3600 + %M \* 60 + %S")
eval expr $vzorec
}
[ $# -eq 1 ] || die "Špatný počet parametrů"
expr "$1" : '[[:digit:]]*$' >/dev/null || die "Parametr není číslo"
start=$(now)
konec=$(expr $start + $1)
trap 'expr $(now) - $start' 2
trap 'expr $konec - $(now)' 3
trap 'expr $(now) - $start; kill $! 2>/dev/null; exit' 15
while [ $(now) -lt $konec ]
do
sleep $(expr $konec - $(now) ) &
wait $!
[ $? -gt 128 ] && kill $! 2>/dev/null
done
# V uvedeném cyklu by stačilo sleep 1, ale proces by se probouzel
# každou vteřinu.
# sleep $(expr $konec - $(now) ) samo nestačí kvůli TERM, pokud totiž
# dostane proces shellu se skriptem TERM ve chvíli, kdy běží sleep,
# tak nechá nejprve sleep doběhnout a pak teprve zpracuje signál akcí
# danou trap. (To se neprojeví nutně u ctrl-C, protože to dostane sleep
# samo.) Proto je sleep puštěn na pozadí a poté spuštěno čekání, dokud
# sleep nedoběhne. Po wait se zpracuje signál, aby se nekumulovaly
# procesy sleep, je jim poté poslán signál TERM, čímž jsou průběžně
# ukončovány. V případě obsluhy TERM je též zabit poslední sleep
# běžící na pozadí. Test na to, zda wait s kódem větším než 128 je tu
# proto, že wait vrací takové číslo, právě pokud došlo k přerušení
# signálem. -
Napište skript
cutreorder
, který se bude chovat podobně jakocut
, bude mít parametry-d
a-f
, které budou mít týž formát a význam jako ucut
, v dalších parametrech budou názvy souborů (pokud nebudou uvedeny, čte se standardní vstup). Rozdíl je v tom, že skriptcutreorder
na výstupu umístí sloupce v pořadí daném parametrem-f
(vcut
jsou sloupce vypisované v pořadí, v jakém jsou ve vstupním souboru). Například-
cutreorder -d : -f 3,1 /etc/passwd
by vypsalo z/etc/passwd
do dvou sloupcůuid:login
. -
cutreorder -d : -f 3,3-5,1 /etc/passwd
by vypsalo sloupceuid:uid::gid:full name:login
.
(Ukázat řešení)#!/bin/sh
# Zpracování parametrů
# defaultně je delim tabelátor
delim=" "
pole=""
for x in 1 2
do
case "$1" in
-d) delim="$2"; shift 2;;
-d?) delim="${1#-d}"; shift;;
-f) pole="$2"; shift 2;;
-f*) pole="${1#-f}"; shift;;
esac
done
if [ "$pole" = "" ]
then
# Není co vypisovat
exit 0
fi
# Test, nemáme-li číst standardní vstup
if [ $# -eq 0 ]
then
set -- -
fi
# Upravíme si pole do tvaru vhodného pro for cyklus
pole="`echo $pole | tr , " "`"
# Zpracování souborů
for soubor
do
cat $soubor | while read radek
do
vysledek=""
# Zpracování jednotlivých položek vstupu a výstupu
for f in $pole
do
if [ -n "$vysledek" ]
then
vysledek="$vysledek$delim"
fi
vysledek="$vysledek$(echo "$radek" | cut -d $delim -f $f)"
done
echo "$vysledek"
done
done -
- Napište skript, který dostane jako parametr název souboru, skript očekává poté na standardním vstupu
posloupnost čísel, každé je na nové řádce a je indexem řádky, kterou
má skript vypsat. Skript implementujte tak, že nejprve načte řádky
souboru do pole, které implementujete pomocí eval (tj.
eval '$'POLE_$INDEX
), poté dotazy zodpovídejte přístupem do tohoto "pole".
(Ukázat řešení)#!/bin/sh
[ $# -eq 1 ] || { echo "Špatný počet parametrů"; exit 1; }
pocet=0
while read radka
do
pocet=$(expr $pocet + 1)
eval RADKY_$pocet='"$radka"'
done <"$1"
while read index
do
eval echo '$'RADKY_$index
done
Domácí úkol
- Napište skript pro spouštění příkazu pomocí e-mailu, na
standardní vstup dostane soubor následujícího tvaru:
From:<addr>
Subject:<cmd>
<input>
...
Skript spustí příkaz <cmd> a na standardní vstup mu dá <input>. Návratový kód (tj. $?) pošle zpět na adresu <addr> v subjectu, v těle mejlu posílaného zpět bude obsah standardního výstupu spuštěného programu.
Upřesnění:- Místo <addr> je pochopitelně skutečná adresa, místo <cmd> je skutečný příkaz i s parametry (které by měl shell zpracovat) a místo <input> je text, který se předá tomu příkazu. Třetí řádka je prázdná.
- Skript nedostane žádné parametry, vstup dostane na standardním vstupu.
- Parametry příkazu jsou uvedené v rámci subjectu, na standardní vstup dostane obsah mejlu od 4. řádky včetně dál.
[3 body]
12. cvičení (5. května 2015)
Příkazy:
awk
.
- Napište awk skript, který vypíše každou desátou řádku vstupu.
(Ukázat řešení)#!/bin/awk -f
(NR % 10) == 0 - Napište awk skript, který v souboru /etc/passwd převede písmena v
loginech na velká.
(Ukázat řešení)#!/bin/awk -f
BEGIN { FS=":"; OFS=":" }
{ $1=toupper($1); print } - Napište awk skript, který otočí pořadí slov na každé řádce vstupu
(napíše je v opačném pořadí)
(Ukázat řešení)#!/bin/awk -f
{
for (pole = NF; pole > 1; pole --)
{
printf ("%s" OFS, $pole);
}
printf ("%s" ORS, $1);
} - Napište awk skript, který očísluje řádky na vstupu a vypíše je i s
čísly (jako
nl
).
(Ukázat řešení)#!/bin/awk -f
{
print NR " " $0;
} - Napište awk skript, který vypíše tolik náhodných čísel mezi 0 a 1,
kolik dostane zadáno parametrem.
(Ukázat řešení)#!/bin/sh
# Toto je skript, který obaluje awk skript
# Sám dostane počet potřebných parametrů ve svém parametru.
awk -v pocet="$2" 'BEGIN {
srand();
for (i=0; i<pocet; ++i)
{
print rand ();
}
}' - Napište awk skript, který z výstupu
getent passwd
) vypíše plná jména uživatelů, jejichž login má tvar čtyři písmena následovaná čtyřmi číslicemi.
(Ukázat řešení)#!/bin/awk -f
BEGIN {
FS = ":";
}
$1 ~ /^[[:alpha:]]{4}[0-9]{4}$/ {
print $5;
} - Napište awk skript, který vypíše počet linků, tedy tagů
<a
ve vstupním html souboru.
(Ukázat řešení)#!/bin/awk -f
# Jako RS zvolíme "<", protože podle toho se dělí HTML soubor
# nejvhodněji. Specifikace ani nepřipouští víceznakové RS (v tom
# případě je výsledek nedefinovaný), regexp může být jen ve FS, tak to
# ani jinak nejde.
#
BEGIN {
RS = "<";
pocetAcek = 0;
}
($1 ~ /^a$/) {
++ pocetAcek;
}
END {
print pocetAcek;
} - Napište awk skript, který dostane na vstupu html soubor a vypíše
adresy, na které se tento soubor odkazuje (to jest za značku <a
href="adresa"> vypíše adresa). Pozor na to, že ta značka může být
rozložena přes víc řádků a můžou tam být kolem
a
,href
mezery. Volbou vhodného RS a FS si výrazně usnadníte práci.
(Ukázat řešení)#!/bin/awk -f
# Je to trochu komplikovanější, aby to fungovalo i v případě, že není
# href hned za a, ale je mezi tím ještě něco. Jinak by stačilo rovnou
# brát i=1.
BEGIN {
RS = "<";
FS = "=";
}
$1 ~ /^[[:blank:]]*a[[:blank:]]+/ {
i=1
while (i <= NF)
{
if ($i ~ /[[:blank:]]+href[[:blank:]]*$/)
{
split ($(i+1), pole, "[[:blank:]]*\"[[:blank:]]*");
print pole[2]
}
else if ($i ~ />/)
{
break;
}
++i;
}
} - Napište awk skript, který v textu zakomentuje všechny odstavce,
které začínají komentářovým řádkem. (To jest, před druhou a další
řádky tohoto odstavce předřadí komentářový znak.) Znak uvozující
komentář je '#'. Odstavce se oddělují prázdným řádkem.
(Ukázat řešení)#!/bin/awk -f
# Láká sice použít RS="\n\n", ale RS nemůže být víceznakové, a tak to
# tak jednoduše nejde.
BEGIN {
novyodstavec = 1;
komentovat = 0;
}
novyodstavec && $1 ~ /^#/ {
komentovat = 1;
}
! novyodstavec && NF > 0 && komentovat {
$1 = "#" $1
}
NF > 0 {
novyodstavec = 0;
}
NF == 0 {
novyodstavec = 1;
komentovat = 0;
}
{ print } - Napište awk skript, který vypíše posledních 10 řádků souboru.
(Ukázat řešení)#!/bin/awk -f
BEGIN {
konecBufferu = 0;
}
{
buffer [konecBufferu] = $0;
konecBufferu = (konecBufferu + 1) % 10;
}
END {
if (NR > 0)
{
if (NR < 10)
{
indexVBufferu = 0;
}
else
{
indexVBufferu = konecBufferu;
}
do
{
print buffer[indexVBufferu];
indexVBufferu = (indexVBufferu + 1) % 10;
}
while (indexVBufferu != konecBufferu)
}
} - Napište awk skript, který na vstupu očekává soubor ve formátu csv,
tj. na každé řádce jsou položky oddělené čárkami, přičemž je jich na
každé řádce stejně. První řádka obsahuje hlavičku, tj. názvy sloupců,
první sloupec následujících řádků obsahuje název řádku. Na dalších
pozicích jsou čísla. Skript k tomuto vstupu přidá na výstupu nový
sloupec se jménem „Součet“, který na každém řádku bude obsahovat
součet číselných položek na daném řádku. Tj. například ze vstupu
Jméno,body1,body2,body3 Hynek,3,12,9 Jarmila,7,34,1 Vilém,8,27,0
vytvoří na výstupuJméno,body1,body2,body3,Součet Hynek,3,12,9,24 Jarmila,7,34,1,42 Vilém,8,27,0,35
(Ukázat řešení)#!/usr/bin/awk -f
BEGIN { FS=","; OFS="," }
FNR == 1 { $(NF+1)="Součet"; print }
FNR > 1 {
$(NF+1)=0
for (i=1; i < NF; i++) {
$NF+=$i
}
print
}
Domácí úkol:
- Napište awk skript, který formátuje soubor po odstavcích do
zadaného počtu sloupců (=znaků na řádek). Jednotlivé odstavce jsou od
sebe odděleny volným řádkem, tj. několik řádek za sebou tvoří týž
odstavec, pakliže mezi nimi není žádná prázdná řádka. Program vždy
vezme odstavec a napíše ho na co nejmenší počet řádků tak, aby k
přechodu na nový řádek nedocházelo uprostřed slova. Počet sloupců bude
zadaný jako parametr předaný pomocí
-v
. [3 body]
13. cvičení (12. května 2015)
- Napište skript, který dostane čtyři parametry: dir,
mask, regexp a repl. Skript poté ve všech
souborech v podstromu adresáře dir, jejichž název vyhovuje
shellové masce mask provede náhradu všech výskytů řetězců,
které vyhovují regulárnímu výrazu regexp za řetězec
repl.
Například volánískript src '*.c' 'prom1' 'prom2'
by provedlo náhradu všech výskytů řetězce prom1 za řetězec prom2 ve všech souborech s příponou .c v podstromu adresáře ./src. - Napište skript newfrom, který dostane jména dvou adresářů DIR1 a DIR2. Soubory z DIR1, které v DIR2 nejsou nebo jsou novější než v DIR2, zkopíruje do DIR2. Skript pracuje rekurzivně i v podadresářích. Pokud přidáme volitelný přepínač -n, skript nebude kopírovat, bude pouze vypisovat, které soubory by se měly zkopírovat. Pokud DIR2 neexistuje, tak by měl být vytvořen, což platí i pro podadresáře.
-
Vytvořte skript,
který převede ve vstupním souboru komentáře ve stylu C (tj. /* */) do
stylu C++ (tj. //). Upozorňuji předem na následující skutečnosti:
- Některé C komentáře jsou jednořádkové a některé víceřádkové.
- Pokud za komentářem ještě něco je, musíte to přesunout na nový řádek.
- Na řádku může být více komentářů.
- Ve standardním sedu nelze psát komentář # na stejný řádek za nějaký příkaz a nelze v něm použít \n v nahrazující části příkazu s (v té s regulárním výrazem ano)! (Místo toho pužijete explicitní odřádkování s \ před ním.
- Napište skript, který dostane počet sekund sec a
název programu prg a poté následují argumenty, které je třeba
programu předat. Skript spustí program a poté čeká, než skončí, pokud
program neskončí do zadaného limitu daného parametrem sec,
pak skript program ukončí odesláním signálu TERM. Poté počká 5 sekund
a pokud ani poté program neskončil, pošle mu signál KILL, kterým
program zabije.
Pro jednoduchost můžete předpokládat, že interval daný počtem nezasahuje přes půlnoc do dalšího dne.
Domácí úkol:
- Napište skript
rmexcept
, který očekává n+1 parametrů, kde první parametr je adresář a další parametry jsou řetězce popisující názvy souborů (může jít o shellové masky) v tomto adresáři. Skript v zadaném adresáři vymaže všechny soubory, jejichž názvy nevyhovují maskám zadaným v parametrech. Soubory, které vyhovují zadaným maskám a názvům, skript nechá beze změny, přičemž se jich ani netkne (tedy speciálně nebude je přesouvat mimo a pak zpět). Například volání
rmexcept . '*.jpg' '*.png'
by v aktuálním adresáři vymazalo všechny soubory, které nemají příponu jpg ani png. [3 body]
14. cvičení (19. května 2015)
Doplňkové domácí úkoly (pokud nemáte dost bodů).
- Vytvořte skript pro sed, který na řádce očekává číslo n zadané v desítkové soustavě. Na výstup vypíše sed čísla 0, 1, ..., n, každé na nové řádce. [2 body]
- Napište skript, který ze souborů jejichž názvy jsou zadané v
parametrech, vybere n nejdelších řádek, u každé řádky je
uvedeno pořadí ve vstupním souboru (ve formátu číslo řádky:
řádka), řádky jsou na výstupu uspořádány podle pořadí ve
vstupním souboru (tj. podle toho čísla před dvojtečkou na výstupu).
Parametr n je implicitně 5, nebo může být zadán jako první
jeden až dva parametry pomocí
-n10
, nebo-n 10
(to je pro příklad n=10, tj. syntaxe je stejná jako třeba uhead
). Pokud chybí parametry s názvy souborů, čte se standardní vstup. Pokud jsou zadány alespoň dva soubory, pak je výpis nejdelších řádek z každého souboru uvozen názvem souboru. [3 body] - Napište skript, který bude ve vstupním textovém souboru provádět
indentaci řádků podle hloubky zanoření daném závorkami. Parametry
skriptu bude název znak indentace c, počet znaků na úroveň
n a seznam souborů (pokud chybí soubory, čte se standardní
vstup). Skript před každou řádku vloží k*n znaků c,
kde k je hloubka zanoření počátku řádku v závorkách, uvažují se kulaté,
hranaté a složené závorky. Pokud jsou před začátkem textu na řádce již
prázdné znaky, tak jsou nejprve odstraněny. Například vstupní
soubor
a ( b c d [ e ] f [ g h { j ( k ) } l m n ] o ) p q r
by volání skriptu s parametrem c='.' a počtem n=1 upravilo do následující podoby:a ( b .c d [ e ] f [ ..g h { j ( ....k ) } l m ..n ] o ) p q r
Můžete předpokládat, že vstup je správně uzávorkovaný (pokud není, je chování nedefinované). Dá se očekávat, že znakem může být mezera nebo tabelátor. [3 body]