Konsola nie gryzie – operacje na tekście (część pierwsza) 22


Kilka miesięcy temu toczyliśmy kolejną luźną i niezobowiązującą dyskusję o systemach operacyjnych i aplikacjach, z których zazwyczaj korzystamy.  W czasie rozmowy zrodził się pomysł, by „sprzedać” szerszej publiczności część naszej wiedzy na temat lekkich menadżerów okien stworzonych na bazie Blackboksa — tak właśnie  powstał ten oto artykuł napisany na użytek Czytelni. Ponieważ wspólne pisanie okazało się być całkiem interesującym doświadczeniem, postanowiliśmy je kontynuować. Przy okazji wyboru tematu naszego następnego artykułu zdecydowaliśmy się jednak pójść o krok dalej: tym razem będzie mowa tylko i wyłącznie o konsoli.

Poruszane tutaj zagadnienie w logiczny sposób wiąże się z naszym poprzednim tekstem. Skoro była mowa o lekkich menedżerach okien, naturalnym następstwem ich obecności w systemie jest używanie „lekkich” programów i korzystanie z mniejszej ilość graficznych aplikacji czy konfiguratorów. Taka sytuacja prędzej lub później musi zaowocować zdecydowanie częstszym uruchamianiem konsoli.

Konsola to narzędzie o olbrzymich możliwościach. Wie o tym niemal każdy, kto zetknął się z nią przez dłuższą chwilę. Mnogość aplikacji i „klepanych na piechotę” poleceń przyprawia jednak o zawrót głowy, często więc spotyka się reakcję typu: „Konsola? O nie, bardzo dziękuję…”. W tym artykule postanowiliśmy pokazać, że konsola nie gryzie i skupić się na czymś podstawowym, a dokładniej — na przetwarzaniu tekstu. System GNU/Linux to w większości pliki tekstowe, którymi można bardzo szybko i wygodnie zarządzać właśnie przy wykorzystaniu narzędzi konsolowych. Racjonalnym rozwiązaniem wydaje się więc rozpoczęcia „oswajania” konsoli od tego właśnie tematu. Poruszane tutaj zagadnienie było już po części omówione na łamach Czytelni Ubuntu.pl — w artykule Bruce’a Byfielda. Mimo to zdecydowaliśmy się napisać nasz artykuł — chociażby po to, żeby niektóre kwestie opisać obszerniej. Poniższy tekst można by uznać za uzupełnienie informacji z wspomnianego artykułu, przede wszystkim należy go jednak traktować jako niezależną i samodzielną całość.

WYŚWIETLANIE TEKSTU
Chyba wystarczy już tego wstępu — pora przejść do działania i przywitać się z konsolą. Najprostszą komendą, jaką można wydać, jest „wyświetl” — do tego celu służy echo. Użycie tej komendy jest bardzo proste:

Przykład 1.
echo "Tekst wyświetlany"

Tekst wyświetlany

Sam tekst może być objęty cudzysłowem pojedynczym lub podwójnym — chociaż nie jest to konieczne do działania komendy. Co zatem zyskujemy w ten sposób? Zastosowanie cudzysłowu pozwala nam przenieść wyświetlany tekst na kilka linii (np. z powodu jego długości, czy też przez nasze widzimisię).

Przykład 2.
echo "To jest tekst
na dwie linie"

To jest tekst
na dwie linie

Stosując backslash, możemy w trakcie podawania ciągu znaków do wyświetlania „załamać tekst” — będzie on jednak wyświetlany w jednej linii.

Przykład 3.
echo To jest tekst
na trzy linie, ale
efekt polecenia wyświetli tekst w jednej linii.

To jest tekst na trzy linie, ale efekt polecenia wyświetli tekst w jednej linii.

Uważny czytelnik spostrzegł zapewne, że nie każdy znak wypisany po komendzie echo jest wyświetlany na ekranie. Istnieje grupa znaków specjalnych (należą do nich m.in.: , oraz ), które są interpretowane przez polecenia jako coś więcej aniżeli zwykły znak. Cudzysłów zawiera blok tekstu, natomiast pozwala na załamanie linii — wciśnięcie ENTER-a bez zatwierdzenia wykonania komendy. Pojedynczy cudzysłów powoduje dodatkowo interpretowanie wszystkich znaków wewnątrz bloku dosłownie (warto spojrzeć, jak wyświetlane są zapisane w ten sposób zmienne). Podobny efekt dla pojedynczego znaku specjalnego można uzyskać, stawiając przed nim symbol backslasha.

Przykład 4a.
echo "Ten tekst \
zajmuje dwie linie. A oto przykład zmiennej - $HOME"

Ten tekst 
zajmuje dwie linie. A oto przykład zmiennej - /home/k2cl

Przykład 4b.
echo 'Ten tekst
zajmuje dwie linie. W tym cudzysłowu zmienna ta wygląda tak - $HOME'

