Rewolucja Języków Wysokiego Poziomu
18/50

Abstrakcja od Sprzętu: Języki Kompilowane

Podczas gdy systemy operacyjne ewoluowały w kierunku interaktywności, równolegle trwała rewolucja w dziedzinie języków programowania. Ograniczenia asemblera – jego zależność od sprzętu i niska produktywność – stawały się coraz bardziej dotkliwe. Zrodziła się potrzeba tworzenia języków, które pozwoliłyby programistom wyrażać algorytmy w sposób bliższy ludzkiej logice i notacji matematycznej, a nie architekturze konkretnego procesora. Tak powstały języki wysokiego poziomu. Ich kluczową cechą była abstrakcja od sprzętu i przenośność. Program napisany w takim języku mógł być, po odpowiednim przetłumaczeniu, uruchomiony na różnych maszynach. Procesem tłumaczenia kodu źródłowego na kod maszynowy zajmował się specjalny, złożony program – kompilator. To on brał na siebie ciężar dostosowania ogólnych instrukcji do specyfiki danej architektury procesora.

Poziomy Abstrakcji w Programowaniu

+-------------------------------------+
| Aplikacje (np. edytor tekstu)       |
+-------------------------------------+
| Języki Wysokiego Poziomu (C, Java)  |  <-- Cel: Przenośność i produktywność
+-------------------------------------+
| Język Asemblera (MOV, ADD)          |
+-------------------------------------+
| Kod Maszynowy (10110010...)         |
+-------------------------------------+
| Mikroarchitektura (bramki logiczne) |
+-------------------------------------+
| Sprzęt Fizyczny (tranzystory)       |
+-------------------------------------+
                
Grace Hopper i Pierwszy Kompilator
19/50

Pionierka Kompilacji: System A-0

Koncepcja automatycznego tłumaczenia kodu wysokiego poziomu na język maszynowy zawdzięczamy w dużej mierze Grace Hopper. W 1952 roku, pracując nad komputerem UNIVAC I, stworzyła ona system A-0 (Arithmetic Language version 0), który jest uznawany za pierwszy w historii kompilator. W tamtych czasach dominowało przekonanie, że komputery potrafią jedynie wykonywać operacje arytmetyczne, a nie manipulować symbolami, co było konieczne do translacji kodu. Praca Hopper udowodniła, że jest inaczej. System A-0 pozwalał programistom zapisywać operacje za pomocą czytelnych, anglojęzycznych słów kluczowych, które następnie były tłumaczone na kod maszynowy. Choć A-0 był w istocie bardziej zaawansowanym linkerem niż współczesnym kompilatorem, jego stworzenie było przełomem, który otworzył drogę do rozwoju znacznie bardziej zaawansowanych języków, takich jak COBOL, w którego powstaniu Hopper również miała kluczowy udział.

 Grace Hopper (1906-1992)

  "Amazing Grace"
  Pionierka informatyki

    /`·.¸
   /¸...¸`:·
 ¸.·´  ¸   `·.¸.·´)
: © ):´;      ¸  {
 `·.¸ `·  ¸.·´\`·¸)
     `\\´´\¸.·´
           
   +----------------------+
   | Osiągnięcia:         |
   | * System A-0 (1952)  |
   | * Wpływ na COBOL     |
   | * Popularyzacja terminu|
   |   "debugging"        |
   +----------------------+
                
FORTRAN: Język dla Nauki i Inżynierii
20/50

Formula Translation System

Pierwszym powszechnie używanym, skompilowanym językiem wysokiego poziomu był FORTRAN (Formula Translation), opracowany w latach 1954-1957 przez zespół pod kierownictwem Johna Backusa w firmie IBM. Jego głównym celem było uproszczenie programowania skomplikowanych obliczeń naukowych i inżynieryjnych. FORTRAN pozwalał na zapisywanie formuł matematycznych w notacji zbliżonej do algebraicznej, co było rewolucją w porównaniu z asemblerem. Kluczowym elementem projektu był niezwykle zaawansowany jak na owe czasy kompilator, który potrafił generować kod maszynowy o wydajności porównywalnej z ręcznie pisanym kodem asemblerowym. To właśnie ta wydajność przekonała sceptyczną społeczność naukową do porzucenia asemblera na rzecz języka wysokiego poziomu. FORTRAN zdefiniował wiele fundamentalnych koncepcji, takich jak pętle `DO`, instrukcje warunkowe `IF` czy obsługa tablic, które stały się standardem w późniejszych językach.

