Sed (Stream Editor)


Zdravím všechny Linuxáře! V dnešním článku se podíváme pod kapotu fantastického stream editoru, SEDu. Je to opravdu šikovný nástroj a vyplatí se ho umět používat. Dokáže Vám ušetřit spoustu času. Přejdu rovnou na příklady.

sed_zakladni_diagram

Několik příkladů na zahřátí


cat input_file.txt

1;user=xx;password=4587xyqweas
2;user=aa;password=tyommlxcx
3;user=bb;password=asd55787oipk
4;user=cc;password=asda5sadsd
5;user=dd;password=lqwkndlkd
6;user=ee;password=asdajdhaskjwlq
7;user=ff;password=wertrgfsdssa
8;user=gg;password=4444sdasdsad
9;user=hh;password=asdas5d2sa5d
10;user=ab;password=as25d4a25d
11;user=ac;password=asda2d2qw5e
12;user=ad;password=fdg7a57
13;user=dd;password=sdfsac75

Ze souboru vytiskni řádky 1, 3 – 5.


sed -n -e 1p -e 3,5p input_file.txt

1;user=xx;password=4587xyqweas
3;user=bb;password=asd55787oipk
4;user=cc;password=asda5sadsd
5;user=dd;password=lqwkndlkd

Proveď náhradu středníků za znak „|“ na posledním řádku


sed '$s/;/\|/g' input_file.txt

...
12;user=ad;password=fdg7a57
13|user=dd|password=sdfsac75

Na řádcích 1 – 5 nahraď slovo password za passwd


sed '1,5s/password/passwd/' input_file.txt

1;user=xx;passwd=4587xyqweas
2;user=aa;passwd=tyommlxcx
3;user=bb;passwd=asd55787oipk
4;user=cc;passwd=asda5sadsd
5;user=dd;passwd=lqwkndlkd
6;user=ee;password=asdajdhaskjwlq
7;user=ff;password=wertrgfsdssa
...

Na řádcích kde se vyskytuje uživatel, jehož počáteční písmeno je „a“, udělej substituci: user -> USER, password -> PASSWORD a hesla nahraď řetězcem „no_passwd“.


sed -n -e '/user=[a].*/{s/user/USER/;s/password=\(\S\+\)/PASSWORD=no_passwd/p}' input_file.txt 

2;USER=aa;PASSWORD=no_passwd
10;USER=ab;PASSWORD=no_passwd
11;USER=ac;PASSWORD=no_passwd
12;USER=ad;PASSWORD=no_passwd

Od 4. řádku vytiskni každý druhý řádek.


cat numbers.txt
1
2
3
4
5
6
7
8
9
10
sed -n '4~2p' numbers.txt
4
8
10

Vypiš obsah, který se nachází mezi řádkem začínajícím na „BEGIN_57sa1d5468ser6ASA53s4“ a řádkem začínajícím na „END_57sa1d5468ser6ASA53s4“

cat begin_end.txt
*****
BEGIN
********
BEGIN_57sa1d5468ser6ASA53s4
> 1\ tento obsah by mel byt vytisten
> 2\ tento obsah by mel byt vytisten
> 3\ tento obsah by mel byt vytisten
END_57sa1d5468ser6ASA53s4
**********
**
END
sed -n -e '/BEGIN_57sa1d5468ser6ASA53s4/,/END_57sa1d5468ser6ASA53s4/p' begin_end.txt | sed '1d;$d'
> 1\ tento obsah by mel byt vytisten
> 2\ tento obsah by mel byt vytisten
> 3\ tento obsah by mel byt vytisten

Hrátky s bufferem

Vytištění řádků ze souboru v obráceném pořadí

cat numbers.txt
1
2
3
sed '1!G;h;$!d' numbers.txt
3
2
1

Jak to celé funguje? Sed zde obsahuje 3 příkazy:

  • 1!G – pokud se nejedná o 1. řádek provádět operaci G (přidej obsah hold bufferu do pattern bufferu)
  • h – nahraď obsah hold bufferu obsahem pattern bufferu
  • $!d – pokud se nejedná o poslední řádek, smaž ho