Ten tekst 
zajmuje dwie linie. W tym cudzysłowu zmienna ta wygląda tak - $HOME

Przykład 4c.
echo Żeby wyświetlić znak specjalny, robimy coś takiego: "

Żeby wyświetlić znak specjalny, robimy coś takiego: "

Omawiając echo, warto wspomnieć o opcji -e, która interpretuje specjalne sekwencje takie jak n (znak nowej linii) oraz t (znak tabulacji). Odsyłamy do manuala echo po więcej informacji na ten temat (man echo jest stosunkowo krótki).

Przykład 5a.
echo -e "Ten tekst n jest wyświetlany w dwóch liniach"

Ten tekst
 jest wyświetlany w dwóch liniach

Przykład 5b.
echo "Ten kod n mimo, iż zawiera znak nowej linii, wyświetli się w jednej."

Ten kod n mimo, iż zawiera znak nowej linii, wyświetli się w jednej.

ŁĄCZENIE PLIKÓW I ICH WYŚWIETLANIE
Ponieważ kwestię wyświetlania wpisanego tekstu mamy już za sobą, możemy zająć się prezentowaniem danych, które znajdują się „gdzieś w systemie”. Podstawowym poleceniem jest „wyświetl zawartość plików” — cat, który łączy podane mu w argumentach pliki i wyświetla całość na ekranie. Komenda ta ma swoją bliźniaczą siostrę, mianowicie tac. Jej działanie polega na wypisaniu zawartości podanych plików na ekranie według kolejności z polecenia, jednak wiersze wypisywane są od ostatniego do pierwszego. Można uznać, że to „bajer”, ale okazuje się być całkiem przydatny, kiedy interesujące nas informacje znajdują się gdzieś na samym początku pliku o ciut większych rozmiarach.

PAGERY
Skoro opisujemy rozmaite sposoby wyświetlenia zawartości danego pliku, pora chyba omówić pagery — programy, które można nazwać konsolowymi przeglądarkami plików tekstowych. Najbardziej podstawowym jest more — program domyślnie instalowany chyba w każdej dystrybucji GNU/Linuksa. Umożliwia on czytanie plików ekran po ekranie (wyświetla na raz taką część pliku na jaką pozwala wielkość konsoli) i zasadniczo umożliwia intuicyjne przewijanie tylko w jednym kierunku — w dół. Tutaj zdecydowanie lepiej wypada less, który także jest częścią domyślnej instalacji Ubuntu. Pozwala on strzałkami (oraz innymi klawiszami) swobodnie przemieszczać się po pliku we wszystkich kierunkach. Najbardziej intuicyjna próba przewijania tekstu strzałkami spowoduje przesuwanie zawartości pliku o pełny ekran na szerokość, bądź też o jedną linijkę w pionie. Cechą charakterystyczną less jest również fakt, że po dojściu do końca pliku wyświetli nam ładne END, ale nie zakończy działania — pozwalając na komfortowy powrót do wcześniejszych informacji. Z ciekawostek warto też wspomnieć o tym, że less wyświetla pierwszy ekranu tekstu natychmiast po jego odczytaniu, a nie po otwarciu całego pliku. Jest to szczególnie przydatne, kiedy operujemy na naprawdę dużych plikach i nie mamy ochoty na zbyt długie oczekiwanie. Jeżeli naszego Czytelnika nie satysfakcjonuje tak pobieżny opis tych dwóch programów, zapraszamy do bardziej szczegółowych informacji zawartych we wspominanym już wcześniej artykule.

Alternatywą dla tych dwóch narzędzi może być mało popularny most. Nie jest on domyślnie instalowany w większości dystrybucji GNU/Linuksa — Ubuntu nie jest więc tutaj żadnym wyjątkiem. Jego największą zaletą w stosunku do less jest fakt, że most ustawiony jako domyślny pager wyświetla w X-terminalu strony manuala z kolorowaniem składni. Taki efekt można wprawdzie uzyskać również wykorzystując w roli pagera less, wymaga to jednak ustawienia w systemie kilku dodatkowych zmiennych. Oprócz tego most na dole ekranu przedstawia czytelny status ekranu — położenie kursora oraz procent przejrzanego już pliku/tekstu. Jeszcze niżej wyświetlana jest minipomoc — opis najważniejszych skrótów klawiszowych, służących do obsługi programu.