C     PROGRAM OBLICZAJACY POLE KOLA
      PROGRAM AREA
      REAL :: R, PI, S
      PI = 3.14159
      
      PRINT *, 'Podaj promień koła:'
      READ *, R
      
      S = PI * R**2
      
      PRINT *, 'Pole koła wynosi:', S
      END PROGRAM AREA
                
COBOL: Język dla Świata Biznesu
21/50

Common Business-Oriented Language

Podczas gdy FORTRAN zaspokajał potrzeby świata nauki, sektor biznesowy i administracyjny wymagał języka zoptymalizowanego pod kątem przetwarzania danych, a nie skomplikowanych obliczeń. Odpowiedzią był COBOL (Common Business-Oriented Language), zaprojektowany w 1959 roku przez komitet, w którym kluczową rolę odegrała Grace Hopper. Głównym założeniem COBOL-a była czytelność i zrozumiałość kodu, nawet dla osób niebędących programistami. Składnia języka celowo naśladowała naturalny język angielski, co miało ułatwić tworzenie i utrzymywanie aplikacji biznesowych, takich jak systemy płacowe, księgowe czy magazynowe. COBOL wprowadził zaawansowane mechanizmy opisu i manipulacji strukturami danych (rekordami) oraz operacji na plikach. Mimo upływu lat, ogromna ilość kodu napisanego w COBOL-u wciąż działa w systemach bankowych i ubezpieczeniowych na całym świecie.

IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO-WORLD.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
    DISPLAY "Hello, world!".
    STOP RUN.
                
Proces Kompilacji: Zarys Ogólny
22/50

Wielofazowa Transformacja Kodu

Kompilacja jest złożonym, wieloetapowym procesem, w którym kod źródłowy napisany w języku wysokiego poziomu jest systematycznie analizowany, transformowany i ostatecznie tłumaczony na wykonywalny kod maszynowy. Proces ten można podzielić na dwie główne fazy: analizę i syntezę. Faza analizy, zwana również front-endem kompilatora, polega na dogłębnym zrozumieniu kodu źródłowego. Dzieli się ona na analizę leksykalną, składniową i semantyczną. Jej celem jest sprawdzenie poprawności kodu i zbudowanie jego wewnętrznej reprezentacji, najczęściej w postaci drzewa składni abstrakcyjnej (AST). Faza syntezy, czyli back-end kompilatora, odpowiada za wygenerowanie kodu wynikowego. Obejmuje ona generowanie kodu pośredniego, jego optymalizację oraz ostateczne wygenerowanie kodu maszynowego dla docelowej architektury. Każdy z tych etapów odgrywa kluczową rolę w tworzeniu poprawnego i wydajnego programu.

Uproszczony Schemat Kompilatora

+------------------+
|   Kod źródłowy   |
+------------------+
         |
         v
+------------------+  <-- FRONT-END (Analiza)
| Analiza Leksykalna|
+------------------+
         |
         v
+------------------+
| Analiza Składniowa|
+------------------+
         |
         v
+------------------+
| Analiza Semantyczna|
+------------------+
         |
         v
+------------------+  <-- BACK-END (Synteza)
|   Optymalizacja  |
+------------------+
         |
         v
+------------------+
| Generowanie Kodu |
+------------------+
         |
         v
+------------------+
|  Kod maszynowy   |
+------------------+
                
Krok 1: Analiza Leksykalna (Tokenizacja)
23/50

Dzielenie Kodu na "Słowa"

Pierwszym etapem analizy kodu źródłowego jest analiza leksykalna, realizowana przez komponent zwany lekserem lub skanerem. Zadaniem leksera jest odczytanie surowego strumienia znaków z pliku źródłowego i pogrupowanie go w logiczne jednostki zwane leksemami. Każdemu leksemowi przypisywany jest odpowiedni typ, tworząc w ten sposób token. Token jest podstawową "cegiełką" składniową języka. Przykładowo, w instrukcji `if (x > 10) y = 20;`, lekser zidentyfikuje takie tokeny jak: słowo kluczowe `if`, nawias otwierający `(`, identyfikator `x`, operator porównania `>`, stała liczbowa `10`, nawias zamykający `)`, identyfikator `y`, operator przypisania `=`, stała liczbowa `20` oraz średnik `;`. W trakcie tego procesu lekser zazwyczaj usuwa nieistotne elementy, takie jak białe znaki (spacje, tabulacje) i komentarze. Wynikiem tego etapu jest uporządkowany strumień tokenów, gotowy do dalszej analizy.

