Systemy operacyjne. Laboratorium 4. Potoki. Jarosław Rudy Politechnika Wrocławska 30 marca 2017

Podobne dokumenty
Systemy operacyjne. Laboratorium 9. Perl wyrażenia regularne. Jarosław Rudy Politechnika Wrocławska 28 lutego 2017

Wstęp do Informatyki dla bioinformatyków

Bash - wprowadzenie. Bash - wprowadzenie 1/39

Wstęp do informatyki. stęp do informatyki Polecenia (cz.2)

1 Przygotował: mgr inż. Maciej Lasota

POPULARNE POLECENIA SKRYPTY. Pracownia Informatyczna 2

Przekierowanie wejścia wyjścia:

Systemy operacyjne. Laboratorium 8. Perl find

System operacyjny Linux

PODSTAWY INFORMATYKI

Skrypty powłoki Skrypty Najcz ciej u ywane polecenia w skryptach:

Skrypty powłoki w systemie Linux

Systemy operacyjne. Laboratorium 5. Awk podstawy. Jarosław Rudy Politechnika Wrocławska 28 lutego 2017

Temat zajęć: Tworzenie skryptów powłoki systemu operacyjnego.

Temat zajęć: Filtry, strumienie standardowe oraz przetwarzanie potokowe. stderr

Skrypty BASH a. Systemy Operacyjne 2. Mateusz Hołenko. 4 października 2012

Po uruchomieniu programu nasza litera zostanie wyświetlona na ekranie

Przetwarzanie tekstu 2. Operacje na plikach tekstowych w systemie Linux

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

Powłoka I. Popularne implementacje. W stylu sh (powłoki zdefiniowanej w POSIX) W stylu csh. bash (najpopularniejsza) zsh ksh mksh.

Przedstawię teraz tzw. podstawowe symbole wyrażenia regularne (BRE, Basic Regular Expression)

Nazwa implementacji: Nauka języka Python wyrażenia warunkowe. Autor: Piotr Fiorek. Opis implementacji: Poznanie wyrażeń warunkowych if elif - else.

Pętle. Dodał Administrator niedziela, 14 marzec :27

Wyrażenia regularne. Wyrażenia regularne 1/41

Zmienne powłoki. Wywołanie wartości następuje poprzez umieszczenie przed nazwą zmiennej znaku dolara ($ZMIENNA), np. ZMIENNA=wartosc.

Technologie Informacyjne - Linux 3

skrypt powłoki to plik tekstowy, rozpoczynający się sekwencją: pierwsza linia określa powłokę, w której wykonywany jest skrypt; druga to komentarz

Uwagi dotyczące notacji kodu! Moduły. Struktura modułu. Procedury. Opcje modułu (niektóre)

PHP: bloki kodu, tablice, obiekty i formularze

Systemy operacyjne. Laboratorium 1. Podstawy oraz powłoka BASH

Operatory zmiany sposobu przypisania standardowych strumieni >,<,>> Jeżeli pierwsze polecenie powiodło się to wykona drugie

Technologie Informacyjne - Linux 2

Lekcja : Tablice + pętle

Języki formalne i automaty Ćwiczenia 6

Powłoka bash. Kurs systemu Unix 1

7. Pętle for. Przykłady

Systemy operacyjne. Laboratorium 3. Find. Jarosław Rudy Politechnika Wrocławska 28 lutego 2017

Systemy operacyjne. Laboratorium 7. Perl podstawy. Jarosław Rudy Politechnika Wrocławska 27 kwietnia 2017

Ćwiczenie 1. Ćwiczenie 2. Ćwiczenie 3. Opisz działanie następujących komend systemowych : COPY EDIT FDISK FIND FORMAT XCOPY

- wszystkie elementy - wszystkie elementy

PRACOWNIA INFORMATYCZNA BASH - PODSTAWOWE INFORMACJE

ze względu na jego zaokrąglony kształt musimy go umieścić w innych bloczkach np. ze zmienną: lub jeśli chcemy sprawdzić jaki właśnie znak odczytujemy:

W pierwszej kolumnie wyświetlany jest identyfikator procesu (pid)

Bash i algorytmy. Elwira Wachowicz. 20 lutego

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Jak zawsze wyjdziemy od terminologii. While oznacza dopóki, podczas gdy. Pętla while jest

Zastosowanie filtrów w Linuksie