Domyślny pager możemy ustawić przy pomocy zmiennej PAGER — wystarczy przypisać jej nazwę programu (bądź efektywniej ścieżkę dostępu do pliku wykonywalnego.

Przykład 6.
Aby ustawić domyślny pager na most musimy wykonać polecenie:
PAGER="most"
Natomiast aby ustawienie takie zapisać na stałe powyższą linijkę należy zapisać w pliku konfiguracyjnym powłoki, w przypadku Basha — ~/.bashrc.

WYŚWIETLANIE POCZĄTKU LUB KOŃCA PLIKU
Wyświetlanie tekstu mamy już za sobą — zacznijmy dostosowywać otrzymany wynik do naszych potrzeb. Pierwszymi narzędziami jakie omówimy, będą head oraz tail. Już ich nazwy sugerują sposób działania — wyświetlają one jedynie początek (head) lub koniec (tail) plików podanych jako argumenty polecenia. Domyślnie wyświetlany wynik ograniczony jest do dziesięciu linii tekstu, jednak przy pomocy parametru -n liczba można ustawić ilość podawanych informacji.

Przykład 7.
tail -n 2 /etc/passwd /etc/group

==> /etc/passwd <==
dbus:x:81:81:System message bus:/:/bin/false
hal:x:82:82:HAL daemon:/:/bin/false

==> /etc/group <==
hal:x:82:
rfkill:x:24:

Powyższe polecenie pokaże nam na przykład dwóch ostatnio dodanych użytkowników systemu oraz dwie ostatnio utworzone grupy. Dodatkowo wyniki są gustownie podzielone nagłówkami informującymi o tym, jakiego pliku dotyczą informacje. To jedna z cech, które odróżniają cat od head — ten pierwszy łączy dane z różnych plików i wyświetla je bez żadnego podziału.

Komendy head oraz tail mają jeszcze jedną przydatny parametr — -c liczba pozwala na wyświetlenie zadanej liczby znaków z początku lub końca pliku. Coś takiego przydaje się zwłaszcza przy pisaniu skryptów.

POTOK — ŁĄCZENIE POLECEŃ
Polecenia head oraz tail często przydają się do filtrowania wyniku innych komend. Zanim jednak zaczniemy omawiać to zagadnienie, musimy poruszyć jeszcze jedną kwestię — potoki. Ten miły wynalazek, zwany czasem „składaniem klocków Lego”, pozwala łączyć polecenia w szereg — filtrując jednym poleceniem wynik poprzedniego. Jak łatwo się domyślić, coś takiego będzie szczególnie przydatne przy przetwarzania tekstu, kiedy to z dużej ilości informacji dostarczonej przez pierwsze polecenie potrzebujemy jednego, za to precyzyjnie określonego drobiazgu. Żeby całość zadziałała tak, jak sobie tego życzymy, polecenia łączymy znakiem |. Ten symbol informuje powłokę systemu, że wynik polecenia nie będzie wyświetlony na ekranie, zostanie natomiast przekierowany do drugiego polecenia.

Jak to działa w praktyce? Przyjmijmy, że interesuje nas zawartość ósmej od końca linii z pliku /etc/passwd. Wyświetlenie całego wyrzuci na ekran sporo niepotrzebnych danych — komu chciałoby się jeszcze liczyć, który wiersz jest ósmym od końca? Użycie samego tail ułatwi nam sprawę, ale mocno zapaskudzi ekran. Zgrabne połączenie dwóch komend pozwoli nam uzyskać estetyczny efekt pokazany w Przykładzie 8.

Przykład 8.
tail -n 8 /etc/passwd | head -n 1

daemon:x:2:2:daemon:/sbin:/bin/false

Prawda, że proste? tail wycina z interesującego nas pliku osiem ostatnich linii i przekierowuje je do następnego polecenia. Użyty później head wybiera początkowe wiersze (w tym wypadku zażyczyliśmy sobie tylko jeden) i wyświetla interesującą nas informację.

Korzystając z potoków nie powinno się nadużywać polecenia cat. Jest to tylko marnowanie swojego czasu i zasobów komputera — musi on uruchomić zbędny program. Jeśli coś można wykonać bez użycia tej komendy — należy tak zrobić.

WYŚWIETLANIE POSZCZEGÓLNYCH ZNAKÓW LUB PÓL
Narzędzia head/tail pozwalają nam wyświetlić lub też skierować do dalszej obróbki wybrane linie z konkretnego pliku. Co jednak zrobić, kiedy potrzebujemy „wypreparować” mniejszą porcję informacji? Jeśli interesuje nas pojedyncze słowo czy znak każdej lini, możemy je po prostu wyciąć — czyli posłużyć się narzędziem cut. Sposób jego działania jest prosty — wyświetla zadane informacje z każdej linii pliku, na którym operujemy. Od nas zależy, jakie „porcje” danych chcemy wycinać (-b to bity, -c oznacza znaki, -f wytnie nam pola kolumn) oraz ile ich potrzebujemy. Zasięg działania polecenia ustalamy, podając konkretną liczbę (wtedy otrzymamy bit, znak lub słowo znajdujące się na zadanej pozycji w linii) lub też zakres — zdefiniowany jako przedział zamknięty a-b lub też przedział otwarty (a- lub -b).

Ponieważ plik /etc/passwd jest dość obszerny, dla oszczędności miejsca i większej przejrzystości artykułu utworzyliśmy plik passwd, zawierający jedynie trzy pierwsze linie oryginału:

cat passwd

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false

I bierzemy się do wycinanek:

Przykład 9a.
cut -c -6 passwd

root:x
bin:x:
daemon

Przykład 9b.
cut -c 2-5 passwd

oot:
in:x
aemo

W powyższych przykładach zaprezentowaliśmy wycinanie konkretnych znaków. W pierwszym przypadku interesowała nas treść od początku linii do szóstego jej znaku, w drugim poleceniu był to już przedział od drugiego do piątego znaku.

Kilka akapitów wcześniej wspomnieliśmy o wycinaniu „pól” – w normalnych okolicznościach są one rozdzielone tabulatorem lub spacją, tak więc na dobrą sprawę pola to nic innego jak słowa. W razie potrzeby można jednak zdefiniować własny separator — przyda się do tego parametr -d znak. Dzięki jego wykorzystaniu możemy sprawnie przetwarzać tekst, w którym słowa porozdzielane są trochę bardziej nietypowym symbolem — w przypadku /etc/passwd jest to :.

Przyjmijmy, że z jakiegoś powodu chcemy wyświetlić informacje o kilku użytkownikach (nazwa użytkownika, numery UID oraz GID), ale nie chcemy prezentować informacji o ich katalogach domowych oraz wykorzystywanej powłoce. Skoro wiemy, że interesujące nas informacje to pierwsze cztery pola (ignorujemy w tym przykładzie pole hasła — drugie), możemy łatwo się z tym uporać w taki oto sposób:

Przykład 10.
cut -d : -f -4 passwd

root:x:0:0
bin:x:1:1
daemon:x:2:2

Chociaż dotychczas nie prezentowaliśmy takiego rozwiązania, przedziałów do wycinania może być więcej — w takiej sytuacji informacje te rozdzielamy przecinkami. Tym razem wyświetlimy listę użytkowników pozbawioną numerów GID oraz UID — zatem chcemy wyrzucić z wyniku trzecie i czwarte pole. Można to zrobić w taki oto sposób:

Przykład 11.
cut -d : -f -2,5- passwd

root:x:root:/root:/bin/bash
bin:x:bin:/bin:/bin/false
daemon:x:daemon:/sbin:/bin/false

Należy pamiętać, że jeśli w pliku pola oddzielane są różną ilością spacji, jak to często ma miejsce w pliku /etc/fstab korzystanie z cut będzie możliwe, aczkolwiek może być trochę trudniejsze — dwie spacje to przejście nie o jedną lecz o dwie kolumny, itd.

ZLICZANIE
Czasem nie interesuje nas wyświetlenie zawartości danego pliku, interesuje nas natomiast jego wielkość pliku — i nie chodzi tu o jego rozmiar w MB czy KB, lecz o ilość linii, słów czy też znaków. Oczywiście, możemy liczyć to „na piechotę”, ale konsola potrafi nas w tym wyręczyć, oddając do dyspozycji użytkownika całkiem przyjemne narzędzie wc. Uprzedzając pytania — jego nazwa tylko przypadkowo brzmi tak jak skrót toalety… Tak naprawdę jest to akronim słów word counter (z języka ang. licznik słów). Podając komendę bez żadnych opcji otrzymamy jako wynik trzy liczby — ilość linii, słów i znaków oraz nazwę pliku, który został przetworzony. Podając jako argument więcej niż jeden plik otrzymamy wyniki zliczeń poszczególnych plików oraz wynik całkowity.

Przykład 12.
wc /etc/passwd /etc/group

 10  13 382 /etc/passwd
 32  32 516 /etc/group
 42  45 898 razem

Spośród parametrów z którymi można uruchomić narzędzie wc, z całą pewnością trzeba wspomnieć o -c (zliczanie tylko ilości znaków), -w (policzy wyłącznie słowa) oraz -l (poda jedynie liczbę linii). Dodatkowo -L wypisuje ilość znaków w najdłuższym wierszu pliku.

Przykład 13.
wc -l /etc/passwd

 10 /etc/passwd

ZAMIANA ZNAKÓW
Skoro już potrafimy wyświetlić tekst, wyciągnąć z niego poszczególne linie czy policzyć znaki, pora zainteresować się jego przekształcaniem. Może nam do tego posłużyć chociażby tr (od angielskiego „translate”) — komenda, która potrafi według określonego schematu zamienić znaki na inne. Zadanie w sumie proste, więc i komenda nie jest zbyt skomplikowana — trzeba jednak pamiętać o tym, że tr nie pobierze danych z pliku, a jedynie czyta informacje ze standardowego wejścia. Pisząc bardziej „po ludzku”, tr zasadniczo korzysta z tekstu, który dostarczy mu np. wcześniejsze polecenie w potoku.

Przykład 14.
echo "mała" | tr 'm' 'p'

pała

Jak widać, składnia polecenia jest prosta: tr ‚znak oryginalny’ ‚znak docelowy po zamianie’. Żeby było wygodniej, zamiast pojedynczych znaków możemy korzystać z grup znaków:

Przykład 15.
echo "mała" | tr 'amł' 'AMŁ'

MAŁA

Reguła zamiany jest prosta: pierwszy znak z pierwszej grupy zamienił się w pierwszy znak drugiej grupy, drugi znak pierwszej grupy — w drugi drugiej, itd.

Znaki do zamiany można podawać też przedziałami (np. ‚a-z’) lub też skorzystać ze specjalnych sekwencji rozpoznawanych przez to polecenie. Jeśli ktoś chce poznać je wszystkie, najlepiej będzie zajrzeć do man tr, natomiast tutaj dwa proste przykłady:

Przykład 16a.
echo "abcdefgh" | tr [:lower:] [:upper:]

ABCDEFGH

Przykład 16b.
echo "a b c d" | tr ' ' 'n'

a
b
c
d

Dotychczas wszystkie przekształcenia wykonywane przy pomocy tr oznaczały zamianę znaków. Komenda ta potrafi też coś więcej, mianowicie usuwać wybrane znaki z ciągu danych dostarczonych na wejściu.

Przykład 17.
echo "tekst testowy" | tr -d 't'

eks esowy

SORTOWANIE
Czasem zdarza się, że informacje z jakiegoś pliku trzeba uszeregować w konkretnej (np. alfabetycznej) kolejności. Do takiego działania wykorzystujemy polecenie sort. Jego użycie nie jest zbyt skomplikowane — w najprostszym wariancie wystarczy zdefiniować plik do sortowania. Dla wygody i przejrzystości znów posłużymy się krótkim, spreparowanym plikiem passwd:

cat passwd

root:x:0:0:root:/root:/bin/bash
bin:x:1:0:bin:/bin:/bin/false
daemon:x:10:2:daemon:/sbin:/bin/false

…a oto najprostsze możliwe zastosowanie komendy sort:

Przykład 18.
sort passwd

bin:x:1:0:bin:/bin:/bin/false
daemon:x:10:2:daemon:/sbin:/bin/false
root:x:0:0:root:/root:/bin/bash

Ułożyliśmy linie w kolejności alfabetycznej ich pierwszych znaków.
Jeśli chcemy działać w bardziej precyzyjny sposób, wystarczy zdefiniować sposób szeregowania wierszy (według następujących po sobie liczb czy też liter alfabetu), a także ustalić pole bądź znak, który będzie brany pod uwagę podczas sortowania. Poszczególne pola znów domyślnie rozdziela spacja lub tabulator, tak więc w normalnym układzie są to zwyczajnie kolejne słowa. W przypadku /etc/passwd pola rozdzielone są dwukropkiem, więc powtarza się sytuacja znana z opisu polecenia cut — przy pomocy parametru -t znak musimy zdefiniować separator.

Wystarczy tej teorii, możemy prezentować dalsze możliwości polecenia sort:

Przykład 19.
sort -t : -k 5 passwd

bin:x:1:0:bin:/bin:/bin/false
daemon:x:10:2:daemon:/sbin:/bin/false
root:x:0:0:root:/root:/bin/bash

Ułożyliśmy kolejne linie testowego pliku w kolejności alfabetycznej, przy czym polecenie brało pod uwagę tekst rozpoczynający się od piątego pola (dla większej czytelności zaznaczone pogrubieniem).

Wspomnieliśmy już o możliwości sortowania alfabetycznego lub numerycznego. Różnicę pokażą kolejne dwa przykłady.

Przykład 20a.
sort -t : -k 3 passwd

root:x:0:0:root:/root:/bin/bash
daemon:x:10:2:daemon:/sbin:/bin/false
bin:x:1:0:bin:/bin:/bin/false

Przykład 20b.
sort -t : -k 3n passwd

root:x:0:0:root:/root:/bin/bash
bin:x:1:0:bin:/bin:/bin/false
daemon:x:10:2:daemon:/sbin:/bin/false

W Przykładzie 20b użyliśmy sortowania numerycznego i jak widać, sort ułożył linie w kolejności 0-1-10, zamiast „alfabetycznego” 0-10-1.
Na etapie prostego użycia komendy możemy też pokazać możliwość sortowania w odwróconej kolejności numerycznej – zamiast -k 3n z Przykładu 20b. wystarczy podstawić -k 3nr:

Przykład 20c.
sort -t : -k 3nr passwd

daemon:x:10:2:daemon:/sbin:/bin/false
bin:x:1:0:bin:/bin:/bin/false
root:x:0:0:root:/root:/bin/bash

Oczywiście, może się zdarzyć, że w niektórych liniach wartość pola według którego chcemy szeregować informacje jest identyczna. Nic nie stoi na przeszkodzie, żeby w takiej sytuacji podać kolejne kryterium sortowania. W poprzednim przykładzie wartości czwartego pola były dla dwóch linii identyczne („0”), przesortowaliśmy plik passwd względem czwartej kolumny, a następnie — trzeciej.

Przykład 21a.
sort -t : -k 4n -k 3n passwd

root:x:0:0:root:/root:/bin/bash
bin:x:1:0:bin:/bin:/bin/false
daemon:x:10:2:daemon:/sbin:/bin/false

Przykład 21b.
sort -t : -n -k 4 -k 3 passwd

root:x:0:0:root:/root:/bin/bash
bin:x:1:0:bin:/bin:/bin/false
daemon:x:10:2:daemon:/sbin:/bin/false

Opcji -n oraz -r można używać zarówno przy definiowaniu klucza do sortowania (Przykłady 20b–c, 21a) jak i uruchomić ją „globalnie” (Przykład 21b).

Poza sortowaniem według wartości całego pola, wyniki można też ułożyć w kolejności wyznaczonej przez dany znak któregoś z pól (w tym konkretnym przypadku – trzeci znak piątego pola):

Przykład 22.
sort -t : -k 5.3 passwd

daemon:x:10:2:daemon:/sbin:/bin/false
bin:x:1:0:bin:/bin:/bin/false
root:x:0:0:root:/root:/bin/bash

POWTARZAJĄCE SIĘ LINIE
Oprócz sortowania informacji w pliku, przydatna bywa też możliwość zarządzania liniami, które wielokrotnie się powtarzają. Z pomocą przyjdzie nam komenda uniq, a przy prezentacji jej możliwości pomoże nam kolejny spreparowany plik test:

cat test

root
root
bin

Zamiast teorii zacznijmy od prezentacji najprostszego zastosowania polecenia:

Przykład 23.
uniq test

root
bin

Uniq użyty bez żadnych parametrów po prostu usuwa z pliku powtarzające się informacje. To polecenia ma jednak znacznie większe możliwości: parametr -c poda nam informację, ile razy powtórzyła się dana linia, -d pokaże na wyjściu jedynie te linie, które się powtarzają, natomiast -u zrobi coś odwrotnego — zaprezentuje wyłącznie linie unikatowe (Przykład 24).

Przykład 24a.
uniq -c test

      2 root
      1 bin

Przykład 24b.
uniq -d test

root

Przykład 24c.
uniq -u test

bin

Należy mieć na uwadze, że sort umie porównać ze sobą jedynie wiersze sąsiadujące — z tego powodu należy plik pierw posortować.

ZAPISANIE DANYCH DO PLIKU I JEGO DALSZA OBRÓBKA
Czasami przydatna jest możliwość zapisania wyniku jakiegoś polecenia do pliku i jego dalsza obróbka — taką funkcjonalność zapewnia polecenie tee. Potrafi ono zapisać interesujące nas dane do nowego pliku, lub też dodać je do pliku który już istnieje (w tym celu należy posłużyć się opcją -a). Prostą prezentacją działania polecenia może być przekształcenie ostatniego przykładu dla polecenia uniq:

Przykład 25.
uniq -u test | tee test1

bin

Wyświetlony wynik nie różni się w niczym od tego otrzymanego w Przykładzie 24c, ale w tym przypadku identyczne dane zostały zapisane w pliku test1 znajdującym się w katalogu, w którym operujemy.

WYSZUKIWANIE
Ostatnim opisywanym dziś narzędziem będzie grep. Służy on do wyszukiwania danego ciągu znaków w plikach. Przykład 26 pokazuje w jaki sposób można sprawdzić do jakich grup należy użytkownik o danej nazwie (tutaj „root”) — oczywiście podobny efekt uzyskamy poleceniem groups lub id.

Przykład 26.
grep root /etc/group

root::0:root
bin::1:root,bin,daemon
daemon::2:root,bin,daemon
sys::3:root,bin
adm::4:root,daemon
disk::6:root,krzysztof
wheel::10:root,krzysztof
log::19:root

Gdybyśmy chcieli poszukiwać ciągu kilku słów musielibyśmy je objąć w cudzysłów lub… zapisać frazę do pliku i użyć polecenia:

grep -f plik_z_fraza przeszukiwany_plik

W odczytywaniu wyniku grep przydaje się opcja --color[=KIEDY]. Dostępne wartości to:

  • always — włącza kolorowanie i w takiej postaci jest wyświetlany wynik na ekranie i przesyłany dalej (np. przez potok),
  • auto — opcja domyślna w przypadku zastosowania samego --color, wyświetla na ekranie wynik pokolorowany, jednak przy jego zapisie do pliku (o czym później), lub przesłaniu przez potok do innego polecenia kolorowanie nie jest już aktywne,
  • never — wyłącza możliwość kolorowania wyniku polecenia grep, włączone domyślnie bez użycia tej opcji

Przykład 27.
grep --color=auto root /etc/group

root::0:root
bin::1:root,bin,daemon
daemon::2:root,bin,daemon
sys::3:root,bin
adm::4:root,daemon
disk::6:root,krzysztof
wheel::10:root,krzysztof
log::19:root

Kolorowe wyróżnienie w wynikach zwracanych przez grep można dowolnie zmieniać dzięki zmiennej środowiskowej GREP_COLOR. Odpowiada ona za definicję podświetlenia i przyjmuje wartości liczb dwucyfrowych oddzielonych średnikami. Więcej na temat ustawiania kolorów można znaleźć w tym artykule (część „Kolorowanie komendy ls”). Ustawienie w pliku .bashrc zmiennej jak w Przykładzie 28 spowoduje kolorowanie tła znalezionego tekstu na niebiesko oraz pogrubienie tekstu.

Przykład 28.
GREP_COLOR="01;44"

Kolejnymi przydatnymi opcjami narzędzia grep są: -A liczba, -B liczba oraz -C liczba. Pierwsza z nich oprócz linii z szukaną frazą, wyświetli nam liczbę wierszy występujących po niej. Druga — liczbę wierszy przed nią. Trzecia natomiast działa jak połączenie obu powyższych — tak więc obydwie komendy z Przykładu 29. dadzą ten sam wynik.

Przykład 29a.
grep -A 3 -B 3 root /etc/passwd

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false

Przykład 29b.
grep -C 3 root /etc/passwd

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false

Warto zauważyć, że jeżeli w szukanym pliku kilkukrotnie występuje szukana fraza wyniki zostaną od siebie elegancko oddzielone. Przekonamy się o tym wykonując Przykład 30.

Przykład 30.
grep -C 1 root /etc/group

root::0:root
bin::1:root,bin,daemon
daemon::2:root,bin,daemon
sys::3:root,bin
adm::4:root,daemon
tty::5:krzysztof
disk::6:root,krzysztof
lp::7:daemon
--
kmem::9:
wheel::10:root,krzysztof
ftp::11:
--
uucp:x:14:
log::19:root
smmsp::25:

W praktyce często stosuje się grep do wyszukiwania wierszy, w których podana fraza nie występuje — taki tryb działania uruchamia opcja -v. Przykład 31 wyświetli wszystkie linie z pliku /etc/group nie zawierające słowa „false”.

Przykład 31.
grep -v false /etc/passwd

root:x:0:0:root:/root:/bin/bash
krzysztof:x:1000:100:,,,:/home/krzysztof:/bin/bash

Bardzo często w przypadku kont systemowych jako ścieżkę do powłoki stosuje się /bin/false, uniemożliwiając tym samym logowanie do systemu i utrudniając ewentualne włamania. Przykład 31 został napisany po to by wyświetlić wszystkich użytkowników nie mających powłoki ustawionej na /bin/false.

Chociaż opcje -n oraz -c opisujemy na końcu, są one całkiem przydatne. Pierwsza z nich przed linią w której podany tekst występuje wypisuje numer wiersza. Zauważmy, że jeśli nakażemy grepowi przeszukać dwa lub więcej plików, przed wszystkim (bez względu na włączenie opcji -n) wyświetli nazwę pliku, w którym podana linia występuje.

Przykład 32.
grep -n root /etc/passwd

1:root:x:0:0:root:/root:/bin/bash

grep -n root /etc/group /etc/passwd

/etc/group:1:root::0:root
/etc/group:2:bin::1:root,bin,daemon
/etc/group:3:daemon::2:root,bin,daemon
/etc/group:4:sys::3:root,bin
/etc/group:5:adm::4:root,daemon
/etc/group:7:disk::6:root,krzysztof
/etc/group:11:wheel::10:root,krzysztof
/etc/group:15:log::19:root
/etc/passwd:1:root:x:0:0:root:/root:/bin/bash

Druga opcja, -c, działa analogicznie jak połączenie potokiem komendy grep z wc -l — zlicza ilość wierszy, w których występuje szukana fraza.

Przykład 33.
grep root /etc/group | wc -l

8

grep -c root /etc/group

8

Oczywiście tak samo jak nadużycia cat łączenia grep z wc -l powinno się unikać.

Przedstawione w tym artykule informacje nie wyczerpują oczywiście całego zagadnienia. Ze względu na komfort Czytelnika postanowiliśmy jednak zakończyć pisanie w tym miejscu — dalsze „konsolowe wynurzenia” znajdą się już w kolejnej części artykułu.


Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

22 komentarzy do “Konsola nie gryzie – operacje na tekście (część pierwsza)

  • mastr

    Pytanie: do czego potrzebne polecenie „echo”? Bo na razie od n00ba do kompilacji doszedłem w półtora roku i jeszcze przenigdy nie musiałem ani tego używać, ani układać jakichś list. Byście dali lepiej naprawdę przydatne komendy.

  • thalcave

    echo przydaje się tak samo jak prinft w C – wypisuje tekst na ekranie, lub można je przekierować do pliku tworząc coś w stylu „Ilość słów w pliku opowiadanie: 1250”.
    Bardzo przydaje się przy pisaniu skryptów – widać nie pisujesz owych.

  • thalcave

    Polecałbym zastosowanie seda, który jest na tyle rozbudowany, że można o nim napisać osobny artykuł.
    Nie wnikając w składnie takie polecenie rozwiązuje Twoje zagadnienie:
    sed s/12/a/g plik

  • epul

    A gdy chcemy, aby sed zapisywał zmiany w pliku warto nie zapominać o parametrze ‚-i’ (bez tego parametru zmiany zostaną tylko wyświetlone w konsoli bez wprowadzania w pliku):
    sed -i s/12/a/g plik

  • lukas

    Do autora, a przy okazji, komentującego master, który nie wie do czego użyć „echo”

    Do @master
    OPTIONS=$( zenity (…) –list –checklist, FALSE ‚Zmień nazwę…)

    echo „$OPTIONS” | grep -q ‚Zmień nazwę…’
    && NAZWA=$( zenity*)

    Do autora.
    grep -q przydaje się bardzo jeśli ktoś preferuje instrukcje [ coś tam zwróci kod 0 ] && zrob || nie rob zamiast if [ coś tam ] ; then ; elif ; else ; fi. Dokładnie, to grep zwraca tylko kod zakończenia 0,1 itp.

  • PlayRoll

    Artykuł godny pochwały, ja osobiście nie wyobrażam sobie korzystania z konsoli bez znajomości wyrażeń regularnych, nie widzę aby było tu wspomniane przy okazji grepa a to właściwie chyba jego główne zastosowanie. I domyślam się, że sed i awk zostaną poruszone w osobnym artykule. Każde z tych poleceń jest na tyle zaawansowane iż można by książkę napisać. To tyle co mi się rzuciło w oczy, czekam na ciąg dalszy 🙂

  • Elche

    Nuda, dłużyzna, nie doczytałem do końca i w żaden sposób nie zachęciło mnie do korzystania z konsoli. 99 % potrzebuje systemu do korzystania a nie do dlubania.

  • u99

    na kacu jestem ale chyba powinno byc cos takiego w tej poradzie:

    echo [quote post=”10766″]Należy mieć na uwadze, że sort umie porównać ze sobą jedynie wiersze sąsiadujące — z tego powodu należy plik pierw posortować.[/quote] | tr „sort” „uniq”

    Fajny artykul

    Pozdrawiam 🙂

  • u99

    na kacu jestem ale chyba trzeba zrobic cos takiego w tej poradzie:

    echo [quote post=”10766″]Należy mieć na uwadze, że sort umie porównać ze sobą jedynie wiersze sąsiadujące — z tego powodu należy plik pierw posortować.[/quote] | tr „sort” „uniq”

    Fajny artykul

    Pozdrawiam 🙂

  • epul

    [quote comment=”41415″]Nuda, dłużyzna, nie doczytałem do końca i w żaden sposób nie zachęciło mnie do korzystania z konsoli. 99 % potrzebuje systemu do korzystania a nie do dlubania.[/quote]
    A ja lubię czasami korzystać z konsoli mimo tego, że mój system służy do korzystania na co dzień, a nie do dłubania. Czasem jest dużo szybciej, dużo wygodniej, a nawet łatwiej.

  • AMartin

    Fajny artykuł. Znam konsolę jak i linuksa od ok. 2 lat dlatego że zaczynałem z Debianem, ale z lenistwa i pewnych problemów przerzuciłem się na Ubuntu. I mimo to kilka poleceń/parametrów było pouczających i z niecierpliwością czekam na kolejną odsłonę.

  • !wR0NG

    [quote comment=”41415″]Nuda, dłużyzna, nie doczytałem do końca i w żaden sposób nie zachęciło mnie do korzystania z konsoli. 99 % potrzebuje systemu do korzystania a nie do dlubania.[/quote]

    Heheh. Więc po co krytykujesz? Kiedyś może się okazać, że będziesz musiał SKORZYSTAĆ z konsoli… wtedy – w przypadku gdy artykuł okaże się słaby, będziesz mógł napisać „Nuda, dłużyzna (…)”
    Więcej rozsądku proponuję!

  • sfp

    „Należy mieć na uwadze, że sort umie porównać ze sobą jedynie wiersze sąsiadujące — z tego powodu należy plik pierw posortować.”
    Chyba „uniq umie …”