Něco málo o sedu: příklady použití

8. 7. 2003
Doba čtení: 5 minut

Sdílet

Sed je nejen mocný, ale podle ohlasů na první díl seriálu i velice populární nástroj. Dnes budeme v povídání o něm pokračovat konkrétními příklady použití - podíváme se na jednoduché, až triviální skripty, ale nakousneme i ty složitější.

Sám pro sebe jsem si rozdělil sedovské skripty do tří kategorií:

  • triviality, často jednopříkazové ‚skripty‘, typicky příkaz s
  • složitější skripty s příkazy pro práci s pracovním a paměťovým prostorem (h, H, g, G, x) a skripty, které kromě předchozího obsahují skoky a cykly, příkazy N;P;D a víc řádků v pracovním prostoru
  • vychytávky a šílenosti

Triviality ne vždy triviální

Trable s jednoduchými skripty většinou plynou z neznalosti regulárních výrazů.

Problémy mohou plynout i z drobných rozdílů mezi různými verzemi sedu. Kromě obligátního odkazu na manuálové stránky a na stránku sedovských one-linerů (http://sed.sou­rceforge.net/sed1li­ne.txt), dodejme několik tipů:

Jemnosti adresování

Jak funguje rozsah /RE1/RE2/příkaz?

Sed začne aplikovat příkaz na řádce vyhovující RE1 a na řádkách následujících, až do řádky, na které najde RE2 včetně. Poté opět nedělá nic, až do dalšího výskytu RE1 atd…

$ sed -n '/start/,/stop/p' text
radka 2 start
radka 3
radka 4 stop
radka 6 start-stop
radka 7
radka 8
radka 9
radka 10

Proč výpis neskončil řádkou 6? Pokud sed najde RE1, hledá RE2 až odnásledující řádky (na rozdíl od awk).

Dále by nás nemělo zaskočit, že zadáme-li rozsah řádek číselně od větší než do, provede se příkaz také a to pro řádku od.

5,6 p  tiskne řádky 5 a 6
5,5 p  tiskne řádku 5
5,3 p  tiskne řádku 5 (!)

Vhodné uvozovky

Nejlépe jednoduché, dvojité použijme pouze pokud potřebujeme do skriptu propašovat shellovou proměnnou.

Příklad chyby: chceme vytisknout poslední řádku souboru

$ sed -n "$p" text

Rozdíly v chování regulárních výrazů

Původní sed nedovoluje použít \n v nahrazovací části s příkazu. Také podporuje pouze BRE, basic regular expressions, viz man 5 regexp. Nepodporuje plus, otazník a svislítko.

Gnu sed umí \n, \t, a čtyřkové verze s volbou -r používají ERE, ansi sekvence a \osmičkové\de­cimální\hexa sekvence. Nedokumentovaná volba -r zapínající ERE existuje už v sedu 3.02.

Příklad: chci nový řádek za každým výskytem znaku @
pouze gnu sed 4.xx: sed -e ‚s/@/@\n/g‘

Odlišné chování příkazů s a N

Dojde-li k náhradě s/vzor/obraz/p, v kterém případě se tiskne a kdy ne?

  • některé implementace tisknou pattern space pouze při sed -n (jinak spoléhají se na defaultní výstup)
  • všechny Gnu sedy tisknou s///p vždy

Od verze Gnu sed v3.02.80 a vyšší příkaz N na poslední řádce vytiskne pracovní prostor. Původní sed tiše zhasl, jak to popisuje i kniha Sed & Awk,

$ echo "jedna radka" | sed  -e 'N'
$

zatímco modernější vypíší

$
echo "jedna radka" | sed  -e 'N'
jedna radka
$

Oprava tradičních skriptů: místo starého „N“ vložit sekvenci"$d;N"

Co připojím na konec, je až na konci

Pro příkazy a (append), c (change), ale také r soubor jde výstup mimo pracovní prostor a připojuje se až za výstup sedu pro zpracovávaný cyklus.

Příkaz i (insert) tiskne svůj výstup i uprostřed zpracování aktuálního cyklu, tiskne prostě tam, kde ve skriptu je.

Není na světě jenom sed …

Místo složitého regexpu nebo sedscriptu může být pohodlnější použít pajpu a kombinovat s prográmky jako tr, cut, tac …, nebo využít výrazy shellu jako je ${foo%%neco}.

Chci vynechat čtyři poslední řádky

tac | sed '1,4d' | tac

(no jistě, tady by si vystačil tail, je to jenom příklad)

Složitější skripty, s pokročilejšími příkazy nebo cykly

Pokročilejší skripty využívají schopnost pracovního prostoru načíst do sebe víc než jednu řádku. Pracovní a paměťový prostor se používá jako roura.

Porovnávání s předchozí řádkou

Jednoduchý příklad využití paměťového prostoru je sedovská emulace příkazu uniq.

sed 'x;G;/^\(.*\)\n\1$/d;g'

Porovnávání s předešlou řádkou a použití bloku umožnuje udělat akci na začátku, nebo konci výskytu sekvence řádků obsahující VZOR.

# provede něco na řádce za blokem řádek obsahujícím VZOR
/VZOR/!{
x
/VZOR/i\
AKCE - KONEC VZORu
x
}
h
#
--- end ---

Stavová informace v sedu

Obecně vzato – jakými způsoby lze v sedu uchovávat informaci „v prohledávaném textu se nacházím mezi značkami begin a end “? Následující příklady pocházejí od Grega Ubbena – cílem je vstup kopírovat na výstup vyjma řádek mezi značkami begin a end (značky leží na začátku řádky).

Metoda 1 – používá paměťový prostor

Jsem-li na řádce za begin a není-li to zrovna řádka end, v paměťovém prostoru mám schovanou značku begin.

/^end/ h
x
/^begin/{ x; d; }
x
/^begin/ h

Metoda 2 – zda jsem v bloku, mi říká pozice v programu

Kvůli zpracování vnitřku bloku se v programu nachází extra smyčka.

#n run this script using the sed -n flag
/^begin/{
p
: loop
n
/^end/!b
loop
}
p

Metoda 3 – používám rozsah adres

Pokud jde o bloky řádek, v daném případě asi nejhezčí postup.

/^begin/,/^end/{
  /^begin/b
  /^end/b
  d
}

Transformace výseku řádky „hsGs“

Stane se, že z řádky na vstupu potřebujeme transformovat jenom výsek. Originální řádku si nejprve uschováme do paměti, v pracovním prostoru si jí ořízneme a zmutujeme podle potřeby a nakonec sestavíme výsledek.

h
s/zacatek\(VZOR\)zbytek/\1/
# tranformace vzor-->cil
...
G
s/\(.*\)\n\(zacatek\)VZOR\(zbytek\)/\2\1\3/
# nyni mam   zacatekCILkonec

Podobně lze obejít fakt, že v nahrazovací části s-příkazu původního sedu nelze použít newline.

h
s/.*//
G
s/^\(\n\)puvodni radka/v nahrade pouzivam \1 jako newline/

Paměťová omezení

Originální sed omezoval objem pracovního i paměťového prostoru. Tyto proměnné byly původně určeny pro uchovávání několika málo řádek a jejich maximální velikost je třeba vyhledat v manuálových stránkách (např. 8192 bytů). Gnu implementace toto omezení nemá.

Příklad – emulace tac, výpis souboru s převráceným pořadím řádek

sed -n '1!G; h; $p'

pro velké soubory s non-gnu sedem selže.

Ve skriptech obsahující cykly se sed často nespoléhá na implicitní vstup a výstup na začáttku a na konci, ale cyklí a cyklí a mezitím čte a zapisuje si po svém.

Kolovrátek N;P;D

Zhusta používaná technika se dá nazvat cyklus N;P;D. V našem modelovém příkladě vynecháváme čtyři poslední řádky, čistě sedovsky

sed -e ':a' -e '$d;N;2,4ba' -e 'P;D'

Sed je stream editor, vstup zpracovává postupně a pouze jednou, takže netuší, kolik řádek v souboru je. Když chceme mazat čtyři poslední řádky, vytvoříme si z pracovního prostoru 4-řádkovou rouru, kterou na posledním řádku ($d) zahodíme. Do té doby (tj. od pátého řádku dál) vždy z výfuku roury vytiskeme řádek (P), z roury ho odstraníme (D) a rouře do tlamy natlačíme řádek nový (N). Na začátku skriptu si rouru musíme tiše naplnit pomocným cyklem (2,4ba).

bitcoin_skoleni

Mazaná řádka bude vždy aspoň jedna, což plyne ze způsobu, jakým sed nakládá s rozsahem adres.

To by pro dnešek stačilo. Příště dokončíme složitější skripty a dojde i na zmíněné vychytávky a šílenosti.

Autor článku