Zde přikládám obsah bufferů v jednotlivých iteracích:

 

Vypsání řádků v opačném pořadí, lze provést i pomocí příkazu:

sed -n '1!G;h;$p' numbers.txt
  • -n přepínač potlačí automatický tisk obsahu pattern space
  • $p pokud se zpracovává poslední řádka ze vstupního souboru, vytiskne se obsah pattern bufferu

 

Krom příkazu sedu můžete použít i program tac . Všimněte si, že slovo tac je pozpátku cat. Příkaz tac dělá v podstatě totéž co výše uvedené sed příkazy, akorát je jeho zápis podstatně jednodušší a srozumitelnější.

Prohoď každé dva řádky.

sed -n 'h;n;p;g;p' numbers.txt
2
1
4
3
6
5
8
7
10
9

Ze vstupu se načte řádek. Řádek uložíme do HOLD bufferu příkazem h, se HOLD buffer přepíše. Přepíšu obsah PATTERN bufferu načtením dalšího řádku ze vstupu příkazem n. Příkazem p vytisknu obsah PATTERN bufferu a tam je číslo 2.


P...PATTERN SPACE/BUFFER
H...HOLD SPACE/BUFFER

P 1 2
H 1 1

Nyní jsme provedli h;n;p.
Nyní přepíšeme obsah PATTERN space (g) obsahem, který se nachází v HOLD bufferu. Obsah PATTERN bufferu vytiskneme a tam je číslo 1.


P 1
H 1

Provedli jsme g;p.
Celý proces se pak opakuje do konce souboru.


*...tisk
Ukázka stavů bufferů:
P 1 2* 1* 	3 4* 3*  5 6* 5*
H 1 1  1 	3 3  3 	 5 5  5

Sestavení instalačního programu

Úkolem bude vytvořit příkaz, kterým naistalujete všechny programy, které jste kdy na PC instalovali. To se může hodit, když chcete na jný PC nainstaloval stejné programy, které máte na svém PC. Seznam programů, které jste na PC kdy instalovali, najdete v historii příkazem history.


history | grep "apt-get install" | cut -d' ' -f7- | grep -v "apt-get install" |
sed ':l;N;$!bl;s/ /\n/g' | sort | uniq |
sed '1s/^/sudo apt-get install/;:l;N;$!bl;s/\n/ /g'

sudo apt-get install audacity dia gnome-paint htop iftop keepassx traceroute vim wireshark
  • grep „apt-get install“ nejprve si vygrepujeme instalační příkazy
  • cut -d‘ ‚ -f7- vytiskneme pouze názvy instalovaných programů
  • grep -v „apt-get install“ jelikož se do historie ukládá i příkaz, který voláme, jenž obsahuje také apt-get install, jednoduše se ho zbavíme přes přepínač -v
  • sed ‚:l;N;$!bl;s/ /\n/g‘
    • l zde představuje tzv. branch neboli větev, lze ji i pojmenovat např. jako loop, ale i jinak
    • N načte další řádek ze vstupu to tzv. Pattern space
    • $! dokud se nejedná o konec souboru
    • b skočí na větec, která následuje za tímto písmenem a to je l
    • tedy do Pattern space se načte vše a poté se na to aplikuje jednoduchá substituce s/ /\n/g, která mezery za nové řádky, tedy nestane se, aby více programů bylo zapsáno v jednom řádku
    • nyní tedy máme seznam programů, který však může obsahovat duplicity
  • sort | uniq setřídíme a získáme unikátní seznam programů
  • sed ‚1s/^/sudo apt-get install/;:l;N;$!bl;s/\n/ /g‘ toto je velmi obdobné jako v předchozím sedu, nicméně je tu navíc připojení příkazu sudo apt-get install
    • 1s/^/sudo apt-get install/ přidej na první řádek řetězec „sudo apt-get install“