1. Znajdź za pomocą programu locate wszystkie pliki które zawierają w nazwie słowo netscape locate netscape

do instrukcja while(wyrażenie);

Podstawy programowania. Wykład: 9. Łańcuchy znaków. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Administracja sieciowymi systemami operacyjnymi III Klasa - Linux

Programowanie w języku Python. Grażyna Koba

Stałe, znaki, łańcuchy znaków, wejście i wyjście sformatowane

Pliki. Operacje na plikach w Pascalu

Nazwa implementacji: Nauka języka Python pętla for. Autor: Piotr Fiorek

Instrukcja SO powłoka BASH i skrypty powłoki Istotne jest zrozumienie działania narzędzia history powłoki BASH. Każde polecenie wprowadzone i

Python wprowadzenie. Warszawa, 24 marca PROGRAMOWANIE I SZKOLENIA

Systemy operacyjne II Laboratorium. Część 1: Pliki wsadowe Windows

BASH - LINIA POLECEŃ. Bioinformatyka 2018/2019

PODSTAWY INFORMATYKI 1 PRACOWNIA NR 6

1. Wyrażenia regularne. Symbole w wyrażeniach regularnych 1 :

Systemy operacyjne. Laboratorium 2. Dowiązania. Jarosław Rudy Politechnika Wrocławska 28 lutego 2017

SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE

Zmienne środowiskowe: Ścieżka przeszukiwana komendą.

chmod +x skrypt /bin/bash nazwa_skryptu ZMIENNA=123 ZMIENNA="wartość tekstowa" SCIEZKA="$HOME/plik" echo $SCIEZKA

Opis: Instrukcja warunkowa Składnia: IF [NOT] warunek [AND [NOT] warunek] [OR [NOT] warunek].

MATERIAŁY - udostępnianie materiałów dydaktycznych w sieci SGH

do instrukcja while (wyrażenie);

Napisy w PHP. Drukowanie napisów instrukcją echo

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Celem tego projektu jest stworzenie

SED - Stream EDitor. edytor strumieniowy. Bogumił Konopka W-11/I-21 Politechnika Wrocławska

Systemy operacyjne. System operacyjny Linux - wstęp. Anna Wojak

I. Program II. Opis głównych funkcji programu... 19

INFORMATYKA Studia Niestacjonarne Elektrotechnika

Tablice cz. I Tablice jednowymiarowe, proste operacje na tablicach

System operacyjny Linux

lekcja 8a Gry komputerowe MasterMind

Trochę o plikach wsadowych (Windows)

Wprowadzenie do maszyny Turinga

JĘZYK SHELL JEST PEŁNYM JĘZYKIEM PROGRAMOWANIA

Zakład Systemów Rozproszonych

Powłoka (shell) Powłoka ksh

Programowanie skryptów powłoki

Wyrażenie include(sciezka_do_pliku) pozwala na załadowanie (wnętrza) pliku do skryptu php. Plik ten może zawierać wszystko, co może się znaleźć w

1 Powtórzenie wiadomości

Podstawy i języki programowania

Podstawy programowania w C++

Znaki globalne w Linuxie

Cw.12 JAVAScript w dokumentach HTML

8. Wektory. Przykłady Napisz program, który pobierze od użytkownika 10 liczb, a następnie wypisze je w kolejności odwrotnej niż podana.

1 Podstawy c++ w pigułce.

wykład Organizacja plików Opracował: dr inż. Janusz DUDCZYK

Programowanie w Turbo Pascal

Podstawy Programowania Podstawowa składnia języka C++

Instrukcje cykliczne (pętle) WHILE...END WHILE

System operacyjny UNIX Ćwiczenie 1. Podstawowe polecenia systemu Unix

Lekcja 10. Uprawnienia. Dołączanie plików przy pomocy funkcji include() Sprawdzanie, czy plik istnieje przy pmocy funkcji file_exists()

Wskazówki dotyczące zmiennych, tablic i procedur 1

Transkrypt:

Systemy operacyjne Laboratorium 4 Potoki Jarosław Rudy Politechnika Wrocławska 30 marca 2017 Laboratorium obejmuje znajomość potoków linuksowych, zwłaszcza w połączeniu z komendami while i read. 1 Potoki Do tej pory wykonanie komend odbywało się wyłącznie sekwencyjnie: komenda1 komenda2 lub w pojedynczej linii: komenda1; komenda2 Oznacza to, że komenda1 musi się zakończyć zanim komenda2 się rozpocznie. Ponadto mogliśmy uzależnić uruchomienie komenda2 od wyniku działania komenda1: command1 && command2 command1 command2 Jednak i w tym przypadku komenda2 nie zacznie się wykonywać, jeśli komenda1 jeszcze się nie zakończyła. Istnieje jednak możliwość, aby więcej niż jedna komenda pracowała jednocześnie podczas wykonywania skryptu. Jedną z takich możliwości jest użycie potoków, jako że technika ta daje istotne możliwości. Załóżmy, że wywołaliśmy komendę find, która drukuje nazwy plików. Teraz chcemy jednak wyświetlić jedynie 10 pierwszych wyników. Można to uzyskać za pomocą komendy head, o ile podamy na jej wejście wynik (wyjście) finda. Bez użycia potoków musielibyśmy przechować wynik finda w pliku tymczasowym i wysłać ten plik na wejście head z użyciem przekierowań: find > somefile head < somefile 1

Znak < (>) oznacza przekierowanie wejścia (wyjścia) komendy. Po ich użyciu na wejście komendy zostanie skierowana zawartość pliku (wyjście zostanie skierowane danego pliku). Składnia > spowoduje wyzerowanie (nadpisanie) pliku. Składnia >> pisze nowe dane na końcu pliku bez jego wyzerowania. Podejście to zadziała, ale ma wady. Komenda head musi czekać na zakończenie komendy find. Ponadto potrzebne jest utworzenie rzeczywistego pliku, co zajmuje czas (dyski twarde są stosunkowo powolne). Nie mamy też 100-procentowej gwarancji, że plik tymczasowy nie zostanie po drodze usunięty. Drugim naiwnym podejściem jest zapisanie wyniku działania finda zmiennej bashowej: var=$(find) head <<< $var # lub var=`find` Składnia <<< jest pobna <, ale nie wyśle na standarwe wejście head zawartości pliku podanego w zmiennej $var, tylko wyśle wprost zawartosć $var na wejście komendy jako tekst. To podejście nie jest jednak dużo lepsze od poprzedniego. Po pierwsze, wszystkie znaki nowej linii znikną po zapisaniu tekstu zmiennej var. Po drugie, find wciąż musi się zakończyć zanim head zacznie pracę. Po trzecie, chociaż to podejście jest szybsze, to i tak cały wynik finda musi zostać zapisany w zmiennej. Jeśli ten wynik ma wielkość 100 megabajtów, to musimy przechować 100 megabajtów w jednej zmiennej. Jest to możliwe (chociaż skrajnie nieefektywne). A co jeśli wynik finda ma wielkość większą niż stępna pamięć operacyjna? Istnieje jednak zadziwiająco prosta odpowiedź na wszystkie powyższe problemy. Po prostu połączmy komendy find i head z użyciem potoku: find head Takie rozwiązanie jest proste, wygodne, łatwe zapamiętania i wydajne. Ale jak to naprawdę działa? Potok oznacza, że bash uruchomi jednocześnia DWA procesy (komendy). Oczywiście w przypadku komputera o jednym procesorze komendy te nie będą się wykonywały w pełni równolegle, lecz procesor będzie wykonywał je naprzemiennie po kawałku, ale nie zmienia to faktu, że komenda head zacznie się wykonywać zanim komenda find się zakończy. Oprócz tego standarwe wyjście komendy find zostanie połączone ze standarwym wejściem komendy head. Oznacza to, że każda linia tekstu wyprodukowana przez find będzie wiczna dla head tak jak każde inne wejście (z klawiatury czy z pliku) i komenda ta nie musi być świama, że jej dane pochodzą z potoku. Transferem danych pomiędzy komendami zajmuje się jądro systemu operacyjnego z użyciem własnego buforu. Takie podejście posiada wiele zalet: 1. Transfer danych jest szybki, ponieważ odbywa się bez udziału plików tymczasowych i dysku twardego czy pobnych nośników. 2. Transfer jest przezroczysty dla wszystkich użytkowników systemu, więc zagrożenie podejrzenia czy modyfikacji przesyłania danych praktycznie nie istnieje. 2