// Kod źródłowy
int a = 10;

// Strumień tokenów po analizie leksykalnej
[
  { type: 'KEYWORD', value: 'int' },
  { type: 'IDENTIFIER', value: 'a' },
  { type: 'OPERATOR', value: '=' },
  { type: 'LITERAL', value: '10' },
  { type: 'SEPARATOR', value: ';' }
]
                
Krok 2: Analiza Składniowa (Parsing)
24/50

Budowanie Struktury Programu

Po uzyskaniu strumienia tokenów od leksera, do akcji wkracza parser, odpowiedzialny za analizę składniową (syntaktyczną). Jego zadaniem jest sprawdzenie, czy sekwencja tokenów jest zgodna z regułami gramatycznymi danego języka programowania. Parser weryfikuje, czy tokeny tworzą poprawne konstrukcje, takie jak deklaracje zmiennych, wyrażenia, instrukcje warunkowe czy pętle. Jeśli struktura jest niepoprawna, na przykład brakuje nawiasu zamykającego lub średnika, parser zgłasza błąd składniowy. Jeśli kod jest poprawny, parser buduje jego hierarchiczną reprezentację w postaci Drzewa Składni Abstrakcyjnej (Abstract Syntax Tree, AST). AST jest strukturą danych, która odzwierciedla logiczną strukturę kodu, pomijając nieistotne detale składniowe. Drzewo to staje się centralną strukturą danych, na której operują kolejne etapy kompilacji.

Kod: a = b + 5;

Drzewo Składni Abstrakcyjnej (AST)

      [ASSIGNMENT: =]
           /      \
          /        \
    [IDENTIFIER: a]  [BINARY_OP: +]
                       /       \
                      /         \
               [IDENTIFIER: b]  [LITERAL: 5]

Krok 3: Analiza Semantyczna
25/50

Sprawdzanie Znaczenia i Poprawności Logicznej

Analiza semantyczna to etap, na którym kompilator sprawdza, czy poprawny składniowo program ma również sens logiczny. Podczas gdy parser weryfikuje "jak" coś jest napisane, analiza semantyczna sprawdza "co" to znaczy. Na tym etapie odbywa się między innymi sprawdzanie typów (type checking), czyli weryfikacja, czy operacje są wykonywane na zgodnych typach danych. Na przykład, kompilator zgłosi błąd, jeśli programista spróbuje dodać liczbę do tekstu w języku silnie typowanym. Analizator semantyczny sprawdza również, czy zmienne zostały zadeklarowane przed użyciem, czy funkcje są wołane z poprawną liczbą i typem argumentów, oraz czy nie ma prób dostępu do prywatnych składowych obiektów. Informacje o typach i deklaracjach są zbierane i przechowywane w strukturze zwanej tablicą symboli, która jest wykorzystywana do wzbogacenia AST o kontekst semantyczny.

// Błędy wykrywane przez analizę semantyczną

int x = 10;
string y = "hello";

int z = x + y;  // BŁĄD SEMANTYCZNY:
                   // Niezgodność typów w operacji '+'

print(a);      // BŁĄD SEMANTYCZNY:
                   // Użycie niezadeklarowanej zmiennej 'a'
                
Krok 4: Generowanie Kodu Pośredniego
26/50

Uniwersalna Reprezentacja Programu

Po pomyślnym przejściu fazy analizy, wiele kompilatorów tłumaczy AST na kod pośredni (Intermediate Representation, IR). Kod pośredni jest reprezentacją programu, która jest niezależna zarówno od języka źródłowego, jak i od docelowej architektury sprzętowej. Jest to abstrakcyjny, niskopoziomowy język, często przypominający uniwersalny asembler. Stosowanie kodu pośredniego ma dwie kluczowe zalety. Po pierwsze, ułatwia przeprowadzanie optymalizacji, ponieważ wiele z nich jest łatwiejszych do zaimplementowania na prostej, jednolitej reprezentacji niż na złożonym AST. Po drugie, promuje modularność kompilatora. Dzięki IR można stworzyć jeden front-end dla danego języka (np. C++) i wiele back-endów dla różnych architektur (x86, ARM, MIPS). Wystarczy napisać tylko jeden generator kodu pośredniego i wiele generatorów kodu maszynowego z tego kodu pośredniego.

Zaleta Kodu Pośredniego (IR)

    Front-endy           Back-endy