No a to je vše. Nové řádky lze samozřejmě odstranit jednodušším zápisem:


tr '\n' ' ' nazev_souboru.txt

nebo nahradit nové řádky a to pomocí příkazu:


tr -d '\n' nazev_souboru.txt

Odstranění hesel ze souboru

Pojdmě se podívat jak se jednoduše zbavit citlivých dat. Dejme tomu, že všechna naše citlivá data se nacházejí v uvozovkách. V souboru budeme chtít nahradit prostor mezi uvozovkami řetězcem „cleared“.


cat input_file.txt
Citlivá data v uvozovkách "Heslo97"
Také tu jsou citlivá data "Data ke smazání"

sed -e ':loop

$!{
N
/\n$/!b loop
}

s/\"[^\"]*\"/--cleared--/g' input_file.txt
  • :loop definice branch
  • $! dokud není konec souboru tak prováděj příkazy v chlupatých závorkách { }
  • N načti do pattern space další řádek
  • /\n$/!b loop pokudse nejedná o poslední řádek, skoč znovu na branch pojmenovanou jako loop
  • s/\“[^\“]*\“/no_passwd/g potom co se vše načte do bufferu tak provádíme substituci

Citlivá data v uvozovkách --cleared--
Také tu jsou citlivá data --cleared--

Práce se souborem (append, insert, replace)


cat test_for_formating.txt
Nadpis 1
zde je nejaky text v nadpisu 1
adresa xx
tel 456

Nadpis 2
zde je nejaky text v nadpisu 2
adresa xzzz
tel 20

Nadpis 3
zde je nejaky text v nadpisu 3
adresa ta
tel 71

#!/bin/bash
cat test_for_formating.txt | sed -e '/^\s*$/d' \
-e '/^zde/,/^adresa/{ 's/^/##/' }' \
-e '/^tel/c telefon-nahrazen' \
-e '/^[Nn]adpis\s[0-9]\+\s*$/i ------------------' \
-e '$a ------------------' \
-e 's/^\(Nadpis\s\+[0-9]\+\s*\)/<h1>\1<\/h1>/'

V jedno-řádkové podobě:
cat test_for_formating.txt | sed -e '/^\s*$/d' -e '/^zde/,/^adresa/{ 's/^/##/' }' -e '/^tel/c telefon-nahrazen' -e '/^[Nn]adpis\s[0-9]\+\s*$/i ------------------' -e '$a ------------------' -e 's/^\(Nadpis\s\+[0-9]\+\s*\)/<h1>\1<\/h1>/'
  • /^\s*$/d odstraní prázdné řádky nebo řádky obsahující pouze bílé znaky
  • /^zde/,/^adresa/{ ‚s/^/##/‘ } zakomentuje řádky počínaje od řádku začínajícím na „zde“, až po řádek začínající na „adresa“
  • /^tel/c telefon-nahrazen nahradí řádek, začínající na „tel“ řádkem obsahující text „telefon-nahrazen“
  • /^[Nn]adpis\s[0-9]\+\s*$/i —————— před nadpisy vloží oddělovací čáru z pomlček
  • $a —————— vloží oddělovací čáru na konec souboru
  • s/^\(Nadpis\s\+[0-9]\+\s*\)/<h1>\1<\/h1>/ nadpisy obalí html tagem h1
------------------
<h1>Nadpis 1</h1>
##zde je nejaky text v nadpisu 1
##adresa xx
telefon-nahrazen
------------------
<h1>Nadpis 2</h1>
##zde je nejaky text v nadpisu 2
##adresa xzzz
telefon-nahrazen
------------------
<h1>Nadpis 3</h1>
##zde je nejaky text v nadpisu 3
##adresa ta
telefon-nahrazen

V sedu se dají dělat všelijaké kousky. Tak to jenom v krátkosti na úvod. Jinak vše potřebné se dočtete v manuálových stránkách. Mohu doporučit knihu Learn SED, která je dostupná zdarma.