3. Jeśli komenda wysyłająca produkuje dane wystarczajaco wolno, by komenda odbierająca zdążyła je przetworzyć na biężąco, to wymagany rozmiar buforu jest bardzo mały. Czasami możliwa jest sytuacja, gdzie w buforze systemu operacyjnego jest jednocześnie tylko kilka bajtów/linii tekstu, nawet jeśli łączny rozmiar wymienianych danych wynosi gigajty. Dla potwierdzenia ostatniego punktu można wykonać następującą komendę: find KAT -print gdzie KAT to katalog zawierający dużo plików (np. ~ czyli katalog mowy lub /), tak aby wywołanie takiej komendy trwało zauważalny czas (co najmniej sekunda). Następnie wykonujemy: find KAT -print head Komenda ta wykona się dużo szybciej niż poprzednia, ponieważ head myślnie drukuje tylko 10 pierwszych otrzymanych linii, więc po uzyskaniu 10 nazw plików od find po prostu zignoruję resztę i zakończy pracę. Należy jednak zadać pytanie: co się stanie, jeśli find będzie produkował swoje wyjście zbyt wolno i bufor będzie pusty gdy head będzie próbowało odczytać kolejne dane? Komenda head zostanie wtedy zablokowana czasu pojawienia się nowych danych w buforze lub odczytania końca pliku. Analogicznie, gdy find będzie produkował dane zbyt szybko i przepełni bufor, to zostanie zablokowany przy próbie zapisu czasu zwolnienia części bufora. Na tym jednak nie koniec możliwości potoków. Ich praktyczna przydatność bierze się także z tego, że wynik drugiej komendy również można połączyć potokiem z trzecią komendą. Powstaje w ten sposób łańcuch, który może osiąnąć znaczą długość: command1 command2 command3 command4 Taki potok będzie działał o ile wszystkie komendy poprawnie wykorzystują dane na swoim standarwym wejściu/wyjściu. W systemach uniksowych taka praktyka jest często zwana filtrowaniem, jako że poszczególne komendy w potoku analizują otrzymane dane i zwykle modyfikują je lub obcinają ich część. Przykławo: cat file1 sort grep Ala tail tee file2 Ten potok spowoduje wysłanie zawartości pliku file1 (komenda cat) w celu posortowania linia po linii (sort), następnie usunięte z potoku zostaną linie, które nie zawierają tekstu ''Ala'' (grep), potem zostaną usunięte wszystkie linie poza ostatnimi dzięsiecioma (tail), zaś końcowy wynik trafi zarówno na standarwe wyjście potoku (konsolę) oraz pliku file2 (tee). Poniżej znajduje się lista komend przydatnych w pracy z potokami: 1. sort. Sortuje dane linia po linii. Brzmi to syć prosto, ale komenda ta dysponuje znacznymi możliwościami oferując różne tryby sortowania (numeryczne, alfabetyczne itp.) oraz pozwala na sortowanie względem różnych kluczy (kolumn). Komenda może rozpocząć wypisywania wyjścia piero jak pozna całe swoje wejście (może spowalniać potok). 3

2. tac. Odwrotność cat łączy i listuje podane pliki, ale każdy plik jest listowany od końca (odwrócona kolejność linii). 3. rev. Listuje podane pliki, odwracając kolejność znaków w każdej linii. 4. uniq. Domyślnie usuwa powtarzające się linie (zostawia jedynie linie unikalne), ale z łatwością można go zmusić zachowania odwrotnego. Uwaga! uniq działa bardzo naiwnie i po prostu usuwa wszystkie następujące po sobie identycznie linie poza pierwszą. Oznacza to, że poprawnej pracy uniq musi otrzymać dane posortowane! Przy użyciu odpowiednich opcji komenda sort może również działać jak uniq. 5. head. Domyślnie wypisuje 10 pierwszych linii wejścia i ignoruje resztę. Bardziej ogólnie można ją zmusić wypisywania N pierwszych linii wejścia lub np. N pierwszych znaków każdej linii. 6. tail. Analogicznie head, ale zajmuje się końcowymi liniami/znakami pliku, a nie początkowymi. 7. grep. Rozbuwana komenda używana filtrowania wejścia w poszukiwaniu linii pasujących wzorca (zwykle danego wyrażeniem regularnym tzw. regexem). Na laboratorium z potoków zwolone jest jedynie proste użycie tej komendy (bez regexów). 8. tee. Użyteczna komenda nazwana od litery T, która działa jak trójnik. Nie zmienia swojego standarwego wejścia, ale podwaja je i wysyła dwóch miejsc: 1) na swoje standarwe wyjście (co pozwala kontynuować potok) oraz wskazanego pliku. 9. tr. Komenda służąca zastępowania pewnych znaków na wejściu innymi znakami zgodnie z podaną definicją. 2 Pętla while While jest konstrukcją składniową basha, jednocześnie pobną jak i odmienną od konstrukcji for. Zacznijmy od założenia, że chcemy przetworzyć dane wypisane przez komendę find w pętli. Teoretycznie możemy tego wykorzystać pętlę for w następujący sposób: for var in `find` (...) ne Podejście to nie jest jednak pozbawione wad. Pętla NIE zacznie pracy póki wywołanie find się nie zakończy i nie będzie można skonstruować listy dla pętli. Ponadto, lista dla pętli for dzieli wyrazy względem każdego białego znaku, co oznacza, że wyjście finda postaci: 4