+---------------|      |----------------+
| C++ Front-end |-----\/                 |
+---------------|      +----------------+
                       | IR (Kod        |      +-------------+
+---------------|      | Pośredni)      |----> | x86 Back-end|
| Java Front-end|----->|                |      +-------------+
+---------------|      |                |
                       |                |      +-------------+
+---------------|      |                |----> | ARM Back-end |
| Rust Front-end|-----/\                |      +-------------+
+---------------|      +----------------+
                
Krok 5 i 6: Optymalizacja i Generowanie Kodu
27/50

Tworzenie Wydajnego Kodu Maszynowego

Etap optymalizacji jest jednym z najważniejszych w całym procesie kompilacji. Kompilator analizuje kod pośredni i stosuje szereg transformacji mających na celu poprawę jego wydajności (szybkości wykonania) lub zmniejszenie jego rozmiaru. Techniki optymalizacyjne obejmują m.in. eliminację martwego kodu (instrukcji, które nigdy nie będą wykonane), rozwijanie pętli, czy stałą propagację (zastępowanie zmiennych ich wartościami, jeśli są znane w czasie kompilacji). Po zoptymalizowaniu kodu pośredniego, następuje ostatni etap – generowanie kodu wynikowego. Generator kodu tłumaczy zoptymalizowany IR na konkretny język docelowy, najczęściej asembler lub bezpośrednio kod maszynowy dla docelowej architektury. Na tym etapie odbywa się alokacja rejestrów procesora i wybór odpowiednich instrukcji maszynowych. Wynikiem jest plik obiektowy, który po połączeniu z bibliotekami przez linker staje się gotowym programem wykonywalnym.

// Przykład prostej optymalizacji

// Kod przed optymalizacją
x = 10;
y = 20;
z = x + y;

// Kod po optymalizacji (stała propagacja)
z = 30;
                
Zalety i Wady Kompilacji
28/50

Analiza Modelu Kompilowanego

Główną i niekwestionowaną zaletą języków kompilowanych jest wydajność. Ponieważ cały program jest tłumaczony na kod maszynowy jednorazowo, przed uruchomieniem, jego wykonanie jest bardzo szybkie. Kompilator ma czas na przeprowadzenie zaawansowanych optymalizacji, co dodatkowo zwiększa szybkość działania. Kolejną zaletą jest wczesne wykrywanie błędów. Wiele błędów składniowych i semantycznych jest identyfikowanych już na etapie kompilacji, co zapobiega ich pojawieniu się w trakcie działania programu. Wadą jest natomiast sam proces kompilacji, który może być czasochłonny, zwłaszcza w przypadku dużych projektów. Cykl pracy programisty (zmiana w kodzie -> kompilacja -> uruchomienie) jest przez to wydłużony. Ponadto, skompilowany program jest specyficzny dla danej platformy (systemu operacyjnego i architektury procesora), co utrudnia przenośność, choć problem ten jest częściowo rozwiązywany przez kompilację na różnych platformach.

+-------------------------+
| Zalety Kompilacji       |
+-------------------------+
| + Wysoka wydajność      |
| + Wczesne wykrywanie    |
|   błędów (statyczne)    |
| + Ochrona kodu źródł.   |
+-------------------------+

+-------------------------+
| Wady Kompilacji         |
+-------------------------+
| - Czasochłonny proces   |
|   kompilacji            |
| - Mniejsza przenośność  |
|   binariów              |
| - Trudniejsze debugowanie|
+-------------------------+
                
Nowy Paradygmat: Idea Interpretacji
29/50

Alternatywa dla Kompilacji

Równolegle do rozwoju języków kompilowanych, narodziła się zupełnie inna filozofia wykonywania programów – interpretacja. W przeciwieństwie do kompilacji, gdzie cały kod źródłowy jest tłumaczony na kod maszynowy przed uruchomieniem, w modelu interpretowanym program jest wykonywany bezpośrednio z kodu źródłowego. Odpowiada za to specjalny program zwany interpreterem, który czyta kod źródłowy linia po linii (lub instrukcja po instrukcji), analizuje go i natychmiast wykonuje odpowiadające mu operacje. Nie ma tu oddzielnego etapu kompilacji i nie jest tworzony żaden plik wykonywalny. Program źródłowy jest jednocześnie programem "uruchamianym". To podejście oferuje zupełnie inny zestaw kompromisów w porównaniu z kompilacją, kładąc nacisk na elastyczność, szybkość rozwoju i przenośność, często kosztem czystej wydajności wykonania. Idea ta stała się fundamentem dla języków skryptowych.

Model Kompilowany
+----------+   +-----------+   +----------+
| Kod źród.| ->| Kompilator| ->| Kod masz.| -> Uruchomienie
+----------+   +-----------+   +----------+
(osobny krok)

Model Interpretowany
+----------+
| Kod źród.| ---\
+----------+    +-->| Interpreter | -> Natychmiastowe
                     |             |    wykonanie
+----------+    /--->| (czyta i    |
| Dane     |---/     | wykonuje)   |
+----------+         +-------------+
                
Czym Jest Interpreter?
30/50

Program, Który Wykonuje Inne Programy

Interpreter to program, którego zadaniem jest bezpośrednie wykonanie instrukcji napisanych w określonym języku programowania. Można go postrzegać jako wirtualną maszynę, która symuluje działanie procesora dla danego języka. Działanie interpretera można opisać jako pętlę "czytaj-analizuj-wykonaj" (Read-Eval-Print Loop, REPL). W każdej iteracji pętli interpreter odczytuje kolejną instrukcję lub fragment kodu, przeprowadza jego analizę leksykalną, składniową i semantyczną (podobnie jak front-end kompilatora), a następnie natychmiast wykonuje odpowiednie operacje. Nie generuje on kodu maszynowego; zamiast tego sam wywołuje odpowiednie, prekompilowane procedury wewnętrzne, które realizują zadania opisane w kodzie źródłowym. Dzięki temu pętla deweloperska (zmiana-uruchomienie) jest niezwykle krótka, co sprzyja szybkiemu prototypowaniu i interaktywnej pracy.