plik1 dane plik2 dane spowoduje 4 iteracje pętli, a to nie jest zwykle to co nas interesuje w tym przypadku. Dobrym pomysłem jest wykorzystanie potoku, ale pętla for nie jest tego przystosowana. Wywołanie: find for var in somelist nie ma sensu, gdyż pętla for nie analizuje swojego wejścia. Rozwiązaniem jest użycie pętli while, ale aby je zastosować należy wpierw zrozumieć samą ideę tej pętli. Składnia while wygląda następująco: while warunek (...) ne W przeciwieństwie pętli for, while będzie kontynuować pracę póki kod powrotu komendy warunek będzie równy prawdziwe (tzn. 0). W wielu zastosowaniach rolę warunek dla while pełni komenda test ([): var=10 while [ $var -gt 0 ] echo $var var=$[var-1] ne Taki warunek nie pomoże jednak w naszym przypadku. Do użycia while wraz z potokiem potrzebna nam jest jeszcze jedna komenda: read. 3 Read Rola komendy read jest prosta: pobiera ona pojedynczą linię wejścia i rozdziela ją pomiędzy podane zmienne. Przykławo: read var1 var2 var3 odczyta (i usunie!) JEDNĄ linię ze swojego standarwego wejścia i spróbuje podzielić tą linię na 3 części względem (myślnie) białych znaków: spacji i tabulatora. Jeśli teraz na wejście read wyślemy tekst ''jakis przyklawy tekst'', to w rezultacie nastąpi przypisanie: var1=jakis var2=przyklawy var3=tekst 5

Zdaje się to proste (pamiętamy, że wykorzystana linia znika z wejścia read). Powstaje jednak pytanie co stanie się gdy liczba słów w linii będzie różna od liczby podanych zmiennych (szczególnie istotne, gdy find będzie produkował linie o różnej liczbie słów dla różnych przypadków)? Jeśli podamy za dużo zmiennych, to nadmiarowe zmienne nie będą miały przypisanej wartości. Jeśli więc wyślemy w powyższym przykładzie tekst ''jakis przyklawy'', to zmienna var3 będzie po prostu pusta. Z drugiej strony, jeśli podamy za mało zmiennych, to wszystkie nadmiarowe słowa rozdzielanej linii zostaną umieszczone w ostatniej zmiennej. Jeśli więc podamy na nasz read tekst ''jakis przyklawy nieco dluzszy text'', to nastąpi przypisanie: var1=jakis var2=przyklawy var3=nieco dluzszy tekst Należy też zauważyć, że kod powrotu komendy read przyjmuje wartość 0, chyba że wystąpił błąd lub koniec pliku. 4 While + read Możemy teraz użyć obu opisanych komend by przetworzyć wyjście finda w potoku: find while read jakies zmienne (...) ne Jak to działa? Dzięki zastosowaniu potoku wyjście finda zostaje skierowane na komendę while. Komenda ta jest jednak złożoną komendą, na którą może składać się wiele innych komend wewnątrz pętli. Trik polega na tym, że wszystkie komendy wewnątrz while współdzielą z nią standarwe wejście i wyjście, tyczy to więc także read. Oznacza to, że read będzie odczytywał (i usuwał ) wejście finda, które następnie zostanie rozdzielone na podane zmienne, które można z kolei wykorzystać we wnętrzy pętli. Co więcej, gdy read przeczyta wszystkie linie, to odnajdzie koniec pliku i zmieni swój kod powrotu na niezerowy, co zakończy pętlę. Należy jednak cały czas pamiętać, że wejście while jest współdzielone przez wszystkie komendy w obrębie pętli, a odczytane wejście znika, więc trzeba zadbać, by odpowiednie komendy odczytywały odpowiedni fragment wejścia. Sama pętla while i wiele komend nie będzie w ogóle ruszało tego wejścia, ale należy zachować ostrożność. Dotyczy to sytuacji, gdy w pętli znajduje się więcej niż jeden read, a już szczególnie gdy różne komendy read umieszczone są pod różnymi warunkami i ciężko przewidzieć, które linie wejścia zostaną wyłapane przez który read. Przykład takiej sytuacji: 6

find while read var1 var2 read var3 var4 if [ $var2 = $var4 ]; then read var5 fi ne Poprawne sformułowanie komend while i read to jednak nie koniec. Równie ważna jest poprawna definicja komendy find, a ściślej określenie formatu danych wyjściowych jaki find ma stosować, by while/read mogły je przetworzyć. Przykławo, jeśli find wysyła dane w formacie: znacznik1 jakiesdane znacznik2 jakiesdane to wystarczy prosta konstrukcja pętli typu: find while read var1 var2 if [ var1 = znacznik1 ]; then # jakas akcja else # inna akcja fi ne W tym przykładzie znacznik1 oraz znacznik2 są jakimś tekstem, który posłuży konstrukcji while/read zorientowania się z jakim przypadkiem mamy czynienia w danej linii. Ten sam przykład można rozwiązać inaczej. Jeśli zmusimy find (poprzez -printf) formatowania wyjścia w taki sposób: znacznik1 jakiesdane znacznik2 jakiesdane to pętlę należy tego stosować: find while read var1 read var2 if [ var1 = znacznik1 ]; then # jakas akcja else # inna akcja fi ne 7

Dobre zaprojektowanie pętli jest tym bardziej istotne, jeśli find wypisuje różną liczbę słów dla różnych przypadków. Należy wtedy tak rozpisać read (ready), aby pomieścić każdy przypadek i używać tych zmiennych właściwych dla danego przypadku. Pozostają jeszcze omówienia pewne kwestie praktyczne wynikające z zastosowania while jako części potoku. Po pierwsze, pętla ta jest traktowana jako zwyczajna komenda i nie musi kończyć potoku: find while read (...) (...) ne kontynuacja potoku Możliwe jest połączenie ze sobą w potoku dwóch pętli while, aczkolwiek podczas laboratorium nie jest to niezbędne i zwykle oznacza błąd. Drugi problem jest bardziej złożony. Rozważmy poniższy skrypt: var="przed" find while read jakies zmienne var="po" ne echo $var Każda komenda dziedziczy wartości zmiennych po komendzie, która ją wywołała. Ponadto while, będąc komendą złożoną, może zmieniać wartości zmiennych jak pokazano powyżej. Proste. Jednakże, jeśli uruchomimy powyższy skrypt, to okaże się, że ostatnia komenda echo wypisze tekst przed zamiast po. Dlaczego? Winowajcą tej sytuacji jest potok, gdyż obie jego strony (w tym przypadku find i while) wykonują się jednocześnie (współbieżnie). Różnica polega na tym, że komendy te wykonują się w ramach osobnych skryptów. Co więcej, komenda echo należy tego samego skryptu co find (powiedzmy, że jest to skrypt A), zaś while należy drugiego skryptu (B). Komenda echo odziedziczy więc zmiany po swoim poprzedniku, czyli find, a nie while. My chcemy jednak, by echo było świame zmian konanych przez while. Jak tego konać? Należy po prostu umieścić komendy while i echo w tym samym skrypcie. Co prawda składnikiem potoku może być tylko pojedyncza komenda, ale istnieje sposób, by umieścić ciąg komend w osobnym podskrypcie. Służą tego nawiasy okrągłe ''('' oraz '')'': var="przed" find ( while read jakies zmienne var="po" ne echo $var ) 8

Po tej zmianie find nie jest połączony bezpośrednie z while, a z całym podskryptem, na który składa się while i echo, a skrypt zadziała teraz zgodnie z oczekiwaniem, wypisując na ekran tekst po. To specjalne znaczenie nawiasów okrągłych jest powodem, dla którego na poprzednim laboratorium należało je poprzedzać odwrotnym ukośnikiem (lub otaczać apostrofami), by nie zostały zinterpretowane przez bash jako próba utworzenia podskryptu. 9