Pętla Interpretera (REPL)

  +-------------------------+
  |                         |
  |   START                 |
  |     |                   |
  |     v                   |
  | +-------+               |
  | | READ  | <-- Kod źródł. |
  | +-------+               |
  |     |                   |
  |     v                   |
  | +-------+               |
  | | EVAL  | (Analizuj)    |
  | +-------+               |
  |     |                   |
  |     v                   |
  | +-------+               |
  | | PRINT | --> Wynik     |
  | +-------+               |
  |     |                   |
  |     `-------------------'
  |                         |
  +-------------------------+
                
Kompilacja vs. Interpretacja: Kluczowe Różnice
31/50

Dwa Podejścia, Różne Kompromisy

Podstawowa różnica między kompilacją a interpretacją leży w momencie translacji kodu. Kompilator tłumaczy cały program na kod maszynowy przed jego pierwszym uruchomieniem, tworząc samodzielny plik wykonywalny. Interpreter tłumaczy kod w trakcie wykonania, instrukcja po instrukcji, za każdym razem, gdy program jest uruchamiany. Ta fundamentalna różnica prowadzi do odmiennych charakterystyk. Programy kompilowane są zazwyczaj znacznie szybsze, ponieważ cała praca związana z analizą i tłumaczeniem została wykonana z góry. Programy interpretowane uruchamiają się szybciej (brak etapu kompilacji), ale działają wolniej, ponieważ każda instrukcja musi być ponownie analizowana przy każdym wykonaniu. Z perspektywy przenośności, kod źródłowy programu interpretowanego jest w pełni przenośny – zadziała wszędzie tam, gdzie dostępny jest interpreter. W przypadku kompilacji, przenośny jest kod źródłowy, ale wynikowy plik binarny jest przypisany do konkretnej platformy.

| Cecha                 | Kompilacja                        | Interpretacja                      |
|-----------------------|-----------------------------------|------------------------------------|
| Moment translacji     | Przed uruchomieniem (jednorazowo) | W trakcie uruchomienia (za każdym razem) |
| Wynik translacji      | Plik wykonywalny (kod maszynowy)  | Brak (wykonanie bezpośrednie)     |
| Szybkość wykonania    | Wysoka                            | Niższa                             |
| Szybkość uruchomienia | Wolniejsza (wymaga kompilacji)    | Natychmiastowa                     |
| Przenośność           | Niska dla binariów                | Wysoka dla kodu źródłowego         |
| Wykrywanie błędów     | Statyczne (w czasie kompilacji)   | Dynamiczne (w czasie wykonania)    |
                
Narodziny Języków Skryptowych
32/50

Języki Stworzone do Integracji Systemu

Idea języków skryptowych wyewoluowała naturalnie z potrzeby automatyzacji zadań w interaktywnych systemach operacyjnych, takich jak Unix. Podczas gdy języki kompilowane (np. C, Fortran) były przeznaczone do tworzenia dużych, samodzielnych aplikacji, istniała rosnąca potrzeba posiadania prostszego narzędzia do wykonywania mniejszych zadań: manipulacji plikami, zarządzania procesami czy łączenia ze sobą istniejących programów w celu tworzenia nowych funkcjonalności. Języki skryptowe były odpowiedzią na tę potrzebę. Zazwyczaj były to języki interpretowane, co zapewniało szybkość i łatwość użycia. Charakteryzowały się prostszą składnią, dynamicznym typowaniem (brak konieczności deklarowania typów zmiennych) oraz silnymi wbudowanymi mechanizmami do obsługi tekstu i interakcji z systemem operacyjnym. Ich celem nie była maksymalna wydajność obliczeniowa, lecz maksymalna produktywność programisty w zadaniach automatyzacyjnych.

Języki Kompilowane (np. C)
+---------------------------+
| Budowa dużych, wydajnych  |
| aplikacji od podstaw.     |
| Np. system operacyjny,    |
| baza danych, gra 3D.      |
+---------------------------+

Języki Skryptowe (np. Shell)
+---------------------------+
| Automatyzacja, integracja |
| istniejących narzędzi.    |
| Np. backup plików,        |
| przetwarzanie logów.      |
+---------------------------+
                
Idea "Skryptu": Scenariusz dla Komputera
33/50

Zapisana Sekwencja Poleceń

Sama nazwa "skrypt" doskonale oddaje istotę tej koncepcji. Tak jak aktor w teatrze podąża za scenariuszem (skryptem), tak interpreter języka skryptowego wykonuje krok po kroku polecenia zapisane w pliku. Skrypt jest zatem plikiem tekstowym zawierającym sekwencję komend, które mogłyby być wpisane ręcznie w wierszu poleceń, ale zostały zapisane w celu ich wielokrotnego i zautomatyzowanego wykonania. Początkowo skrypty były prostymi listami poleceń. Z czasem jednak języki skryptowe wzbogacono o konstrukcje znane z "prawdziwych" języków programowania, takie jak zmienne, pętle, instrukcje warunkowe i funkcje. To pozwoliło na tworzenie znacznie bardziej złożonych i inteligentnych skryptów, zdolnych do podejmowania decyzji i adaptowania swojego działania w zależności od warunków. Skrypt stał się potężnym narzędziem w rękach administratorów i programistów.

# Prosty skrypt powłoki (backup.sh)
# To jest "scenariusz" dla powłoki systemowej

echo "Rozpoczynam tworzenie kopii zapasowej..."

# Krok 1: Utwórz archiwum katalogu /home/user
tar -czf /tmp/backup.tar.gz /home/user

# Krok 2: Skopiuj archiwum na serwer zdalny
scp /tmp/backup.tar.gz user@remote:/backups/

# Krok 3: Poinformuj o zakończeniu
echo "Kopia zapasowa zakończona!"
                
Rola Powłoki w Systemie Unix
34/50

Środowisko Skryptowe od samego Początku

System operacyjny Unix, tworzony od końca lat 60., od samego początku projektowany był z myślą o interaktywnej pracy i automatyzacji. Jego powłoka (początkowo Thompson shell, a następnie Bourne shell, sh) była czymś więcej niż tylko prostym interpreterem poleceń. Została zaprojektowana jako potężne środowisko programistyczne. Umożliwiała nie tylko uruchamianie programów, ale także łączenie ich ze sobą w potoki (pipelines), przekierowywanie standardowego wejścia i wyjścia, oraz definiowanie zmiennych. Co najważniejsze, każdą sekwencję poleceń można było zapisać do pliku i uruchomić jako skrypt. To sprawiło, że powłoka stała się pierwszym i najważniejszym językiem skryptowym w świecie Uniksa. Administratorzy i użytkownicy mogli tworzyć własne narzędzia, automatyzować powtarzalne czynności i dostosowywać system do swoich potrzeb bez potrzeby pisania i kompilowania programów w języku C.

Powłoka Unix jako interpreter

Użytkownik wpisuje:
> ls -l | grep ".txt"

Powłoka:
1. Uruchamia proces `ls -l`.
2. Tworzy potok (pipe).
3. Uruchamia proces `grep ".txt"`.
4. Przekierowuje standardowe
   wyjście `ls` na standardowe
   wejście `grep`.
5. Wyświetla ostateczny wynik.

To samo można zapisać w skrypcie.