% -*- coding: iso-8859-2 -*-

\documentclass[12pt, a4paper, pdftex, oneside]{article}
\usepackage[pdftex]{graphicx, color}
\usepackage[margin=2cm]{geometry}
\usepackage{fancyhdr}
\usepackage{fancyvrb}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[latin2]{inputenc}
\usepackage[magyar]{babel}
\ifx\magyarOptions\relax\else
  \PackageError{magyar.ldf}{Download newest magyar.ldf from
    \MessageBreak http://www.math.bme.hu/latex/ }{}
  \csname @@end\endcsname \fi
\usepackage[pdftex, colorlinks=true, backref=section, unicode, pdftex,
            linkcolor=blue, citecolor=blue, urlcolor=blue,
            breaklinks=true, pdfstartview=FitV, bookmarksnumbered=true,
            bookmarksopen=true,
            pdfauthor={Riskó Gergely <progalap@risko.hu>},
            pdftitle={Programozási alapismeretek 1. gyakorlati jegyzet},
            pdfsubject={Programozási alapismeretek 1. gyakorlati jegyzet},
            pdfkeywords={magyar unix GNU linux bash}]{hyperref}
%\usepackage{bookmark}
\title{Programozási alapismeretek 1. gyakorlat emlékeztető}
\author{Riskó Gergely <progalap@risko.hu>}
\date{ELTE IK PSZT, 2007.}\frenchspacing
\pagestyle{fancy}
\newcommand{\screenshot}[3]{%
  \begin{figure}[htp]
    \centering
    \includegraphics[#1]{#2}
    \caption{#3}
  \end{figure}
}

\DefineShortVerb{\_}
\DefineVerbatimEnvironment{ProgalapVerbatim}{Verbatim}
{fontsize=\footnotesize,frame=single,numbers=left,gobble=2,commandchars=\%\{\}}

\setlength{\headheight}{16pt}
\hyphenation{
pa-ra-mé-te-rek
pa-ra-mé-te-rek-et
egy-sze-rű
vég-re-hajt-ja
el-ér-he-tő-sé-gét
szög-le-tes
a-mi-vel
fut-tat-ha-tó-vá
e-di-tor-ral
pa-rancs-össze-té-telt
pa-rancs-össze-té-tel
ír-ha-tunk
vég-re-haj-tó-dik
meg-en-ge-dő-vé
le-he-tő-sé-günk
le-tölt-he-tő
ver-zi-ó-ja
web-lap-já-ról
el-vá-lasz-tott
meg-fe-le-lő-en
fe-led-kez-zünk
al-könyv-tá-rak
prog-ra-moz-ha-tunk
pa-ra-mé-ter-lis-tán-kat
hard-link-je-i
lis-tá-zás-kor
me-net-ren-de-ket
ki-fej-lesz-tett
sze-ret-né
}
\begin{document}

\maketitle

Követelmények és házi feladatok, órarend, ZH időpontok,
adminisztráció, a jegyzet új verziói:
\hbox{\url{http://www.gergely.risko.hu/oktatas-2007-pa1.hu.html}}

A jegyzet során feltételezzük, hogy bizonyos alapvető fogalmakkal a
hallgató tisztában van.  Pl. tudja, hogy mi a különbség egy szöveg
fájl és egy bináris fájl között és ezért érti, hogy miért nem
szerkeszthet Microsoft Worddel szövegfájlokat.  Az alapvető
informatikai ismeretekről remek szakkönyvek állnak rendelkezésre.

\section{A UNIX és a GNU/Linux}
Röviden: a UNIX egy olyan operációs rendszer, ami nagyon régen
készült.  A GNU/Linux pedig ennek egy utánzata, amely a szabad
szoftver mozgalom keretein belül készül(t).  Külön érdekessége, hogy a
félév során meg fogjuk tanulni haladó felhasználói szinten kezelni.

Bővebben:

\url{http://en.wikipedia.org/wiki/UNIX}

\url{http://en.wikipedia.org/wiki/GNU}

\url{http://en.wikipedia.org/wiki/Linux}

\url{http://www.gnu.org/}

\url{http://www.fsf.org/}

\url{http://www.kernel.org/}

Nem kell feltelepíteni ilyen operációs rendszert senkinek otthon,
habár nyugodtan megpróbálkozhat vele szabad
idejében\footnote{Legkönnyebben talán az Ubuntuval fog boldogulni:
  \url{http://www.ubuntu.com/}}.  Minden megtanulható és elvégezhető
az egyetem számítógé\-pe\-in a Lovardában\footnote{A Lovarda egy gépterem,
  ami a földszinten van a déli tömbben és minden gépen van Windows és
  GNU/Linux is.}, illetve a Pandorán\footnote{A Pandora egy
  szerverszámítógép, ahova bármely hallgató bejelentkezhet és az
  interneten elérhető a pandora.inf.elte.hu címen}.

Továbbá nem kari, hanem egyetemi szinten is lehetőség van GNU/Linux
rendszerre azonosí\-tót kérni.  Ezen a gépen további szolgáltatások, pl. az egyetem
hírcsoportjai (ELTE NEWS) is elérhetőek: \url{http://www.caesar.elte.hu/}.

Szintén az ELTE Informatikai Igazgatósága\footnote{\url{http://iig.elte.hu/}} üzemeltet egy levelezőlista
szolgáltatást, amin megtalálhatóak az egyes szakok levelezőlistái is,
nézzünk körül ezen a
szolgáltatáson\footnote{\url{http://listbox.elte.hu/}}.  Ajánlott
figyelemmel követni a
_progmat_\footnote{\url{https://listbox.elte.hu/mailman/listinfo/progmat}}
és a
_proginf_\footnote{\url{https://listbox.elte.hu/mailman/listinfo/proginf}}
elnevezésű listák történéseit.

\section{Alapvető parancsok, műveletek}
\subsection{Bejelentkezés egy GNU/Linux-os gépre}
Mindenki rendelkezik a Pandorára egy felhasználói névvel és jelszóval,
ezzel tud bejelentkezni a Lovarda helyi számítógépeire akár a
Windows-ba, akár a GNU/Linux-ba értelemszerű módon, kicsit
bonyolultabb a helyzet, ha a Pandorát vagy a Caesart akarja használni,
ugyanis ezek elé fizikailag nem tud leülni, csak interneten keresztül
tudja elérni
ún. SSH\footnote{\url{http://en.wikipedia.org/wiki/Secure_Shell}}
kliensprogrammal, amilyen pl. GNU/Linux-on a _ssh_ vagy
Windows-on a PuTTY\footnote{\url{http://en.wikipedia.org/wiki/PuTTY}}.

A megadandó számítógépnév (host name) a pandora.inf.elte.hu, a port a
22-es, a kiválasztandó protokoll az SSH, a
``window$\rightarrow$translation'' menüpontban a ``character set''-et
állítsuk ``UTF-8''-ra!  A beállításokat el is lehet menteni!

GNU/Linux alól a Pandorára való bejelentkezés az _ssh usernév@pandora.inf.elte.hu_ paranccsal lehetséges.

Minden esetben ügyeljünk a felhasználói név és a jelszó pontos
megadására, a kis- és nagybetűk közti különbségre!

Sikeres bejelentkezés után megjelennek a rendszergazda éppen aktuális
üzenetei, majd elindul a shell, a parancsértelmező.  Ez a program
fogja a továbbiakban minden parancsunkat feldolgozni, lényegében a
félév GNU/Linux-os része ezen program megismeréséről fog szólni.

Kijelentkezni a _logout_ paranccsal lehet:
\begin{ProgalapVerbatim}[label=\fbox{Egy bejelentkezés és egy kijelentkezés},labelposition=bottomline]
  %underline{errge@home:~$} ssh errge@pandora.inf.elte.hu
  errge@pandora.inf.elte.hu's password:
  Last login: Tue Sep  4 23:47:33 2007 from catv-5063052c.catv.broadband.hu
  Linux pandora 2.6.16-hardened-r10-pandora #1 Fri Jul 21 14:17:46 CEST 2006 i686 GNU/Linux

  * Van lehetőség authentikált smtp servert használni
  pandorás azonosítóval (SSL kell). Server: smtp.inf.elte.hu, port 465

  * Oracle adatbázis elérése lehetséges sqlplus cliensel a pandora-n.
  Használat: sqlplus username@oradb v sqlplus username@ablinux

  * Meglevo szovegfajlokat az iconv paranccsal lehet konvertalni:
  iconv -f ISO-8859-2 -t UTF-8 <regi_iso.txt >uj_utf8.txt
  iconv -f UTF-8 -t ISO-8859-2 <regi_utf8.txt >uj_iso.txt


  UTF-8 ekezetteszt: áéíóöőűÁÉÍÓÖŐÚÜŰ
  %underline{errge@pandora:~$} logout
  Connection to pandora.inf.elte.hu closed.
  %underline{errge@home:~$}
\end{ProgalapVerbatim}

Az aláhúzott rész a sor elején a shell által adott prompt, ahol várja
az utasításunkat, az utána következő részt kell begépelni, majd entert
ütve a számítógép válasza látható a következő promptig.

\subsection{Dokumentáció}
Minden tárgyalásra kerülő parancsról kérhetünk segítséget a %
_man 1 parancsnév_ segítségével.  A fontosabb parancsok segítőlapja,
illetve hibaüzenetei magyarul jelenhetnek meg a Pandorán, ha ez minket
zavar\footnote{Ugye zavar!}, akkor adjuk ki az _export LANGUAGE=C_
parancsot!  Hamarosan megtanuljuk beállítani, hogy ez minden
belépésünkkor automatikusan megtörténjen.

\subsection{Könyvtárak, fájlok}
Adatainkat fájlokban tárolhatjuk, melyeket könyvtárakba rendezhetünk.
Minden fájlnak van egy neve, a fájl rejtett, ha első betűje pont.  A
rejtett fájlok néhány esetben speciálisan viselkednek
(pl. automatikusan nem listázódnak, illetve a minden fájlra vonatkozó
parancsok rájuk nem vonatkoznak), de egyébként teljesen hagyományosak.
A fájlkiterjesztések használata nem annyira megszokott és magától
értetődő, mint Windowsban, de azért elterjedt.

A GNU/Linux rendszerekben minden állomány egy közös fában van, a fa
építőelemeit a _/_ jellel választjuk el, a fa gyökerének a neve
szintén a _/_.  Minden egyes pillanatban a shellünk a fa valamely
csomópontjában áll, ezt hívjuk az aktuális könyvtárnak vagy
munkakönyvtárnak.  Ez a _pwd_ paranccsal lekérdezhető, a _cd_
paranccsal változtatható és erre a műveletre olyan hétköznapilag
fogunk hivatkozni, minthogy ``menjünk bele a könyvtárba''.

Azért célszerű ez a fogalom, mert a fájlok nevét az aktuális
könyvtárhoz viszonyítva is megadhatjuk (ha nem a _/_ jellel kezdünk), és
így a saját könyvtáramban lévő _jegyzet.pdf_-re nem kell mindig a
bonyolult _/h/e/errge/jegyzet.pdf_ módon hivatkoznom, hanem ha az
aktuális munkakönyvtáram a _/h/e/errge_, akkor egyszerűen
_jegyzet.pdf_-et is írhatok.

Az munkakönyvtárunk neve a felhasználói név és a gépnév után a
promptban mindig látszik.

Most lássunk jópár parancsot egy példán keresztül, a parancsok egy
részét majd részletesebben is tárgyaljuk, de a már bemutatott _man_
paranccsal a dokumentációjuk önállóan is olvasható.  Figyeljünk fel a
_.._ és a _._ használatára!

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~$} mkdir progalap-pelda
  %underline{errge@pandora:~$} cd progalap-pelda
  %underline{errge@pandora:~/progalap-pelda$} mkdir konyvtar
  %underline{errge@pandora:~/progalap-pelda$} echo hello world >fajl
  %underline{errge@pandora:~/progalap-pelda$} ls
  fajl  konyvtar
  %underline{errge@pandora:~/progalap-pelda$} cp fajl masolat
  %underline{errge@pandora:~/progalap-pelda$} ls
  fajl  konyvtar  masolat
  %underline{errge@pandora:~/progalap-pelda$} cd konyvtar
  %underline{errge@pandora:~/progalap-pelda/konyvtar$} ls
  %underline{errge@pandora:~/progalap-pelda/konyvtar$} cp ../masolat megegymasolat
  %underline{errge@pandora:~/progalap-pelda/konyvtar$} ls
  megegymasolat
  %underline{errge@pandora:~/progalap-pelda/konyvtar$} cd ..
  %underline{errge@pandora:~/progalap-pelda$} ls .
  fajl  konyvtar  masolat
  %underline{errge@pandora:~/progalap-pelda$} pwd
  /h/e/errge/progalap-pelda
  %underline{errge@pandora:~/progalap-pelda$} cd ~
  %underline{errge@pandora:~$} ls progalap-pelda
  fajl  konyvtar  masolat
  %underline{errge@pandora:~$} pwd
  /h/e/errge
  %underline{errge@pandora:~$} cd /h/e/errge/progalap-pelda/
  %underline{errge@pandora:~/progalap-pelda$} cd ..
  %underline{errge@pandora:~$} rm progalap-pelda
  rm: cannot remove `progalap-pelda': Is a directory
  %underline{errge@pandora:~$} rmdir progalap-pelda
  rmdir: progalap-pelda: Directory not empty
  %underline{errge@pandora:~$} rm -r progalap-pelda
\end{ProgalapVerbatim}

A példában szereplő _echo_ dolga, hogy kiírja a paramétereit, az utána
szereplő jellel ez a kiírás egy fájlba irányítható.  A különböző fajta
átirányításokról még részletesen lesz szó.

Az _rmdir_ parancs csak üres könyvtárat töröl, míg az _rm_ parancs
alapértelmezés szerint nem foglalkozik könyvtárakkal, ha könyvtárat
adunk meg neki, hibával tér vissza.  Ugyanakkor a _-r_ opcióval
rekurzívan működik, azaz az egész megadott könyvtárstruktúrát bejárva,
minden fájlt és könyvtárat töröl, végül a megadottat is.  Ez fontos
jellemzője lesz a többi parancsunknak is, hogy a megfelelő opciók
megtalálásával a program működése nagyban befolyásolható.  Az
opciókról minden esetben tartalmaz részletes leírást a parancs man
oldala.

Láthatjuk, hogy amikor a saját könyvtárunkba belépünk (_/h/e/errge_ az
esetemben), akkor a promptban munkakönyvtárként a _~_ jelenik meg, nem
pedig a _/h/e/errge_.  Ez azért van, mert a shellben a _~_ rövidíti a
saját könyvtárunkat, ezt mi is használhatjuk, így a %
_cd ~/progalap-pelda_ helyett a _cd /h/e/errge/progalap-pelda/_ parancs is
szerepelhetne.

A Pandorán minden felhasználóhoz tartozik egy nyilvános könyvtár is,
az én esetemben ez a _/h/public/e/errge_, ami mindenki más által
böngészhető.  Próbáljuk ki, nézzük meg mások publikus könyvátrait,
menjünk oda cd-vel, listázzunk, nézelődjünk: fájlokat a _cat_
paranccsal jeleníthetünk meg a képernyőn!  A publikus könyvtáron belül
van egy \verb+public_html+ nevű könyvtár, aminek a tartalma weblapként
jelenik meg a \url{http://people.inf.elte.hu/errge/} címen.

Próbáljuk ki az _mc_ parancsot is, ennek hatására megjelenik egy
modern fájlkezelő alkalmazás (mint amilyen Windowson a Total
Commander, vagy a Far Manager).

A fájlaink és könyvtáraink neveibe sose tegyünk speciális karaktereket
(mint amilyenek az ékezetek, próbáljuk meg a szóközt is kerülni, bár
arról fogunk szót ejteni, hogy mire kell ügyelni szóköz használatakor).

\subsection{Editorok}
Munkánk során sokszor lesz szükség arra, hogy szövegfájlokat
szerkesszünk, ezt tehetnénk a saját gépünkön is, ugyanis hamarosan
megláthatjuk, hogyan másolhatunk fájlokat a Pandorára, de ez rendkívűl
kényelmetlen lenne hosszútávon, mindenképp érdemes megtanulni legalább
egy editort használni.  Egyébként is, egy programozó ideje nagy részét
UNIX gépek előtt tölti, így az ezen a rendszeren működő editorokat, de
legalább egyet alaposan ismernie kell, különben reménytelenül lassan
fog csak tudni dolgozni.

A kurzus ideje alatt a _vim_ vagy az _emacs_ valamelyikének
elsajátítását ajánlom.  Ha egyiket sem ismeri még a hallgató, akkor
inkább az _emacs_ megismerését javaslom, ugyanis ehhez sokkal több
kényelmi funkció érhető el és határtalanul testreszabható,
programozható.

Mindkét editorhoz elérhető rengeteg dokumentáció, bevezető, ezeket
Google-vel keressük meg!  Amennyiben ebbe mégsem akarunk időt
fektetni, akkor használjuk az _mcedit fájlnév_ parancsot fájlok
szerekesztésére, ez azonnal, intuitíven működtethető.

Gyakorlásként készítsük el a saját könyvátrunkban a _.profile_ nevű
állományt és írjunk bele egy sort, méghozzá az _export LANGUAGE=C_
sort.  Ne felejtsünk a sor végére entert tenni!  Jól nevelt ember, jól
nevelt szövegfájlai közt nincs olyan, aminek az utolsó karaktere nem
újsor.  Például azért, mert az ilyen fájlokat _cat_-tel megjelenítve a
következő promptunk (az újsor híján) nem sor elején kezdődik.  Továbbá
az utolsó sordobás hiánya okozhat egyéb kellemetlenségeket is
különböző programoknál (olyan alapvetőeknél is, mint a _grep_ vagy a
_sed_).  Mint az már sejthető, a _.profile_ állomány fut le minden
bejelentkezéskor, mielőtt az első promptunk megjelenne.  Ezekszerint
ebbe a fájlba tehetünk emlékeztetőket is magunknak, amik minden
alkalommal megjelennek belépéskor.  Próbáljuk ezt ki, használjuk az
_echo_ parancsot!

Ilyen egyszerű feladatot elvégezhetünk persze editor használata nélkül
is, hiszen az \linebreak _echo export LANGUAGE=C >~/.profile_ paranccsal
is elkészül a fájl és a tartalma is megfelelő.  Ugyanakkor ezzel a
módszerrel a fájl korábbi tartalma figyelmeztetés nélkül elvész, míg
egy editorban látjuk, hogy hoppá, ebben a fájlban már van valami.

\subsection{Levelezés, PINE}
A Pandorás felhasználónkhoz \texttt{felhasznalonev@inf.elte.hu} alakú
email cím kapcsolódik, ezért fedezzük fel a _pine_ program használatát
is, ezzel olvashatjuk el az érkezett leveleket és küldhetünk újakat
tetszőleges emailcímre a Pandorás azonosítónkról.  Ez az _mcedit_-hez
hasonlóan a képernyőn megjelenő információk segítségével azonnal
használható, figyeljünk fel arra, hogy az éppen használható billentyűk
jelentéséről a képernyő alján mindig megjelenik információ!  Ha egy
billentyűkombináció _^X_-ként van jelölve, ez a _Ctrl-X_-et jelenti.

Fájlokat csatolni egy levélhez az új levél foglamazása (%
_Compose Message_) közben lehet az _Attchmnt_ sorban a _^J_
lenyomásával.

\subsection{Fájlok másolása gépek között}
Tegyük fel, hogy egy másik, szintén internetre kapcsolt GNU/Linux
gépre vagy gépről akarunk átmásolni egy fájlt, ekkor az _scp_
parancsot használhatjuk:
\begin{ProgalapVerbatim}[label=\fbox{Az \texttt{scp} használata}]
  %underline{errge@home:~/tmp$} scp valami.zip errge@pandora.inf.elte.hu:
  errge@pandora.inf.elte.hu's password:
  valami.zip                                100%%  193KB 192.6KB/s   00:00
  %underline{errge@home:~/tmp$} cd ~/bpkonyv/
  %underline{errge@home:~/bpkonyv$} scp -r errge@pandora.inf.elte.hu:/h/public/f/fa/public_html/ps .
  errge@pandora.inf.elte.hu's password:
  f.ps                                      100%%   55KB  54.7KB/s   00:00
  elemi.ps                                  100%%  747KB 373.7KB/s   00:02
  ...
\end{ProgalapVerbatim}

Mindig először a forrásállományt kell megadni, majd a célt.  A célban
vagy a forrásban kettősponttal elválaszthatjuk a cél-, illetve
forrásgép nevét a gépen való elérési úttól, ha nem használunk
kettőspontot, akkor a helyi gépen értjük az elérési utat.  A gépnév
előtt, az _ssh_-hoz hasonlóan az at (_@_) jellel, mint elválasztóval a
felhasználói nevünket is megadhatjuk.  A _-r_ kapcsolót használva
tudtunk egész könyvtárat másolni, ugyanis a példában szereplő _ps_
az egy könyvtár neve volt.

Amennyiben az a gép, ami előtt ülünk, Windows operációs rendszert
futtat, akkor a már említett PuTTY projekt weblapjáról
letölthető\footnote{\url{http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html}}
a _pscp.exe_ nevű program, ami az _scp_-vel teljesen azonos módon
működik, csupán _pscp_ a neve, tehát a megtanult módon annak
segítségével másolhatunk Windowsról is fájlokat.

Amennyiben ennél kényelmesebb módszerre vágyunk, akkor próbáljuk ki a
WinSCP\footnote{\url{http://winscp.net/eng/download.php}} nevű programot!  Ez az _mc_-hez hasonlóan, egyik
ablakban mutatja a saját gépünk tartalmát és egy másikban a távoli
gépét (pl. Pandora) és a megszokott billentyűkkel (F5, F6, F8, stb.)
végezhetjük el a kívánt műveleteket.

\section{További egyszerű parancsok, opciók}
Mielőtt megnéznék, hogy hogyan lehet apró parancsokból hatalmas,
mindent elvégző, kávéfőző pipeline-okat (avagy
csővezetékeket\footnote{Ugyanis ez a magyar neve.  Pár könyv
  használja, mi is néha, de nem mindenki érti meg, ha mondjuk neki,
  hogy csővezeték.}) építeni, nézzünk még pár új parancsot,
illetve már ismert parancsokhoz új opciókat.  Természetesen nem fogjuk
megnézni az összes létező opciót, vagy parancsot.  Referenciaként a
man oldalakat lapozgassuk, ne ezt a jegyzetet!

\subsection{\texttt{cat}}
A _cat_ parancs az argumentumokban felsorolt fájlokat kiírja a képernyőre, opciói:
\begin{itemize}
\item _-n_: sorszámozás
\item _-b_: a nem üres sorok sorszámozása
\item _-E_: a sorvégek megjelölése \texttt{\$} jellel, így láthatóvá válnak a sorvégi láthatatlan karakterek\footnote{Egy kultúrált ember fájlaiban nincsenek a soros végén fölösleges szóközök vagy tabok (azaz whitespacek).}
\end{itemize}

Próbáljuk ki ezeket az opciókat egy--két szövegfájlon, amiket megírtunk
a kedvenc editorunkkal!\footnote{Ami mostanra ugye az \texttt{emacs} és nem a \texttt{vim}... ;)}

\subsection{\texttt{ls}}
\begin{itemize}
\item _-a_: a rejtett, ponttal kezdődő bejegyzések megjelenítése
\item _-A_: mint _-a_, de nem listázza a mindig jelenlévő _._ és _.._ bejegyzést
\item _-l_: részletes lista soronként egy fájllal (bejegyzéssel), a mezők:
  \begin{itemize}
  \item fájltípus (1 karakter) és jogosultságok (9 karakter) leírása
  \item hardlinkek száma
  \item a fájl tulajdonosa
  \item a fájlhoz rendelt csoport
  \item a fájl mérete
  \item az utolsó módosítás ideje
  \item a fájl neve
  \end{itemize}
\item _-d_: a könyvtárak egy bejegyzésként való megjelenítése a tartalmuk helyett

  Ha a paraméter listában megadunk egy könyvtárat, akkor az _ls_
  alapértelmezés szerint a könyvtár tartalmát listázza, hiszen így
  működik az intuíciónknak megfelelően az _ls /h/e_ parancs.
  Ugyanakkor ha meg szeretnénk tudni, hogy milyen is a jogosultságok
  beállítása a publikus és a privát könyvtárunkon, mi lehet a
  különbség, akkor meg az tűnik logikusnak, hogy kiadjuk az %
  _ls -l ~ /h/public/e/errge_ parancsot, ami azonban ilyenkor a két
  említett könyvtár tartalmát listázza, mit sem mondva magukról a
  könyvtárakról, ilyenkor kell használni ezt az opciót.
\end{itemize}

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~$} ls -ld ~ /h/public/e/errge
  drwx------ 23 errge progterv 4096 2007-09-09 23:47 /h/e/errge
  drwxr-xr-x  5 errge progterv  117 2007-09-09 21:22 /h/public/e/errge
\end{ProgalapVerbatim}

A jogosultságokról, tulajdonosokról, csoportokról és a hardlinkekről még lesz szó.

Vegyük észre a példában, hogy az _ls -l -d ..._ parancs _ls -ld ..._
formában is kiadható, azaz az opciók összevonhatók.  Ez igaz minden
programra (parancsra), a rövid opciónevek tekintetében.  Van ugyanis
sok opciónak hosszú neve, a _-d_-é például a _--directory_, míg a
_-l_-nek nincs hosszú neve.  Vannak olyan ritkán használt opciók is,
amiknek csak hosszú neve van, illetve vannak olyan hosszú nevű opciók
is, amiknek plusz argumentumot is kell adni, például:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~$} ls -l --full-time righaat.txt
  -rw-r--r-- 1 errge progterv 11561 2007-03-21 14:29:33.599879779 +0100 righaat.txt
  %underline{errge@pandora:~$} ls -l --time-style=+%%Y%%m%%d-%%H%%M%%S righaat.txt
  -rw-r--r-- 1 errge progterv 11561 20070321-142933 righaat.txt
\end{ProgalapVerbatim}

Az utolsó példában látható rejtélyesnek tűnő kifejezés formáját a
_date_ parancs tárgyalásakor majd tisztázzuk, vagy a _man 1 date_ súgó
használatával már most megismerkedhetünk velük.

Rövid névvel is rendelkező opció is várhat argumentumot, azonban az
egyenlőségjelet nem szabad kitenni a rövid opciónév után:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/public$} ls -w 20
  asm2
  local
  nevnap.tar
  public_html
  recept.txt
  xor
  xor.asm
  xor.o
  %underline{errge@pandora:~/public$} ls -w
  ls: option requires an argument -- w
  Try `ls --help' for more information.
  %underline{errge@pandora:~/public$} ls -w=30
  ls: invalid line width: =30
  %underline{errge@pandora:~/public$} ls -w30
  asm2         recept.txt
  local        xor
  nevnap.tar   xor.asm
  public_html  xor.o
  %underline{errge@pandora:~/public$} ls --width=30
  asm2         recept.txt
  local        xor
  nevnap.tar   xor.asm
  public_html  xor.o
\end{ProgalapVerbatim}

Próbáljuk ki, hogy mi történik, ha teljesen rövid _-w30_ forma mellé
még egy _-d_ opciót is szeretnénk besűríteni!  A _-w30_ kifejezésben
hova kell ekkor tennünk az _d_-t, hogyha az argumentumok közt
könyvtárakat sorulunk fel, akkor azoknak ne listázza a tartalmát?
Olvassunk utána, hogy mit is csinál a _-w_ kapcsoló és egyben értsük
meg a _-1_ kapcsoló használatát is!  Lehet különbség az _ls -w1_,
illetve _ls -1_ kimenete között?

\subsection{A \texttt{more} és a nála is több \texttt{less}}

Ezzel a két paranccsal hosszú fájlokat tekinthetünk meg olyan
terminálon, aminek a magassága jóval kisebb, mint a fájl sorainak
száma.  A _less_-ből a _q_ gombbal lehet kilépni, a _more_
automatikusan kilép, amikor a fájl végére érünk a lapozásban.  További
információ a man oldalaikban található, a _less_ sokkal többmindenre
képes (pl. kurzor gombokkal könnyedén visszafele is lapozható a
megjelenített dokumentum).  A _man_ parancs is a _less_-t használja
alapértelmezés szerint a Pandorán, de ez a _PAGER_ környezeti
változóval megváltoztatható.\footnote{A környezeti változókról később még lesz szó.}

\subsection{\texttt{wc} (word count)}
Segítségével megszámolhatjuk szövegfájlainkban lévő karakterek, szavak és sorok számát:
\begin{ProgalapVerbatim}
  %underline{errge@home:~/docs/elte/progalap1$} wc jegyzet.tex
    492  2515 22220 jegyzet.tex
  %underline{errge@home:~/docs/elte/progalap1$} wc -w jegyzet.tex Makefile
   2444 jegyzet.tex
     33 Makefile
   2477 total
  %underline{errge@home:~/docs/elte/progalap1$} wc -c jegyzet.tex Makefile
  21448 jegyzet.tex
    301 Makefile
  21749 total
  %underline{errge@home:~/docs/elte/progalap1$} wc -l jegyzet.tex Makefile
    470 jegyzet.tex
     15 Makefile
    485 total
  %underline{errge@home:~/docs/elte/progalap1$} wc -w jegyzet.tex Makefile
   2444 jegyzet.tex
     33 Makefile
   2477 total
  %underline{errge@home:~/docs/elte/progalap1$} wc jegyzet.tex Makefile
    470  2444 21448 jegyzet.tex
     15    33   301 Makefile
    485  2477 21749 total
  %underline{errge@home:~/docs/elte/progalap1$} wc -wc jegyzet.tex
   2577 22752 jegyzet.tex
\end{ProgalapVerbatim}

Amennyiben több fájlt adunk meg, akkor összegzést is kapunk, a
különböző bemutatott opciókkal pedig válogathatunk, hogy a három
lehetséges mennyiség (sorok, szavak, karakterek) közül melyikek
jelenjenek meg.\footnote{Az opciók sorrendjének variálása nem
  változtat a kimenet sorrendjén, az mindig sorok, szavak, karakterek
  sorrendű, tehát a \texttt{wc -wl ...} parancs ugyanazt csinálja, mint a
  \texttt{wc -lw ...} parancs.}

\subsection{\texttt{sort}}
A _sort_ parancs segítségével fájlok sorai ABC (vagy a _-n_ opcióval
numerikus) sorrendbe rendezhetőek, a _-r_ kapcsolóval pedig a
fordított sorrend érhető el.

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp$} cat proba
  2
  z
  10
  a
  %underline{errge@pandora:~/tmp$} sort proba
  10
  2
  a
  z
  %underline{errge@pandora:~/tmp$} sort -n proba
  a
  z
  2
  10
  %underline{errge@pandora:~/tmp$} sort -nr proba
  10
  2
  z
  a
\end{ProgalapVerbatim}

\subsection{\texttt{reset}}
Ha a képernyőre nem megjeleníthető karaktereket írunk (pl. bináris
fájl megjelenítésével, mondjuk a _cat /bin/ls_ paranccsal), azzal
akarutunkon kívűl is ``megbolondulhat'' a terminálunk és a megjelenő
prompt is szemétnek tűnik ezekután.  Ilyenkor vakon adjuk ki a _reset_
parancsot, aminek hatására minden visszaáll a régi kerékvágásba.

\subsection{\texttt{gzip}}
Régóta ismertek
algoritmusok\footnote{\url{http://en.wikipedia.org/wiki/Lossless_data_compression}}
fájlok méretének a csökkentésére, a bennük lévő redundanciák
csökkentésével.

A _gzip_ a Huffman kódolás és az LZ77 felhasználásával tömörít
fájlokat.  Fájlok tömörítése a _gzip fájlnév_ paranccsal, kibontásuk a
_gunzip fájlnév.gz_ paranccsal érhető el.  Tö\-mö\-rí\-tés\-kor és kibontáskor
is a kiindulási fájl törlődik és helyette csak a kibontott vagy a
tömörített marad meg.  Ettől eltérő viselkedést a _-c_ kapcsolóval
érhetünk el, azonban ekkor a kimenet a standard outputra érkezik és
annak átirányításáról gondoskodnunk kell, erről még lesz szó.

\subsection{\texttt{tar}}
Az előbb említett _gzip_ parancs csupán arra való, hogy egy-egy fájlt
tömörebb formára hozzunk és a továbbiakban úgy tároljuk.  Több fájl,
illetve egész könyvtárstruktúrák egy fájlba való ábrázolására, majd
később ezen fájlból a könyvtárstruktúra visszaállítására a _tar_
parancs való.

Lássuk\footnote{A \texttt{find test} parancs itt csak kiírja a \texttt{test} nevű könyvtár tartalmát rekurzívan.}:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp$} find test
  test
  test/file
  test/subdir
  test/subdir/file2
  %underline{errge@pandora:~/tmp$} tar -c -f test.tar test
  %underline{errge@pandora:~/tmp$} rm -r test
  %underline{errge@pandora:~/tmp$} ls -l
  total 12
  -rw-r--r-- 1 errge progterv 10240 2007-09-16 23:54 test.tar
  %underline{errge@pandora:~/tmp$} gzip test.tar
  %underline{errge@pandora:~/tmp$} ls -l
  total 4
  -rw-r--r-- 1 errge progterv 204 2007-09-16 23:54 test.tar.gz
  %underline{errge@pandora:~/tmp$} gunzip test.tar.gz
  %underline{errge@pandora:~/tmp$} tar -x -f test.tar
  %underline{errge@pandora:~/tmp$} ls -l
  total 12
  drwxr-xr-x 3 errge progterv    30 2007-09-16 23:53 test
  -rw-r--r-- 1 errge progterv 10240 2007-09-16 23:54 test.tar
  %underline{errge@pandora:~/tmp$} find test
  test
  test/file
  test/subdir
  test/subdir/file2
  %underline{errge@pandora:~/tmp$} tar -t -f test.tar
  test/
  test/file
  test/subdir/
  test/subdir/file2
\end{ProgalapVerbatim}

Mint az a példában látható, a _tar_ parancsnak három fontos módja van,
amit a megfelelő opciók megadásával lehet kiválasztani.  Az első a
csomagolványok létrehozása (a _-c_), a második a csomagolványok
kibontása (a _-x_), a harmadik pedig a csomagolványok tartalmának
kiiratása (a _-t_).  A csomagolvány fájl neve, amivel a műveletet
végrehajtjuk, mindig a _-f_ opcióval adtható meg az opció
argumentumaként.  Valamint csomagolvány létrehozásakor még kötelező
megadni az egész parancs argumentumában a csomaghoz hozzáadandó fájlok
és/vagy könyvtárak nevét.

Látható, hogy a csomagolás helymegtakarítást nem okoz, sőt, általában
a fájlokat kicsomagolt állapotban a fájlrendszer hatékonyabban tudja
tárolni, mint a _tar_, azonban a _gzip_ segítségével a csomagolványt
be is tömöríthetjük és így már a windowsból jól ismert zip fájlokkal
ekvivalens eredményt érhetünk el.  Az így létrejövő _.tar.gz_
kiterjesztésű fájlokat szokás egyszerűen csak _.tgz_ kiterjesztéssel
ellátni.  Ezt a részét a jegyzetnek a _gunzip_ parancs is olvasta és
ezért _.tgz_ kibontásakor a kibontott fájlt _.tar_ kiterjesztéssel
látja el.

Mivel nagyon sokszor a _tar_-t és a _gzip_-et az itt leírt módon
együtt kell használni, erre külön opciót is bevezettek a _tar_-ban (a
_-z_-t).  Ezt megadva, az elkészülő csomagolvány tömörítve is lesz
automatikusan a _gzip_ meghívásával, illetve használhatjuk ezt a
kapcsolót kibontáskor is és akkor emiatt a _tar_ rögtön tudja, hogy a
megadott csomagolványt először a _gzip_-pel ki is kell bontani.

\subsection{\texttt{tail} és \texttt{head}}
E parancsok segítségével kiírható a fájlok eleje vagy vége.
Alapértelmezés szerint mindkettő az első/utolsó 10 sorral dolgozik, de
a _-n_ opció ügyes megválasztásával ez befolyásolható:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~$} for i in $(seq 1 25); do echo $i >>szamok; done
  %underline{errge@pandora:~$} head -n 3 szamok
  1
  2
  3
  %underline{errge@pandora:~$} tail -n 3 szamok
  23
  24
  25
\end{ProgalapVerbatim}

Próbáljuk ki, hogy mi a _head -n -3 szamok_, % buta emacs
_tail -n -3 szamok_, _head -n +3 szamok_ és _tail -n +3 szamok_
parancsoknak mi a hatása és jegyezzük meg ezeket a lehetőségeket, jól
jöhet, ha tudunk róluk!

\subsection{\texttt{cut}}
Egy fájl minden sorának bizonyos részeit vághatjuk ki ezzel a paranccsal.

A kivágandó rész megadható a karakterek pozícióinak specifikálásával
vagy úgynevezett mezők használatával.  A mezők arra használhatók, hogy
bizonyos elválasztókarakter mentén olyan szeleteket is kezelni
tudjunk, amelyben a betűk száma nem azonos.

Amennyiben a _-c <lista>_ (avagy _--characters=<lista>_) opciót
használjuk, akkor karakterpozíciókról beszélünk, amennyiben pedig a
_-f <lista>_ (avagy _--fields=<lista>_) kapcsolót, akkor mezőkről.
Mindkét esetben a lista vesszővel elválasztott elemekből épül fel,
ahol az egyes elemek alakja _N_, _N-_, _N-M_ vagy _-M_ lehet, itt az
_N_ és az _M_ számok, a jelentés pedig értelemszerű.  A _-f_ esetében
a mezőket a _TAB_ karakter választja el, de ez megváltoztatható a _-d_
(hosszúneve: _--delimeter_) használatával, ami argumentumként egyetlen
karaktert vár, a kívánt elválasztókaraktert.\footnote{Az intervallumok
  sorrendjének variálása a \texttt{wc}-hez hasonlóan nem hoz más
  eredményt, mint a növekvő sorrendben való megadásuk.}

A _--complement_ opció megadásával a vágás minden sorra külön-külön
vett pontos ellentéte hajtódik végre.

\begin{ProgalapVerbatim}[label=\fbox{A \texttt{cut}}]
  %underline{errge@pandora:~$} vi test
  %underline{errge@pandora:~$} cat test
  egy-maganal volt a gyilkos fegyver
  ketto-csipkebokor vesszo
  %underline{errge@pandora:~$} cut -c4-10 test
  -magana
  to-csip
  %underline{errge@pandora:~$} cut -d- -f1 test
  egy
  ketto
  %underline{errge@pandora:~$} cut -d" " -f1,3- test
  egy-maganal a gyilkos fegyver
  ketto-csipkebokor
  %underline{errge@pandora:~$} cut -d" " -f1,4- test
  egy-maganal gyilkos fegyver
  ketto-csipkebokor
\end{ProgalapVerbatim}

\subsection{\texttt{uniq}}
Ha egy fájlt a _cat_ helyett a _uniq_-kal iratunk ki, akkor a fájlban
egymásután többször szereplő teljesen azonos sorok csak egyszer
jelennek meg.

Opciói:
\begin{itemize}
\item _-u_: csak az egyedi sorok megjelenítése
\item _-d_: csak az ismétlődő sorok (egyszer való) megjelenítése
\item _-c_: ezzel az opcióval a sorok elé kiírja, hogy az a sor
  mennyiszer fordult elő egymásután
\end{itemize}

Ezt a parancsot programjaink kimeneteinek szépítésére, illetve a
különböző számlálásoknál az azonos találtok többször számolásának
elkerülésére használhatjuk majd.

\subsection{\texttt{fgrep}}
Ez a parancs karakterláncra szűr az input fájlban.  A parancs első
argumentuma mindig a keresett karakterlánc, majd a fájlnevek
felsorolása jön.  Pl.: _fgrep almafa fajl1 fajl2 fajl3_.

Az _-x_ opcióval megköthetjük, hogy a megadott karakterlánc ne a sorban
része legyen csupán, hanem a teljes sort töltse ki.

A kimeneten az illeszkedő sorok jelennek meg.  Amennyiben több fájlt
adtunk meg, akkor az _fgrep_ azt is kiírja, hogy melyik fájlban van az
abban a sorban szereplő találat.  Próbáljuk ki a szóbajövő eseteket
(sok fájl, egy találat; sok fájl, sok találat; nincs találat; egy
fájl, stb.!

\subsection{\texttt{cp}}
Fájlok másolása kétféle alakban:
\begin{itemize}
\item _cp FORRÁS CÉL_, ahol a _FORRÁS_ egyelemű,
\item _cp FORRÁS... CÉL_, ahol a _FORRÁS..._ sok paraméter is lehet és
  mindet másolja, de így a _CÉL_ kötelezően könyvtár.
\end{itemize}

Mindkét esetben megadható a _-a_ kapcsoló a parancs elején, hogy a
_FORRÁS_ részben lévő könyvtárakat egy az egyben másolja, így teljesen
(jogosultságokra is) egyező rekurzív másolat készíthető.

\subsection{\texttt{mv}}
Az _mv_ a _cp_-vel egyező két formában működik, azonban mindig
eltávolítja a _FORRÁS_-t, amikor már készen van a _CÉL_.
\footnote{Vagy, ha lehetséges, akkor egylépésben csinálja ezt a kettő
  dolgot.}  Könyvtárakkal mindenféle opció nélkül is hajlandó
foglalkozni, így _-a_ opciója nincs is.

\subsection{\texttt{last}}
A számítógépre történt belépéseket listázhatjuk ezzel a paranccsal.
Amennyiben egy belépés távoli volt\footnote{A Pandorán és egyéb
  szervergépeken kevés, rendszergazdai művelettől eltekintve minden
  belépés ilyen természetesen.}, akkor a _last_ megpróbálja azt is
megmutatni, hogy melyik számítógépről történt ez a bejelentkezés.
Azonban amikor a kimeneti formátumát kitalálták, a számítógépek
elnevezése az interneten még jóval rendezettebb és rövidebb neveket
eredményező volt.  Így csak 16 karaktert tartottak fent, ami ma már
kevés.  Ráadásul a gépneveknek általában a vége az érdekes, hiszen
ebből lehet következtetni arra, hogy valaki az épületben van-e,
érdemes-e megpróbálni megkeresni, stb.  Ugyanakkor a last parancs pont
a gépnév végét vágja le, ha nem fér el a 16 karakterben.  Ezért mi
használni fogjuk mindig a _-a_ kapcsolót, ami a gépnevet a kimenet
legvégére viszi és így ott (szinte) korlátlan hely áll rendelkezésre a
megjelenítéshez.

Amennyiben csak valakiknek a belépéseire vagyunk kiváncsiak, akkor
soroljuk fel ezeket a felhasználókat a parancs végén argumentumokként.

Az első oszlopban található a felhasználó neve, ez sosem hosszabb 8
betűnél és biztosan nem tartalmaz szóközt vagy TAB karaktert.  Utána
következik a 10. karaktertől kezdve (12 karakter hosszan) a belépéshez
felhasznált terminál, távoli bejelentkezésekkor ez valami fizikailag
nem létező eszközön foglalt (a mi szempontunkból véletlenszerű
sorszámú) képernyő.  A 23. karakterpozíciótól 36 karakteren keresztül
a bejelentkezés kezdeti dátuma és a gépen töltött időintervallum
szerepel.\footnote{Ezen rész értelmezése ember számára könnyű, azért
  az \texttt{fgrep} segítségével keressünk rá egy + jelet tartalmazó
  sorra, hogy arra is lássunk példát, ha egy bejelentkezés több, mint
  egy napig él.  Ugyanakkor programmal eldönteni, hogy valaki be
  volt-e jelentkezve ekkor, meg akkor nehézkes ezen információ
  alapján, erre a \texttt{-t} opció való, nézzük meg a man oldalban a
  használatát!} A 61. karaktertől kezdődik ezekután a bejelentkezéshez
használt gép neve.

\begin{ProgalapVerbatim}[label=\fbox{A \texttt{last} alapértelmezés szerint, majd a \texttt{-a} kapcsolóval}]
  %underline{errge@pandora:~$} last
  kandras  pts/3        g3kthljf0y.adsl. Mon Sep 17 01:18 - 01:28  (00:09)
  nct      pts/1        fw.tigra.hu      Mon Sep 17 01:16 - 01:41  (00:24)
  kandras  pts/3        g3kthljf0y.adsl. Mon Sep 17 01:09 - 01:14  (00:04)
  ...
  szabogab pts/0        dsl51b642e3.pool Sat Sep  1 06:38 - 06:44  (00:05)

  wtmp begins Sat Sep  1 06:38:58 2007
  %underline{errge@pandora:~$} last -a
  kandras  pts/3        Mon Sep 17 01:18 - 01:28  (00:09)     g3kthljf0y.adsl.datanet.hu
  nct      pts/1        Mon Sep 17 01:16 - 01:41  (00:24)     fw.tigra.hu
  kandras  pts/3        Mon Sep 17 01:09 - 01:14  (00:04)     g3kthljf0y.adsl.datanet.hu
  ...
  szabogab pts/0        Sat Sep  1 06:38 - 06:44  (00:05)     dsl51b642e3.pool.t-online.hu

  wtmp begins Sat Sep  1 06:38:58 2007
\end{ProgalapVerbatim}

\section{Parancsfájlok}
Nyilvánvaló lehet, hogy a parancsértelmező viszonylag mély
tárgyalásába nem mennénk bele, ha a cél az lenne, hogy az olvasó
tudjon kiadni parancsokat a terminálon.  Hiszen kinek lenne kedve egy
egyszeri eset kedvéért (főleg kezdőként, amíg lassan megy) bonyolult
dolgokat átgondolni.

Azért kell mindezt megérteni, mert a parancsainkat beírhatjuk egy
fájlba és azt a fájlt bármennyiszer futtathatjuk később is.  A
parancsfájlainkat elkészíthetjük kényelmesen egy editorral,
kipróbálhatjuk, hogy mi történik, javíthatunk, azok megmaradnak
(valamint gyakorlatvezetőknek is így adhatjuk be a feladatainkat).

Habár nem kötelező, a parancsfájlainkat mindig kezdjük a _#!/bin/bash_
sorral és utána egy új sorral, valamint adjunk nekik _.sh_
kiterjesztést pl.:

\begin{ProgalapVerbatim}[label=\fbox{\texttt{test.sh} és a futtatása}]
  %underline{errge@pandora:~$} cat test.sh
  #!/bin/bash

  echo Ez a parancsfajl nem csinal semmi erdekeset, csupan
  echo a kepernyore irkal, de ket paranccsal, mindketto lefut,
  # kommentek így helyezhetőek el a sor végéig a #-tól kezdődően
  echo sot, mindharom, ha meg ezt a sort is latjuk.
  %underline{errge@pandora:~$} bash test.sh
  Ez a parancsfajl nem csinal semmi erdekeset, csupan
  a kepernyore irkal, de ket paranccsal, mindketto lefut,
  sot, mindharom, ha meg ezt a sort is latjuk.
  %underline{errge@pandora:~$}
\end{ProgalapVerbatim}

\section{Csatornák}
A shell a működése közben nyitva tart három csatornát, ahonnan
olvashat (ebből van egy), illetve ahova írhat (ebből van kettő):
\begin{itemize}
\item standard bemenet (0): alapértelmezés szerint a terminál billentyűzete
\item standard kimenet (1): alapértelmezés szerint a terminál képernyője
\item standard hibacsatorna (2): alapértelmezés szerint a terminál képernyője
\end{itemize}

Az eddig említett legtöbb parancs, ha nem adnak meg neki fájlnév
argumentumokat, akkor a standard inputról olvas, ezért van az, hogy a
_cat_ ``lefagy'', ha önmagában futtatjuk.  Igazából bemenetre vár.  A
bemenet végét billentyűzeten a _Ctrl-D_-vel jelezhetjük, a fájloknak
egyszerűen pedig van végük.

A parancsok a normális működésük eredményét írják a kimeneti
csatornára és a hibaüzenete\-ket (mint pl. nem megfelelő használat) írják
a hibacsatornára.

\subsection{Átirányítások}
A standard bemenet fájllal helyettesíthető, ha parancssor végére a
_<fájlnév_ karaktersorozatot írjuk (vagy még formálisabban a
_0<fájlnév_ karaktersorozatot), illetve a kimenet új fájlba (avagy
régi felülírottjába) irányítható, ha a végére a _>fájlnév_ (ami
megegyezik az _1>fájlnév_ hatásával) sztringet írjuk, a standard
errort a _2>fájlnév_ irányítja át.  A _>_ helyett a _>>_ is állhat,
ekkor létező fájl esetén felülírás helyett hozzáírás történik.

Megjegyezzük, hogy nem szabad ugyanabba a fájlba egyszerre
átirányítani két különböző csatornát az _1>pelda 2>pelda_
parancsvéggel, ugyanis ekkor mindenféle versenyhelyzetek lép\-hetnek fel
és a kimenet erősen kevert vagy akár veszteséges is lehet, erre van
egy külön formula: _>pelda 2>&1_ (fontos a sorrend, fordítva nem
működik).

\subsection{Pipeline-ok}\label{pipeline}
Megoldható az is, hogy két vagy több parancsot indítsunk el egyszerre,
egy parancssorral és mindig az előző parancs kimenete adja a következő
parancs bemenetét.  Ez lesz az az elem, ami az eddig felsorolt unalmas
lehetőségeknek hatalmas erőt ad.  Ez volt az az ötlet, amivel nagyon
hosszú időre sikerült megoldást adni viszonylag nehezen
megfogalmazható, előtte külön-külön programokat igénylő feladatokra
is.

A parancsok közé a _|_ jelet kell tenni, minden parancsrész elindul
párhuzamosan és feldolgozzák egymás be- és kimeneteit, a csővezeték
első és utolsó parancsának be- és kimenete a _<_, _>_ és _>>_ jelekkel
a fent leírt módon átirányítható.

Pl. hány különböző felhasználó jelentkezett be eddig ebben a hónapban
a Pandorára?

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~$} last | head -n -2 | cut -c1-8 | sort | uniq | wc -l
  1137
  %underline{errge@pandora:~$}
\end{ProgalapVerbatim}

További feladatok:
\begin{itemize}
\item Hányszor jelentkezett be _bzsr_ a Pandorára?
\item Mennyi b kezdőbetűs felhasználó jelentkezett be a Pandorára?
\item Számoljuk meg a bejelentkezési könyvtárban lévő directorykat!
\item Az _adatok_ fájl minden sora egy hallgató nevét, lakhelyét,
  születési dátumát, emailcímét és szakját tartalmazza; ezeket
  egymástól _:_-tal elválasztva.
  \begin{itemize}
  \item Mondjuk meg, hogy mely városokban laknak a hallgatók!
  \item Adjuk meg a fizikusok számát!
  \item Készítsünk névsort a proginfósokról!
  \end{itemize}
\item A _VB_ fájlban tároljuk az eddigi labdarúgó VB győzteseinek
  nevét, az évszámmal és a rendező ország nevével együtt.
  \begin{itemize}
  \item Hány különböző nyertes van?
  \item Ki nyert 1966-ban?
  \item Kik tudtak többször is nyerni?
  \item Melyik ország nyert legtöbbször és hányszor?
  \end{itemize}
\item Hány olyan sor van a _mondatok_ nevű fájlban, amiben a tej és a vaj szó
  együtt szerepel?
\item Készítsük el parancssorral a _3legtobb_ nevű fájlt, amiben a
  Pandorára ebben a hónapban legtöbbször bejelentkezett felhasználók
  közül az első három szerepel, a bejelentkezési számaikkal együtt,
  csökkenő sorrendben.
\end{itemize}

\section{Parancsfeldolgozás}
Amikor egy parancsot kiadunk a shellünkben, akkor azt a
parancsértelmező először is a benne szereplő (védetlen) szóközök (és
tabok) mentén szavakra vágja, majd végrehajt egy elég bo\-nyo\-lult
szabályrendszer alapján különböző kifejtéseket.

Fontos már most megérteni, hogy az parancssor kifejtését a shell
végzi, nem pedig az egyes programok (és így a hamarosan bevezetendő,
más operációsrendszerekből már jól ismert jo\-ker\-ka\-rak\-te\-rek értelmezését
sem kell újra és újra megírni).

Mivel ezek a szabályok tényleg bonyolultak, először nézzük meg, hogyan
fogjuk őket tesztelni.  A legtöbb rejtélyes hiba biztos, hogy a
kifejtések körül lesz minden olyan hallgatónak, aki először találkozik
Unix rendszerrel.\footnote{Sőt, azoknak is, akik 10 éve írnak shell
  programokat.}  A tesztelésben a _:_ parancs fog segíteni, aminek
lényege, hogy nem csinál semmit, viszont gyorsan be lehet gépelni,
illetve a _set -x_ kiadása után a végrehajtott parancsok előtt
megjelenik, hogy a shell hogyan értette őket.  Ez az ami érdekes most,
maga a _:_ futása nem lesz túl érdekes, mert mint említettem, nem
csinál semmit.  Ha befejeztük a próbálkozást és zavar a túl sok
megjelenő információ minden parancs előtt, akkor a _set +x_
hatástalanítja ezt a nyomkövető beállítást.

\begin{ProgalapVerbatim}[label=\fbox{Kifejtések nyomkövetése}]
  %underline{errge@pandora:~/tmp$} A="x y"
  %underline{errge@pandora:~/tmp$} ls -l
  total 0
  -rw-r--r-- 1 errge progterv 0 2007-09-18 00:23 fajl1
  -rw-r--r-- 1 errge progterv 0 2007-09-18 00:23 fajl2
  drwxr-xr-x 2 errge progterv 6 2007-09-18 00:23 szokoz a konyvtar neveben
  -rw-r--r-- 1 errge progterv 0 2007-09-18 00:24 xxx
  %underline{errge@pandora:~/tmp$} set -x
  %underline{errge@pandora:~/tmp$} : * $A
  + : fajl1 fajl2 'szokoz a konyvtar neveben' xxx x y
  %underline{errge@pandora:~/tmp$} : * "$A"
  + : fajl1 fajl2 'szokoz a konyvtar neveben' xxx 'x y'
  %underline{errge@pandora:~/tmp$} set +x
\end{ProgalapVerbatim}

A kimeneten (a kezdő plusz jel után) látható, hogy a _:_ parancsnak
milyen argumentumai lesznek és az aposztrófokra figyelve az is
megállapítható, hogy az első esetben hat argumentuma van, míg a
második esetben csak öt.

\subsection{Speciális karakterek levédése (Quoting)}
A parancssorkifejtés során a következő karaktereknek van speciális
jelentésük: \newline
_$ ` " \ ' # ! { } * ? ~ | & ; ( ) < > space tab newline_

% buta emacs: $

Minden karakter speciális jelentése három féle módon vehető el:
\begin{itemize}
\item bármely (akár nem speciálisé is) egy karakteré a karaktert
  megelőző backslashsel.  Egyszerűen arról van szó, hogyha a shell
  találkozik egy védetlen backslashsel, akkor törli és a következő
  egyetlen egy karakternek viszont semmiképp nem tulajdonít speciális
  jelentést, pl. _\$ \* \\ \'_

\item egész karaktercsoporté a karaktereket körülvevő idézőjellel:
  _"mindenfele: \$*\"&|<\`"_%buta emacs: $

  Az idézőjel nem védi a _$_, %buta emacs: $
  _`_ (backtick), _\_ és _"_ jeleket, minden mást azonban igen.  Az
  említett négy jel úgy érhető el az idézőjeleken belül, ha backslasht
  teszünk eléjük.  Idézőjelen belül a backslash egyébként nem
  viselkedik speciálisan.

  Ha a shellt interaktívan futtatjuk, tehát billentyűzetről gépeltük be
  a parancsot és nem pedig parancsfájlból hajtódik végre, akkor még a
  _!_ sincs védve.  Ez a speciális viselkedés a _set +H_ paranccsal
  interaktív módban is kikapcsolható.  Ha most beírjuk ezt a
  _.profile_ fájlunkba, később kevesebb gondunk lesz.

\item egész karaktercsoporté a karaktereket körülvevő aposztróffal:
  _'mindenfele: $*"&|<`'_ %buta emacs: $

  A védés ilyenkor teljesen mechanikus, a következő aposztrófnál ér
  véget, bármi is volt előtte, tehát aposztrófot ezen belül nem lehet
  használni még backslashsel sem (sűrű hiba!).  Viszont az aposztróf
  leírható mindenféle környezeten kívűl a _\'_ segítségével és így
  tulajdonképpen megjegyezhető az az ökölszabály, hogy aposztrófon
  belül aposztrófot lehet írni a _'\''_ karaktersorozattal, ugyanis az
  első apsztróf (átmenetileg) lezárja a védő környezetet, majd
  backslashsel lerakjuk a kívánt jelet és megnyitjuk újra a védést.
  Érdekesség, hogy erre a módszerre a shelltől való puskázással is
  rájöhetünk:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~$} set -x
  %underline{errge@pandora:~$} : "gergely's apple"
  + : 'gergely'\''s apple'
\end{ProgalapVerbatim}
\end{itemize}

Ha úgy ütünk entert egy parancssor végén, hogy az utolsó kettő védés
közül valamelyik még nyitva van, akkor megjelenik egy kacsacsőr, ami
további inputot vár, ilyenkor ha nem tudjuk kibogozni, hogy mit is
akarunk, _Ctrl-C_-vel visszakaphatjuk a promptot és semmi nem kerül
végrehajtásra.

\subsection{A kifejtés menete (Expansion)}
Miután a shell a parancssort végigolvasta és olvasás közben az előbb
leírt védési szabályok alapján szavakra bontotta azt, az itt leírt
módszerekkel, az alábbi sorrendben tovább kifejti a szavakat.

A kifejtés megakadályozható levédéssel.

\subsubsection{Kapocs kifejtés (Brace expansion)}
Teljesen mechanikus (azaz a speciális karaktereket
figyelmenkívűlhagyó) direktszorzó, ami kü\-lön\-álló szavakat generál és
intervallumokat is kibont.  Minden eredmény elé odateszi a prefixet,
ami a nyitó kapcsoszárójelet megelőzte, mögé pedig a postfixet, ami a
záró kapcsost követte.

\begin{ProgalapVerbatim}[label=\fbox{A kapocs kifejtés, egymásba ágyazni is szabad...}]
  %underline{errge@pandora:~$} set -x
  %underline{errge@pandora:~$} : e%{1,2,3%{1..7%}%}u
  + : e1u e2u e31u e32u e33u e34u e35u e36u e37u
\end{ProgalapVerbatim}

\subsubsection{Tilde kifejtés (Tilde expansion)}\label{tildeexp}
Ha egy szó elején van (védetlen) _~_, akkor azt az első (védetlen)
_/_-ig az ún. tildeprefix követi.  Ha nincs per a szóban, akkor az
egész tilde utáni rész a tildeprefix.  A shell a tilde és a
tildeprefix helyére behelyezi a tildeprefixben megemlített felhasználó
saját könyvtárának abszolút elérési útját, vagy ha a tildeprefix üres,
akkor az aktuális felhasználóét.

A tildeprefix az új szó elején túl még változóértékadáskor is
használható egyenlőségjel vagy kettőspont után, ez később még (pl. a
_PATH_ átállításánál) praktikus lesz.

\subsubsection{Paraméterek, aritmetikai kifejezések kifejtése és parancsok behelyettesítése (Parameter, arithmetic expansion and command substitution)}
\label{paramexp}
Erről később részletesen beszélünk a paramétereknél, változóknál.
Fontos, hogy a _"_ védőkarakter ezt a kifejtést nem akadályozza meg
(és pont ezért szeretjük), valamint az, hogy ez a három kifejtés egy
menetben történik, balról jobbra, a kifejtett paraméter már nem lesz
újból kifejtve.

Arról lehet felismerni a kifejtések ezen csoportját, hogy mindegyiket
a _$_ jel vagy a _`_ (``fordított aposztróf'') %$ buta emacs
jel vezeti be.  A kifejtések ezen csoportjára igaz, hogyha idézőjellel
vannak körülvéve (azaz a _"_ védi őket), akkor bármi is a kifejtés
eredménye, a végeredmény egy szó lesz, azaz az ilyen részekre a
következő pont nem vonatkozik.

\subsubsection{Szavak szétbontása újra (Word splitting)}\label{wordsplit}
Amennyiben az előző pontban említett kifejtések között van olyan, ami
nem volt idézőjelek között, akkor ezeket a shell újra szavakra bontja
a szóközök, tabok és újsorok mentén.

\subsubsection{Elérési utak kifejtése, globbing (Pathname expansion)}\label{pathexp}
Ha a shell valamelyik szóban _*_, _?_ vagy _[_ jelet talál, akkor azt
a szót mintának értelmezi és kicseréli a mintára illeszkedő fájlokra (mint új paraméterekre)
abc sorrendben, vagy nem végez cserét, ha nincs illeszkedő fájlrendszerbejegyzés.

\begin{ProgalapVerbatim}[label=\fbox{Globbing példák}]
  %underline{errge@pandora:~/tmp$} ls -l
  total 0
  drwxr-xr-x 2 errge progterv 6 Sep 18 08:12 A
  -rw-r--r-- 1 errge progterv 0 Sep 18 00:23 fajl1
  drwxr-xr-x 2 errge progterv 6 Sep 18 00:23 szokoz a konyvtar neveben
  -rw-r--r-- 1 errge progterv 0 Sep 18 00:24 xxx
  %underline{errge@pandora:~/tmp$} set -x
  %underline{errge@pandora:~/tmp$} : *[[:digit:]]
  + : fajl1
  %underline{errge@pandora:~/tmp$} : [[:lower:]]*
  + : fajl1 'szokoz a konyvtar neveben' xxx
  %underline{errge@pandora:~/tmp$} : [[:upper:]]*
  + : A
  %underline{errge@pandora:~/tmp$} : *[[:space:]]*
  + : 'szokoz a konyvtar neveben'
  %underline{errge@pandora:~/tmp$} : *[[:space:][:digit:]x]*
  + : fajl1 'szokoz a konyvtar neveben' xxx
\end{ProgalapVerbatim}

A mintában szereplő speciális karakterek a következők lehetnek:
\begin{itemize}
\item _*_: bármilyen karakterláncot, az üreset is helyettesíti
\item _?_: egyetlen karaktert helyettesít, de abból bármilyet
\item _[...]_: A _..._ helyén felsorolt karakterek közül bármelyik
  egyet helyettesíti, pl. _[ahq]_.  Régi könyvek előszeretettel
  említik, hogy megadhatóak intervallumok és ezért a kisbetűk így
  illeszthetők: [a-z].  Azonban ez újabb rendszereken
  megbízhatatlan, ugyanis újabb telepítéseknél az _A_ betűt nem a
  _B_ betű követi a rendezési sorrendben, hanem az _a_ és így a
  _a_-t nem a _b_, hanem a _B_.  Át lehet ezt állítani, de inkább az
  ajánlható, hogy a szögletes zárójelen belül ezeket a megjelölőket
  használjuk egész karaktercsoportok helyére (igen, ekkor összesen
  két szögletes zárójelre is szükség van a kifejezés két szélén):
  \begin{itemize}
    \item _[:alnum:]_: számjegyek és betűk,
    \item _[:alpha:]_: betűk,
    \item _[:upper:]_: nagybetűk,
    \item _[:lower:]_: kisbetűk,
    \item _[:digit:]_: számok,
    \item _[:space:]_: bármilyen szóköz (akár tab vagy újsor is).
  \end{itemize}
\end{itemize}

Egy minta csak akkor illeszkedik a rejtett fájlokra, ha a minta a _._
karakterrel kezdődik.  A nyitó szögletes zárójel után tett _^_ negációt
jelent, az ilyen mintaelem pont a nem felsorolt karakterekre illeszkedik.

\subsection{A parancs végrehajtása}
Miután minden kifejtés megtörtént, a shell rendelkezik szavak egy
hosszú listájával, ezek közül az első a parancs neve, amit a _PATH_
környezetváltozó segítségével megkeres, majd lefuttatja paraméterként
átadva az összes többi listaelemet, minden elem egy különálló
paraméter\footnote{Kivéve azokat a paramétereket, amiket a csatornák
  átirányítgatására használunk.}. Az, hogy melyik paramétert értelmezi
a program opcióként és melyiket argumentumként, teljesen a programon
áll, nem is kell, hogy így csoportosítsa a paramétereket, ez csak egy
szokás, a shell szemantikailag nem értelmezi a kifejtésnél mélyebben
az eredményt.

Ezért van az is, hogy pl. ha a _-bar_ nevű fájlt akarjuk letörölni,
akkor bajban vagyunk:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp$} echo hello world >-bar
  %underline{errge@pandora:~/tmp$} rm -bar
  rm: invalid option -- b
  Try `rm ./-bar' to remove the file `-bar'.
  Try `rm --help' for more information.
  %underline{errge@pandora:~/tmp$} rm -- -bar
  %underline{errge@pandora:~/tmp$} ls -- -bar
  ls: -bar: No such file or directory
\end{ProgalapVerbatim}

Az _rm -bar_ nem működhet, hiszen az rm ezt úgy érti, hogy biztosan
meg akartuk neki adni a következő opciókat: _b_, _a_, _r_.  _b_
opciója nincs is, ezért hibát üzen és segít, azt mondja, hogy
használjuk az _rm ./-bar_ parancsot, ami valóban működne, azonban ha a
_-_ karakterrel kezdődő fájl elérési út kiterjesztésének az eredménye
(mert kiadtuk az _rm *_ parancsot ebben a könyvtárban), akkor nem
tudjuk hogyan elé tenni minden ilyen fájlnévnek a _./_-t.

Ezért egy általánosabb megoldás és minden fontosabb paranccsal
működik, hogy a fájlnév paraméterek elé, az utolsó opció után
teszünk egy külön paraméterként egy _--_-t, ami azt jelzi a
parancsnak, hogy köszönjük szépen, több opció már nem, csak
argumentumok jönnek.

\section{Paraméterek, környezetváltozók}
Minden paraméter egy értéket tárol.  Paraméter lehet egy szám, egy
név\footnote{Név alatt az olyan szavakat értjük, amik csak
  alfanumerikus karaktereket és aláhúzást tartalmaznak, valamint nem
  kezdődnek számmal.}, illetve egyes speciális karakterek
is.  Változónak hívunk minden olyan paramétert, amit név
azonosít, szokás, hogy a változóneveink csupa nagybetűkből és
esetleges aláhúzásokból állnak, de ez nem kötelező.

Amennyiben egy paraméter szám, akkor az egy pozícionális paraméter,
ezeknek parancsfájlok esetén van jelentősége.  A shell bennük tárolja
el a parancsfájl hívásakor megadott paramétereket, nulladikként a
parancsfájl hívási nevét.  Előrevetítve, hogy paraméter értékét a
parancsainkban a _$paraméter_ %buta emacs $
formával fogjuk kifejteni, álljon itt egy példa:

\begin{ProgalapVerbatim}
  %underline{errge@home:~$} cat test.sh
  #!/bin/bash

  echo fajlnev: $0, elso paramter: $1, 10. paramter: $%{10%}
  %underline{errge@home:~$} bash test.sh egy ketto harom negy ot hat het nyolc kilenc tiz tizenegy
  fajlnev: test.sh, elso paramter: egy, 10. paramter: tiz
\end{ProgalapVerbatim}

A változók értéket (nevükhöz híven) meg is változtathatjuk a
_változónév=újérték_ parancs kiadásával\footnote{Megszüntetni változót
  a \texttt{unset változónév} paranccsal lehet, a \texttt{változónév=}
  parancs ugyanis csak üresre állítja a változóhoz rendelt értéket,
  nem szünteti meg a változót magát.}, az egyéb paraméterek értékét
nem változtathatjuk meg.  Az értékadás megtörténte előtt
végrehajtódnak az _újérték_-en a \ref{tildeexp} és \ref{paramexp}
pontokban leírt kifejtések.  Vannak olyan környezetváltozók, amik nem
csak saját magunk számára értelmesek, hanem a rendszer saját maga is
használja, ilyenre már láttunk példát, a _LANGUAGE_ esetében, ezzel
állíthattuk be az általunk kívánatos nyelvet.
Továbbiak\footnote{Mégtöbb információ fellelhető a \texttt{man 1 bash}
paranccsal előhozott segítőlap ``Shell Variables'' szekciójában.}:
\begin{itemize}
\item _PATH_: programok keresési útvonala, a könyvtárnevek
	  kettősponttal elválasztva, a Pandorán, ha van _~/bin_ könyvtárunk, akkor az automatikusan a _PATH_ elejére kerül\footnote{Az én \texttt{PATH}
    környezetváltozóm a Pandorán: \newline
    \texttt{/h/e/errge/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games}},
  \item _USER_: a felhasználó belépési neve,
  \item _HOME_: a felhasználó munkakönyvátárának elérési útja,
  \item _PS1_: a prompt formája (ld. ``PROMPTING'' fejezet a bash manlapjában).
\end{itemize}

Természetesen a _USER_ és _HOME_ környezetváltozók birizgálása nem
jelent biztonsági kockázatot senkire, attól, hogy oda más valakit vagy
más valaki munkakönyvtárát írjuk, még nem fog a rendszer minket más
jogosultságokkal kezelni, csupán a mi életünk lesz kényelmetlenebb,
mert pl. a tilde kifejtés rosszul fog működni.

Paraméter lehet még a _#_ is, ami a pozícionális paraméterek közül a
létező legnagyobb számértékét adja meg, a _@_ és a _*_, amik
mindketten az összes pozícionális paraméter értékét jelentik.
Különbség az utóbbi kettő között akkor van, ha valamely kifejtendő
szövegben a _$@_, illetve a _$*_ idézőjelek közt van, előbbit ugyanis
ekkor a _"$1" "$2" "$3"..._{\SaveVerb{x}|$@|\footnote{És ez különösen jól használható
  néha, pl. egy egyszerű tömörítőszkript így nézhetne ki: \texttt{tar -c -z -f \UseVerb{x}}}}, utóbbit
  pedig a _"$1 $2 $3..."_ értékké fejti ki a shell.  A _?_ paraméter a
  legutóbbi parancs visszatérési értékét, a _$_ %$ buta emacs
  paraméter az éppen futó parancsértelmező processz-azonosítóját
  tartalmazza, habár ezekről még nem tanultunk, próbáljuk meg kiiratni
  őket!

\section{Bonyolult kifejtések}
Ideje visszatérni arra, amit az előző fejezetben későbbre
halasztottunk, nevezetesen a paraméterek, a változók, az aritmetikai
kifejezések és a parancsok behelyettesítésének tárgyalására.  Ez a
téma szinte kimeríthetetlen, a jegyzet nem referencia, sokkal
részletesebb és mélyebb leírás található a _bash_ manoldalában!

\subsection{Paraméter behelyettesítése}
Amennyiben egy paraméter értékét szeretnénk a neve alapján
parancssorunkba illeszteni, akkor azt megtehetjük a
_${paraméternév}_ %$ buta emacs
kifejezéssel, amiből a kapcsoszárójelek elhagyhatóak, ha a zárót nem
követi olyan karakter, ami a paraméter részét is
képezhetné\footnote{Vajon miért működik az \texttt{echo \$0alma}
  parancs, de nem az \texttt{echo \$PATHalma}?}.

\subsection{Parancsok behelyettesítése}
A _$(parancs)_ (új forma) vagy _`parancs`_ (régi forma) %$ buta emacs
hatására kifejtés közben a shell végrehajtja a parancsot, majd a
kimenetét beilleszti a kifejezés helyére.  Emlékeztetünk rá, hogy
mivel ezután a kifejtés után jön még a \ref{wordsplit} és a
\ref{pathexp}, ezért a behelyettesített kimenet még további
kifejtéseknek eshet áldozatul, ha nincs idézőjellel védve.

Ezt a lehetőséget nagyon sokszor alkalmazzuk az _expr_ paranccsal,
aminek segítségével egyszerű aritmetikai kifejezések értékelhetőek ki.
Pl. az _expr 4 + 5 - 4 / 2_ parancs eredménye _7_.  Mitől függ az
_expr 7 * 5_ parancs eredménye?  Fontos, hogy az _expr_ parancsnak az
argumentumokat és operátorokat külön-külön paraméterként kell megadni, ugyanis ebbe a
programba csak az alapműveletek megvalósítását építették bele, a
parancssor darabokra szedését egyszerűen a shellre bízzák.  Nem
működik ezért pl. az _expr 7-5+6_ parancs.

\begin{ProgalapVerbatim}[label=\fbox{Mindenféle kifejtések}]
  %underline{errge@pandora:~/tmp$} ls
  A  fajl1  fajl2  output  szokoz a konyvtar neveben  xxx
  %underline{errge@pandora:~/tmp$} set -x
  %underline{errge@pandora:~/tmp$} : *
  + : A fajl1 fajl2 output 'szokoz a konyvtar neveben' xxx
  %underline{errge@pandora:~/tmp$} A=*
  + A='*'
  %underline{errge@pandora:~/tmp$} : $A
  + : A fajl1 fajl2 output 'szokoz a konyvtar neveben' xxx
  %underline{errge@pandora:~/tmp$} A="ket szo"
  + A='ket szo'
  %underline{errge@pandora:~/tmp$} : $A
  + : ket szo
  %underline{errge@pandora:~/tmp$} : "$A"
  + : 'ket szo'
  %underline{errge@pandora:~/tmp$} : 7 * 5
  + : 7 A fajl1 fajl2 output 'szokoz a konyvtar neveben' xxx 5
  %underline{errge@pandora:~/tmp$} : $((7*5))
  + : 35
  %underline{errge@pandora:~/tmp$} A=2
  + A=2
  %underline{errge@pandora:~/tmp$} A=$(expr $A + 1)
  ++ expr 2 + 1
  + A=3
  %underline{errge@pandora:~/tmp$} A=$(($A+1))
  + A=4
  %underline{errge@pandora:~/tmp$} : $(ls -w$A)
  ++ ls -w4
  + : A fajl1 fajl2 output szokoz a konyvtar neveben xxx
  %underline{errge@pandora:~/tmp$} : "$(ls -w$A)"
  ++ ls -w4
  + : 'A
  fajl1
  fajl2
  output
  szokoz a konyvtar neveben
  xxx'
  %underline{errge@pandora:~/tmp$} : $((2**16))
  + : 65536
  %underline{errge@pandora:~/tmp$} set +x
  %underline{errge@pandora:~/tmp$} cat test.sh
  #!/bin/bash

  set -x

  : $* $@ "$*" "$@"
  %underline{errge@pandora:~/tmp$} bash test.sh egy\ \ param ket ha
  + : egy param ket ha egy param ket ha 'egy  param ket ha' 'egy  param' ket ha
\end{ProgalapVerbatim}

\subsection{Aritmetikai kifejezések}
Az _expr_ megjelenése után, felismerve, hogy azt mennyien használják,
annak funkcióit a shellbe is beépítették, a
_$((kifejezés))_ %$ buta emacs
forma kifejtéskor a kifejezés aritmetikai értékére cserélődik.  Erről
bővebben a shell dokumentációjának ``ARITHMETIC EVALUATION'' fejezete szól.

\subsection{Feladatok}
\begin{itemize}
\item Írj olyan parancsfájlt feladat1 néven, ami az első
  paramétereként kapott fájlt felülírja ugyanezen fájl azon soraival,
  melyekben szerepel az alma szó.  Feltesszük, hogy a parancsfájlnak
  van paramétere, az tényleg közönséges fájl, továbbá a parancsfájlt
  olyan könyvtárban futtatjuk, amelyikre van írási jog.
\item Írj parancsfájlt feladat2 néven, aminek az első paramétere egy
  fájlnév.  Ennek a fájlnak midnen sorában egy létező könyvtár neve
  van.  A könyvtárnevekben nincs szóköz.  A parancsfájl adjon ezekről
  a könyvtárakról fájllistát (a benne lévő fájlok neveit írja
  ki).
\item Írj parancsfájlt feladat3 néven, ami megadja, hogy az aktuális
  könyvtárban hány közönséges fájl van.

\end{itemize}

\section{Jogosultságok}
A Unix három jogosultságfajtát ismer:
\begin{itemize}
\item _r_ (4), olvasási: a fájl olvashatóságát, illetve egy könyvtár
  tartalmának (egy mélységű) listázhatóságát szabja meg,
\item _w_ (2), írási: a fájl módosíthatóságát, illetve a könyvtárban
  közvetlenül előforduló fájlok létrehozhatóságát, törölhetőségét,
  átnevezhetőségét szabja meg,
\item _x_ (1), futtatási: a fájl futtathatóságát, illetve a könyvtár
  alatti fájlrendszerrész elérhetőségét szabja meg.
\end{itemize}

Minden fájl esetében a felhasználók 3 diszjunkt halmazra oszlanak és
erre a három halmazra külön-külön állítható, hogy a fenti
jogosultságokból melyikkel rendelkeznek, ez a három rész:
\begin{itemize}
\item tulajdonos: a fájl tulajdonosa, egy konkrét felhasználó,
\item csoport: a fájlhoz rendelt csoportba tartozó felhasználók,
  kivéve a tulajdonost,
\item mindenki más.
\end{itemize}

Egy fájl tulajdonosa, illetve a fájlhoz rendelt csoport az _ls -l_
hosszú kimenetében található, a 3. és a 4. oszlopban.  A tulajdonos
megváltoztatására csak a rendszergazdának van joga (a _chown_
paranccsal), a saját fájlainkról nem mondhatunk le\footnote{Hiszen
  azzal összezavarnánk a \texttt{quota} parancsot!}.  A fájlhoz
rendelt csoportot átállíthatjuk a _chgrp újcsoportnév fájlnév_
paranccsal, amennyiben az adott csoportnak tagjai vagyunk.  Az, hogy
milyen csoportoknak vagyunk tagjai, az _id_ vagy a _groups_ paranccsal
tekinthető meg.

Az _ls -l_ listájában a fentiek alapján már lehet értelmezni a 9
karaktert, ami a jogosultságokat jelzi, 3 csoportra kell osztani őket,
az első 3 karakter a tulajdonosra vonatkozó jogok, a második három a
fájlhoz rendelt csoportba tartozó felhasználókra vonatkozó jogok, a
harmadik részben leírtak vonatkoznak mindenki másra.  Mindhárom
részben azok a jogok érvényesek, amik helyén a neki megfelelő betű
(_r_, _w_ vagy _x_) áll és azok nincsenek engedélyezve, amik helyén
kötőjel áll.

Egy fájlra vonatkozó jogokat a _chmod jogok fájlnév..._ paranccsal
lehet változtatni, ahol a jogokat többféleképpen is megadhatjuk.  Az
első lehetőség, hogy a teljes új jogosultságrendszert kiszámoljuk,
méghozzá négy számjegy formájában, amiből az utolsó három számít, az
első mindig 0\footnote{Aki nem hiszi, járjon utána, ezzel sokat
  tanulhat, de nekünk ez így elég...}, pl. a _0517_ az _r-x--xrwx_
jogokat jelenti.  A második lehetőség, hogy a jogoknál csak a
jelenlegi jogrendszerhez képest kívánt változást írjuk le,
_oszlop+-változás_ formában, ahol az _oszlop_ helyén három karakter
(_u_(ser), _g_(roup) vagy _o_(ther)) valamilyen kombinációja állhat,
annak megfelelően, hogy melyik oszlopo(ka)t szeretnénk változtatni, a
_+-_ helyén a _+_ vagy _-_ állhat, attól függően, hogy jogosultságot
adni vagy elvenni szeretnénk, legvégül a _változás_ helyén az _r_, _w_
és _x_ betűk valamilyen kombinációja állhat, aszerint, hogy milyen
jogosultságot adunk vagy veszünk el.

Próbálgassuk ki ezeket a jogosultságokat, különösképp legyünk
figyelemmel a könyvtárakra vonatkozó jogosultságokra, nézzük meg, hogy
mi történik, ha egy könyvtárunkra nincs _x_ jog, _r_ jog, illetve _w_
jog!

A jogosultságok köréből a leggyakrabban használt parancs a %buta latex
_chmod a+x parancs.sh_, amivel a _parancs.sh_ nevű scriptfájlunk (azaz
shell parancsokat tartalmazó programunk) futtathatóvá válik és így a
későbbiekben csupán a nevével, mint paranccsal elindíthatjuk, nem kell
eléírni, hogy _bash_.  Természetesen, ha nincs a _PATH_-ben, akkor a
relatív vagy az abszolút elérési útjával együtt kell megadnunk.

\section{Parancskonstrukciók}
\subsection{Egyszerű parancsok}
A bash shellben\footnote{Apropó: a jegyzetben mindenhol a Bourne-Again
  SHell-el foglalkozunk.  Többféle parancsértelmező létezik, ha valami
  nem működik, ellenőrízzük, hogy a rendszerünkön a bash fut, pl. úgy,
  hogy kiiratjuk a \texttt{BASH\_VERSION} változót, vagy a \texttt{0}
  paramétert.} már tudunk egyszerű parancsokat írni, amiknek a formája
a következő:\newline
_<változó értékadások> <parancs> <opciók és argumentumok> <átirányítások>_
\newline
Például: \texttt{LANG=hu\_HU LC\_CTYPE=hu\_HU man ls}

A változók csak a futatott parancs környezetében változnak meg, a
shell későbbi parancsai már a változók régi értékeivel kerülnek
feldolgozásra:

\begin{ProgalapVerbatim}
  %underline{errge@home:~$} B=almafa bash
  %underline{errge@home:~$} echo $B
  almafa
  %underline{errge@home:~$} exit
  %underline{errge@home:~$} echo $%{B?%}
  bash: B: parameter null or not set
\end{ProgalapVerbatim}

Vegyük észre, hogy milyen hasznos apróságot tartalmaz a példa: ha egy
változó kifejtését a kapcsoszárójelen belül a kérdőjel követ, akkor
amennyiben a változó igazából nem is létezik, akkor a shell hibát ad,
ahelyett, hogy egyszerűen az üres sztringgel helyettesítené.
Ráadásul, ha ez egy szkript közepén fordul elő, akkor az rögtön leáll
és a hiba nem gyűrűzik tovább.  Parancsfájlainkban szinte minden
változóbehelyettesítést lecserélhetünk ilyenre és cserébe sokkal
könnyebben és gyorsabban fény fog derülni az elkövetett hibákra.

A futtatott parancs környezete nem örökli a shellünk indulásakor nem
definiált, azóta saját magunk által (értékadással) létrehozott
változókat.  Ha mégis ezt szeretnénk, akkor az _export_ parancsot kell
használnunk.  A futtatott programok, shellek által végzett változó
beállítások sosem befolyásolják a szülő processz környezetét.

\begin{ProgalapVerbatim}
  %underline{errge@home:~$} echo $%{A?%}
  bash: A: parameter null or not set
  %underline{errge@home:~$} A=korte
  %underline{errge@home:~$} echo $%{A?%}
  korte
  %underline{errge@home:~$} bash
  %underline{errge@home:~$} echo $%{A?%}
  bash: A: parameter null or not set
  %underline{errge@home:~$} C=szilva
  %underline{errge@home:~$} echo $%{C?%}
  szilva
  %underline{errge@home:~$} exit
  %underline{errge@home:~$} echo $%{C?%}
  bash: C: parameter null or not set
  %underline{errge@home:~$} echo $%{A?%}
  korte
  %underline{errge@home:~$} export A
  %underline{errge@home:~$} bash
  %underline{errge@home:~$} echo $%{C?%}
  bash: C: parameter null or not set
  %underline{errge@home:~$} echo $%{A?%}
  korte
\end{ProgalapVerbatim}

Nem volt szó még részletesen arról, hogy minden parancs visszaad egy
értéket, ami egy egész szám (0 és 255 között).  Amennyiben
igazságértéket akarunk ezekhez rendelni, azt úgy szokás, hogy a 0
jelenti az igazat és minden más a hamisat.  Ha kiváncsiak vagyunk a
legutoljára lefutott parancs visszatérési értékre, akkor a _?_
paramétert (azaz pl. az _echo $?_ parancsot) %buta emacs $
használhatjuk.  Mi a saját parancsfájlainkból az _exit_ parancsnak
adott argumentummal jelezhetjük, hogy mi legyen a visszatérési érték,
míg számos parancs tömören megfogalmazza mondanivalóját ebben a
számban.  Pl. a _grep_ csak akkor ad vissza igazat (azaz 0-t), ha
talált legalább egy illeszkedő sort, míg a _cmp_, ami fájlok
összehasonlítására használható, hamissal jelzi, ha a két
összehasonlítandó fájlban különbség mutatkozott.

\begin{ProgalapVerbatim}[label=\fbox{Visszatérési értékek}]
  %underline{errge@pandora:~/tmp$} cat test.sh
  #!/bin/bash

  exit $((4**2))
  %underline{errge@pandora:~/tmp$} chmod a+x test.sh
  %underline{errge@pandora:~/tmp$} ./test.sh
  %underline{errge@pandora:~/tmp$} echo $?
  16
  %underline{errge@pandora:~/tmp$} echo $?
  0
  %underline{errge@pandora:~/tmp$} fgrep zsh test.sh ; echo $?
  1
  %underline{errge@pandora:~/tmp$} fgrep bash test.sh ; echo $?
  #!/bin/bash
  0
  %underline{errge@pandora:~/tmp$} fgrep bash test.sh >/dev/null ; echo $?
  0
\end{ProgalapVerbatim}

\subsection{Pipeline-ok}
Az egyszerű parancsok csővezetékekké szervezhetőek a _|_ jellel, az
egész struktúra visszatérési értéke az utolsó, jobboldali parancs
visszatérési értéke lesz.  Ha a pipeline első parancsa előtt szerepel
a _!_ karakter (szóközzel elválasztva a parancstól), akkor a
visszatérési érték negálódik, tehát 0 lesz, ha nem 0 volt és nem lesz
0, ha 0 volt.  A csővezetékek szervezésének módjára részletesen
kitértünk az \ref{pipeline} fejezetben.

Pipeline-ok készítése közben hasznos, ha ismerjük a _tee_ parancsot,
nyissuk fel a man oldalát és barátkozzunk meg vele most!

\subsection{Listák}
A csővezetékeket eddig mindig új sorba írtuk, azonban a pontosvessző
használatával listába is fűzhetjük őket, ekkor a végrehajtás
szekvenciális.  A pontosvessző helyett az _&&_ és a _||_ operátor is
használható, az első hatására a hátsó parancs csak akkor fut, ha az
elülső igazzal tért vissza, míg a második esetében pont fordítva, a
hátsó parancs csak akkor fut, ha az elülső meghiúsult.  A visszatérési
értéke az összetételnek pedig _&&_ esetén csak akkor 0, ha mindkét
rész 0-val tért vissza, _||_ esetén 0, bármelyik rész 0-val tért
vissza.

Mindennek a bemutatásához felhasználom a _true_ és _false_ beépített
parancsokat, amik nem csinálnak semmit, csupán 0-át, illetve 1-et
adnak vissza:
\begin{ProgalapVerbatim}
  %underline{errge@home:~$} true && echo $?
  0
  %underline{errge@home:~$} false && echo $?
  %underline{errge@home:~$} true || echo $?
  %underline{errge@home:~$} false || echo $?
  1
\end{ProgalapVerbatim}

Mindenképp tanulmányozzuk a nagyon hasznos _test_ parancs
dokumentációját, rengeteg kapcsolójával szinte mindenféle aritmetikai,
sztring egyenlőségi és fájlrendszerbeli vizsgálat elvégezhető.  Az
eredményt mindig a visszatérési értékben jelzi, így programok írásakor
jól használható.

A csővezetéket építő _|_ jel mindig erősebben köt, mint a _;_, _&&_
vagy _||_ bármelyikéé, amik azonos precedenciával bírnak.  Ez elég
meglepő, más programozási nyelvekben ez nem szokás:

\begin{ProgalapVerbatim}
  %underline{errge@home:~$} true || %{ false && false ;%}; echo $?
  0
  %underline{errge@home:~$} true || false && false; echo $?
  1
\end{ProgalapVerbatim}

\subsection{Összetett parancsok}
Az összetett parancsok listákból épülnek fel és a program logikai
vezérlését, a szokásostól eltérő végrehajtási sorrend kijelölését
teszik lehetővé.  Természetesen a _bash_ sokkal többféle
parancsösszetételt ismer, mint amit mi megtanulunk.

\subsubsection{\texttt{( lista )} és \texttt{\{ lista ; \}}}
Listák csoportosítására szolgáló parancsok, így bírálhatjuk felül a
számunkra nem kedvező zárójelezési sorrendet.  A kapcsoszárójeles
formula belsejét mindig újsorral (vagy pontosvesszővel) kell lezárni
és a nyitó- és zárójelei körül szóközöket kell hagyni, ez a funkció
tényleg csak a végrehajtási sorrend megadására való.  A kerezárójeles
kifejezés viszont egy új parancsértelmezőben hajtja végre a listában
felsorolt parancsokat, majd kilép és a végrehajtás a zárójel után
folytatódik.  Erre akkor lehet szükség, ha nem szeretnénk, hogy a
zárójelezett rész környezetének változásai (pl. munkakönyvtár váltás,
változók módosítása) a lefutásuk után érvényben maradjanak.  Mindkét
parancsösszetétel visszatérési értéke az általuk végrehajtott lista
visszatérési értéke.

Vegyük észre, hogy az eddig megtanultakkal már lényegében tudunk
elágazásokat írni, pl. parancsok helyes meghívását tömören ellenőrízni.

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp$} cat test.sh
  #!/bin/bash

  [ "$#" -eq 2 ] || %{ echo "Ezt a parancsot két paraméterrel kell hívni!"; exit 10; %}
  echo "Helyes használat, első paraméter: $1, második paraméter: $2"
  %underline{errge@pandora:~/tmp$} ./test.sh ; echo $?
  Ezt a parancsot két paraméterrel kell hívni!
  10
  %underline{errge@pandora:~/tmp$} ./test.sh a; echo $?
  Ezt a parancsot két paraméterrel kell hívni!
  10
  %underline{errge@pandora:~/tmp$} ./test.sh a b; echo $?
  Helyes használat, első paraméter: a, második paraméter: b
  0
  %underline{errge@pandora:~/tmp$} ./test.sh a b c; echo $?
  Ezt a parancsot két paraméterrel kell hívni!
  10
  %underline{errge@pandora:~/tmp$} ./test.sh a "b c"; echo $?
  Helyes használat, első paraméter: a, második paraméter: b c
  0
  %underline{errge@pandora:~/tmp$} vi test.sh
  %underline{errge@pandora:~/tmp$} cat test.sh
  #!/bin/bash

  [ "$#" -eq 2 ] || %{
    echo "Ezt a parancsot két paraméterrel kell hívni!"
    exit 10
  %}
  echo "Helyes használat, első paraméter: $1, második paraméter: $2"
\end{ProgalapVerbatim}
%$buta emacs

A példában használt _[ kifejezés ]_ pontosan megfelel annak a
működésnek, mint amit a _test kifejezés_ jelent.  Ez nem speciális
shell utasítás, létezik a _/usr/bin/[_ fájl is.

\subsubsection{\texttt{(( kifejezés ))} és \texttt{[[ kifejezés ]]}}

Ezzel a két parancskonstrukcióval nem foglalkozunk, bizonyos
aritmetikai, illetve egyéb kifejezésekre vonatkozó vizsgálatok
írhatóak fel velük külső parancsok (mint amilyen a _test_ vagy az
_expr_) használata nélkül.  Vigyázzunk velük, mert szintaktikájuk
habár hasonló, mint az említett parancsoké, vannak azért különbségek,
inkább kerüljük őket, ha nincs kedvünk alaposan elolvasni a _bash_ man
oldalának ide vonatkozó részeit.

\subsubsection{\texttt{for ... in ...; do lista ; done}}
A _for_ programkonstrukcióval ciklusokat szervezhetünk, az _in_ után
szereplő listát a shell először kifejti, majd végrehajtja a
ciklusmagot annyiszor, ahány eleme van a kifejtésnek, minden egyes
alkalommal a _for_ után lévő névbe, mint környezetváltozóba téve az
aktuális értéket.  Amennyiben a kifejtés eredménye üres, akkor egy
parancsot sem hajt végre és a visszatérési értéke a _for_-nak 0,
egyébként a visszatérési érték az utoljára végrehajtott parancsé.

Amennyiben az _in_ részt elhagyjuk, akkor a pozícionális paramétereken
megy végig a shell.

A \texttt{seq} nagyon hasznos _for_ ciklusokhoz szervezéséhez, ugyanis
ez intervallumokat tud kiírni, így parancsbehelyettesítéssel
kombinálva számlálóciklusokat írhatunk.

\begin{ProgalapVerbatim}[label=\fbox{A \texttt{for} ciklus}]
  %underline{errge@pandora:~/tmp$} cat test.sh
  #!/bin/bash

  for param ; do echo "paraméter: $param"; done

  for x in 1 2 5
  do
    echo -n "x: $x "
  done
  echo

  for x in $(seq 1 100)
  do
    echo $(($x**2))
  done
  %underline{errge@pandora:~/tmp$} ./test.sh p1 p2 | head
  paraméter: p1
  paraméter: p2
  x: 1 x: 2 x: 5
  1
  4
  9
  16
  25
  36
  49
\end{ProgalapVerbatim}

\subsubsection{\texttt{case ... in ...|... ) lista  ;; ... esac}}
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp$} cat test.sh
  #!/bin/bash

  i=1
  for param
  do
    echo -n "$i. paraméter: "
    case $param in
      [[:lower:]]*)
        echo -n "kisbetuvel kezdodik, "
        ( exit 1 ) ;;
      [[:upper:]]*)
        echo -n "nagybetuvel kezdodik, "
        ( exit 2 ) ;;
      [0-9]*)
        echo -n "szammal kezdodik, "
        ( exit 3 ) ;;
    esac
    echo "retval: $?"
    i=$(($i+1))
  done
  %underline{errge@pandora:~/tmp$} ./test.sh Bdsfg 4sdf aeetr ?234
  1. paraméter: nagybetuvel kezdodik, retval: 2
  2. paraméter: szammal kezdodik, retval: 3
  3. paraméter: kisbetuvel kezdodik, retval: 1
  4. paraméter: retval: 0
\end{ProgalapVerbatim}

A _case_ paranccsal sok ágú elágazásokat szervezhetünk.  A _case_ után
kell megmondani, hogy milyen szót próbáljon meg illeszteni a shell,
majd az _in_ utáni részek többszöri ismétlésével megadható, hogy
milyen mintára való sikeres illesztés esetén milyen utasításlista
hajtódjon végre.  Mind a minták, mind a szó kifejtésre kerül, de csak
a \ref{tildeexp} és a \ref{paramexp} szerint.  A mintákban szereplő
speciális védtelen karaktereket úgy viselkednek a szóra való illesztés
közben, mint azt a \ref{pathexp}-ban láttuk.  Az első illeszthető ág
kerül futtatásra és a visszatérési érték az ottani utolsó parancs
visszatérési értéke.  Ha egy ág sem illeszthető, akkor a visszatérési
érték 0.

Gondoljuk meg, hogy miért volt szükség a mintában az _exit_ utasítások
zárójelbe tételére?  Megfelelő lett volna zárójelezésre a _{ ...; }_
formula?

\subsubsection{\texttt{if lista; then lista; elif lista; then lista; ... else lista; fi}}
Ennek a szerkezetnek az _elif_ ágai elhagyhatóak, illetve bármennyi
lehet belőlük és az _else_ ág sem kötelező.  Amikor az első _if_ vagy
_elif_ utáni lista igaz értékkel tér vissza, akkor a hozzá tartozó
_then_-ben foglalt parancsok végrehajtásra kerülnek, majd az egész
_if_ szerkezet visszatérési értéke felveszi az utoljára végrehajtott
parancsét.  Ha egyik ág feltétel listája sem tér vissza igazzal a
kiértékelések során, akkor az _else_ ághoz tartozó lista kerül
végrehajtásra, a visszatérési értékre ugyanaz a szabály vonatkozik
ekkor is.

\begin{ProgalapVerbatim}
  #!/bin/bash

  if test $(($1%%3)) -eq 0
  then
    echo A $1 oszthato 3-mal.
  else
    echo A $1 nem oszthato 3-mal.
  fi
\end{ProgalapVerbatim}

\subsubsection{\texttt{while lista; do lista; done}}
A _while_ ciklus esetében a _for_-ral ellentétben nem előre
meghatározzuk a ciklus által bejárandó elemek sorozatát, hanem egy
parancsot írunk, amit minden ciklusmag futtatása előtt kiértékel a
shell a végrehajtást csak akkor folytatja, ha a kiértékelés eredménye
igaz.

Mivel a _for_ ciklus _in_ opciójának elhagyása nem széleskörben ismert
lehetőség, ezért az összes paraméter bejárását egészen máshogy,
_while_ ciklussal szokták megoldani, méghozzá _shift_ utasítással, ami
minden (1-nél nagyobb) pozícionális paramétert előrébb csúsztat eggyel
és így természetesen az 1 paraméter értéke elveszik.

Vigyázzunk, mert a _while_ és _shift_ ilyen módon való használata
lerombolja a paraméterlistánkat, csak egyszer lehet elsütni
shellkörnyezetenként.  Szerencsére új shellkörnyezetet egyszerűen
indíthatunk a már tanult zárójelezéssel.  Arra is figyeljünk, hogy ne
a %buta latex
_[ "$1" != ""]_ %$ buta emacs
ciklusfeltételt használjuk, hiszen lehet valamelyik paraméter az üres
sztring.
\begin{ProgalapVerbatim}
  #!/bin/bash

  p=0
  # rossz: while [ "$1" != "" ]
  # jo, csak bonyolult, magyarazzuk meg: while ( : $%{1?%} ) 2>/dev/null
  ( while [ $# -ne 0 ]
  do
    p=$(($p+1))
    shift
  done
  echo Parameterek szama: $p )
  echo Elso parameter: $1
\end{ProgalapVerbatim}
%$buta emacs

\subsubsection{Gyakorlás}
\begin{itemize}
\item Írjuk ki az első paramétert függőlegesen, illetve megfordítva!
  \begin{ProgalapVerbatim}
  #!/bin/bash

  H=$(echo -n "$1" | wc -c)
  for i in $(seq $H)
  do
    echo "$1" | cut -c $i
  done

  for i in $(seq $H -1 1)
  do
    echo -n "$(echo "$1" | cut -c $i)"
  done
  echo
  \end{ProgalapVerbatim}
%buta emacs: $
\item Az aktuális könyvtárban lévő fájlokat rendezzük soraik száma
  szerint!
  \begin{ProgalapVerbatim}
  #!/bin/bash

  for i in *
  do
    if test -f "$i"
    then
      wc -l "$i"
    fi
  done | sort -n | cut -d" " -f2-
  \end{ProgalapVerbatim}
%$ buta emacs
Feltéve legalább két fájl létezését, így is meg lehet oldani a feladatot:\\_wc -l * 2>/dev/null | head -n-1 | sort -n | sed 's/^ *//' | cut -d\  -f2-_

\SaveVerb{x},grep -dskip -Hsc '.*' * | sed 's/^\(.*\):\(.*\)$/\2 \1/' | sort -n | cut -d\  -f2-, %$buta emacs

A _wc_ helyett a _grep_-et használva nem kell feltenni legalább két fájl létezését.\footnote{\UseVerb{x}}

Figyeljük meg, hogy mindegyik esetben a fájlneveket a két oszlopos,
szóközzel elválasztott listából nem a _cut -d" " -f2_, hanem %l
a _cut -d" " -f2-_ utasítással nyertük ki, ugyanis a fájlnevekben
előfordulhatnak szóközök, amik az első parancs esetében már a harmadik
oszlop elejét jelentik, tehát nekünk az összes oszlopra szükségünk van
a másodiktól kezdődően, nem csupán a másodikra.

\item Mondjuk meg az aktuális könyvtárban lévő közönséges fájlok
  méretének összegét!
  \begin{ProgalapVerbatim}
  #!/bin/bash

  sum=0
  for i in *
  do
    if test -f "$i"
    then
      sum=$(($sum+$(ls -l "$i" | tr -s ' ' | cut -d' ' -f5)))
    fi
  done

  echo Osszeg: $sum
  \end{ProgalapVerbatim}
\end{itemize}

Ebben a példában azt kell megfigyelni, hogy a parancsbehelyettesítés
hamarabb végrehajtódik, mint az aritmetika kiértékelése, valamint az
aritmetika kiértékelése közben a változóbehelyettesítések megtörténnek
és így az összeadás valóban helyes.

\section{Szövegek átalakítása és illesztése}

A reguláris kifejezések olyan karaktersorozatként megadott minták,
amik sztringek egy egész halmazát írják le önmaguk.  Pl. az a
reguláris kifejezés, hogy _.*a_ egy végtelen halmazát írja le a
karakterláncoknak, ugyanis erre a mintára az összes olyan
karaktersorozat illeszkedik, ami az _a_ betűre végződik.  Ne keverjük
össze ezeket a kifejezéseket a shell által a _case_ utasításnál,
illetve fájlnevek illesztésekor használt globbinggal(\ref{pathexp})!

A reguláris kifejezések használatát újabban közvetlenül a _bash_ is
támogatja, azonban a shell ezen része még most is kialakulóban van,
évről--évre változik, hogy mi mit jelent, így ennek a tárgyalásával
nem foglalkozunk, hanem a régóta létező és kiforrott _grep_, _sed_ és
_find_ parancsok reguláris kifejezéseit nézzük majd meg.

Megjegyezzük, hogy ezen kifejezések értelmezése, az illeszkedő halmaz
meghatározása, illetve egy konkrét sztringről a halmazba tartozás
eldöntése nagyon komoly információtechnológiai problémák, a témakör a
formális nyelvek és automaták nevet viseli.  Ezek az ismeretek
szükségesek fordítóprogramok, illetve olyan (egyszerűnek tűnő)
interpreterek készítéséhez is, mint amilyen a _bash_ shellünk.

\subsection{\texttt{tr}}
A reguláris kifejezések tárgyalása előtt egy egyszerű, de mégis nagyon
sok feladat elvégzését lehetővé tévő programra, a _tr_-re hívjuk fel a
figyelmet pár példán keresztül.  Az olvasó a _man 1 tr_ paranccsal
maga elolvashatja a részletes tudnivalókat erről a programról.

\begin{ProgalapVerbatim}[label=\fbox{\texttt{tr}}]
  %underline{errge@pandora:~$} echo "a  b c          d" | tr -d " "
  abcd
  %underline{errge@pandora:~$} echo "a  b c          d" | tr -s " "
  a b c d
  %underline{errge@pandora:~$} echo "BalmafaE" | tr '[:lower:]' '[:upper:]'
  BALMAFAE
  %underline{errge@pandora:~$} echo "bALMAFAe" | tr '[:upper:]' '[:lower:]'
  balmafae
  %underline{errge@pandora:~$} cat test
  egy-maganal volt a gyilkos fegyver




  ketto-csipkebokor vesszo
  %underline{errge@pandora:~$} cat test | tr -s '\n'
  egy-maganal volt a gyilkos fegyver
  ketto-csipkebokor vesszo
  %underline{errge@pandora:~$}
\end{ProgalapVerbatim}

A man oldal elolvasása után már érteni fogjuk, hogy miért működik a
_\n_-es utolsó példa, de miért is kell a _\n_-t aposztrófok közé
tenni?  Idézőjelekkel is működne ez a parancssor aposztrófok helyett?
Gondolkodjuk el ezeken a kérdéseken, majd ellenőrízzük álláspontunkat
egy számítógép előtt!

\subsection{A reguláris kifejezések}
A reguláris kifejezések egyszerű építőelemekből és ezeken értelmezett
műveletekből állnak, akár csak az aritmetikai kifejezések.  A
legegyszerűbb ilyen építőelemek a kis- és nagybetűk, valamint a
számok, amik egyszerűen önmagukat jelentik, mint minták.  A _._
jelentése már speciális, vele egy tetszőleges karaktert jelölünk a
mintában.

\subsubsection{Karakterosztályok, a szögletes zárójel}
Karakterosztályt úgy hozhatunk létre, hogy egy szögletes zárójelpáron
belül felsoroljuk a karaktereket.  Egy ilyen építőelem bármilyen olyan
karakterre illeszkedik, ami fel van sorolva, illetve bármilyen
olyanra, ami nincs fel sorolva, ha a felsorolást a _^_ karakterrel
kezdjük. Például a _[0123456789]_ egy tetszőleges számjegyre, míg a
_[^.[:lower:]]_ bármire, ami nem pont vagy kisbetű illeszkedik.  A
szögletes zárójelen belül használhatjuk a már megismert intervallumos
kifejezéseket, illetve osztályneveket.

\subsubsection{Sztring elejének és végének jelölése}
A _^_ és a _$_ %buta emacs: $
karakterek az üres karakterláncra illeszkednek, de csak a sztring
elején, illetve végén.  Azaz, ha a reguláris kifejezésünkben a két jel
valamelyikét használjuk, azzal azt mondjuk, hogy a kifejezés csak
akkor illeszkedik, ha a jel helyén van a karakterlánc eleje, illetve
vége.

\subsubsection{A backslash jelentése}
A backslash (_\_) jel többféle szerepet tölt be.  Egyrészt az
ismeretett speciális karakterek (_._, _*_, _^_, stb.) elé írva azok
elvesztik speciális jelentésüket, másrészt pár egyébként saját magát
jelentő jel pont attól nyer speciális jelentést, hogyha előtte a
backslash szerepel.  Pl. a _\<_, illetve _\>_ olyan minta, ami csak
szó elején, illetve végén illeszkedik az üres karakterláncra.  Nézzünk
utána további ilyen hasznos jelölőknek a _grep_ man oldalában (_\w_,
_\W_, _\b_, _\B_)!

\subsubsection{Ismétlés}
Egy minta után tett _\?_ a mintát opcionálissá, a _\+_ kötelezővé, de
több ismétlődést is megengedővé, a _*_ opcionálissá és több
ismétlődést is megengedővé teszi. Figyeljük meg, hogy a _*_ a sűrűn
használt művelet, ezért azt nem kell, hogy _\_ előzze meg.

Lehetőség van pontos ismétlődésszám megadására is a _\{_ és _\}_
operátorok használatával, további információért olvassuk el a man
oldalt!

\subsubsection{Konkatenáció}
A bemutatott építőelemeket egymásután írhatjuk, ekkor olyan mintát
hozunk létre, ami abban az esetben illeszkedik, ha az egymásután írás
sorrendjében illeszhetőek a részek a karakterlánc egymásutáni
részeire.  Pl. az _ab*._ reguláris kifejezés illeszkedik az _dabbbd_
karakterláncra, de nem az _acb_-re, ugyanis az előbbinek van olyan
része (az _abbbd_), ami felvágható úgy, hogy az első részre az _a_, a
másodikra a _b*_, a harmadik a _._ minta illeszkedik.

\subsubsection{Alternáció}
Ha egy reguláris kifejezésben két (vagy több) minta közé a _\|_ jelet
tesszük, azzal kifejezhetjük, hogy a felsorolt minták közül pontosan
az egyiknek kell illeszkednie.

\subsubsection{Precedencia}
Az előző három operátor precedenciája a megismerésük sorrendjével
azonos, azaz az ismétlést előíróak kötnek a legerősebben és az
alternáció a legkevésbé.  Ezt a szabályt zárójelezéssel, méghozzá a
_\(_ és _\)_ zárójelek használatával bírálhatjuk felül.  Vegyük észre,
hogy erre olyan ritkán van szükség, hogy a reguláris kifejezésekben a
zárójelezést backslash kell, hogy megelőzze.

Amennyiben zárójelezést használunk, akkor a _\n_ (ahol _n_ egy
számjegy) kifejezés az első 9 zárójelezett részre illesztett sztring
valamelyikét jelenti.

\subsubsection{Példák}
\begin{center}
  \begin{tabular}[c]{c|c}
    minta & illeszkedő karakterláncok \\
    \hline
    _.*_ & bármi \\
    _^.*$_ & bármi \\ %$ buta emacs
    _^[[:upper:]]\{7\}\.ELTE$_ & az XXXXXXX.ELTE alakú hallgatói azonosítók \\ %$ buta emacs
    _[[:upper:]]\{7\}\.ELTE_ & hallgatói azonosítót tartalmazó bármilyen sztring \\
    _..._ & legalább három hosszú sztring \\
    _^\(.\+\)\1$_ & két azonos sztringből álló konkatenációk \\ %$ buta emacs
    _^\(.*\)\1$_ & mint előbb, de az üres sztring is \\ %$ buta emacs
    _^[[:upper:]]\{7\}\(:[0-9]\+\)*$_ & $\{$XYZXYZQ.ELTE:12:0$, $QWERTYU.ELTE$, \ldots\}$ %$buta emacs
  \end{tabular}
\end{center}

\subsection{\texttt{grep}}
A _grep_, az _fgrep_-hez hasonlóan bizonyos kritériumoknak megfelelő
sorokat ír a képernyőre fájlokból vagy a standard bemenetről.  A
különbség, hogy a _grep_ esetén a kritérium nem csupán az lehet, hogy
egy részkarakterlánc forduljon elő a sorban, hanem egy tetszőleges
reguláris kifejezést próbálhatunk meg illeszteni minden sorra.

Fontos opciók a _-c_, _-v_, _-h_, _-H_, _-x_, _-i_, _-L_, _-l_,
_-q_, _-o_, _-A_, _-B_ és _-C_, ezek jelentését mindenképp nézzük meg a
dokumentációban!

A _grep_ programhoz a man oldalán túl egy jobb dokumentáció is
elérhető ún. texinfo formátumban, ami a jegyzet írásának pillanatában
(az elvhű Debian fejlesztők és a szintén elvhű FSF miatt) a Pandorán
nem érhető el, azonban megtalálható a
\url{http://www.gnu.org/software/grep/doc/grep.html} címen, illetve
egy PDF verziója a jegyzet weblapjáról is letölthető (\url{http://www.gergely.risko.hu/progalap1/manpages/grep-info.pdf}).

Gyakorlásképp írjuk ki az alma.txt fájl azon sorait, melyekben
\begin{itemize}
\SaveVerb{x}_cat alma.txt | grep '[0-9]'_
\item van számjegy,\footnote{\UseVerb{x}}
\SaveVerb{x}_cat alma.txt | grep '^[0-9]\+$'_ %$buta emacs
\item csak számjegy van,\footnote{\UseVerb{x}}
\SaveVerb{x}_cat alma.txt | grep '^\(.\)\1'_
\item a sor elején 2 egyező karakter van,\footnote{\UseVerb{x}}
\SaveVerb{x}_cat alma.txt | grep '\<\(\w\w\)\1\>'_
\item van _xyxy_ (vagy _xxxx_) alakú szó,\footnote{\UseVerb{x}}
\SaveVerb{x}_cat alma.txt | grep '\<[[:upper:]]*\>'_
\item van csupa nagy betűből álló szó (tehát rövidítés),\footnote{\UseVerb{x}}
\SaveVerb{x}_cat alma.txt | grep '\<\w\{4,\}\>' | grep -v '\<\w\{8\}\>'_
\item van legalább 4, de nincs 8 hosszú szó.\footnote{\UseVerb{x}}
\end{itemize}

Írjunk egy parancsfájlt, ami eldönti a megadott paraméterekről, hogy
azok szabályos hallgatói azonosítók-e!  Adjon vissza annyit, amennyi
szabálytalan van köztük, azaz pont igazat adjon vissza, ha mind
szabályos.  Nem kell azzal törődni, hogyha 256 szabálytalannal hívják
meg a programot, akkor újra igazat ad vissza, 257-nél meg 1-et, tegyük
fel, hogy ennyi paramétert nem adnak neki soha!

\subsection{\texttt{sed}, a stream editor}
A _sed_ programmal előre megadott parancsokkal szerkeszthetünk
szöveges folyamokat.  A legnépszerűbb felhasználása természetesen az,
hogy szűrőként építjük a parancssorunkba és a szerkesztéseket ekkor a
standard bemeneten végzi és a standard kimenetre írja.  Paraméterek
megadásával azonban az is elérhető, hogy a bemenetetét már létező
fájlokból olvassa\footnote{Amit persze egyszerűen egy elé írt
  \texttt{cat}-tel is megoldhatunk.}, sőt a _-i_ paraméterrel a
megadott fájlokat helyben szerkeszti\footnote{természetesen közben
  létrehoz egy átmeneti fájlt, de ezzel nekünk nem kell
  foglalkoznunk}.  A _sed_ programozási nyelvén szinte bármilyen
probléma (kellően csúnyán és bonyolultan) megoldható\footnote{A
  texinfo dokumentáció ad is példát a \texttt{tac}, \texttt{head},
  \texttt{tail}, \texttt{uniq}, \texttt{wc} és \texttt{cat}
  megvalósítására \texttt{sed}-ben.} így az összes többi programhoz
hasonlóan csak egy töredékét tudjuk ismertetni a lehetőségeknek.

A man oldalon kívűl egy jobb dokumentáció is elérhető, ugyanúgy texinfo
formátumban, mint a _grep_-hez.  A Pandorán %buta tex
az _info sed_ hozza elő ezt a dokumentációt, de megtalálható a
\url{http://www.gnu.org/software/sed/manual/} címen is, illetve egy
PDF verziója a jegyzet weblapjáról letölthető
(\url{http://www.gergely.risko.hu/progalap1/manpages/sed-info.pdf}).

\subsubsection{A \texttt{sed} működése}
A _sed_ kezdetben két üres tárterülettel (bufferrel) indul, a minta
bufferrel és a tároló bufferrel.  A tároló bufferrel a jegyzet nem
foglalkozik, trükkösebb _sed_ programoknál van rá szükség.

Az input minden egyes sorára a következő műveletsort hajtja végre:
\begin{itemize}
\item beolvassa a sort a bemenetről és levágja a sorvég jelet, ha van;
\item az így kapott sort a minta bufferbe teszi (a régi tartalmat
  törölve);
\item végrehajtja azokat a parancsokat, amiknek a feltétele igaz;
\item ha a _-n_ opció nem volt megadva, akkor kiírja a minta buffer
  tartalmát (kiírva a korábban elvett újsor jeleket is).
\end{itemize}

Ha eléri a fájl végét, a _sed_ kilép.

\subsubsection{Feltételek, avagy címek}
Mint az a végrehajtási cikluson látszik, minden parancshoz megadható
feltétel, amit címnek is szokás hívni, mert az adott feltétel megcímez
bizonyos sorokat, amikre a feltételhez tartozó parancsok
végrehajtódnak.  Cím megadása után _{ parancs1 ; parancs2 ; ...}_
módon több parancs is felsorolható, elkerülve így a feltétel állandó
ismételgetését.  Az üres feltétel az igazat jelenti, tehát ha nincs
megadva cím, akkor a parancs minden sorra végrehajtódik.

Ha van megadva feltétel, akkor csak a feltételt teljesítő sorokon
hajtódnak végre a parancsok.

Két címet vesszővel elválasztva intervallum is megadható, ami ott
kezdődik, ahol a vessző előtti feltétel igaz és ott végződik, ahol a
vessző utáni feltétel igaz.  Beleértjük az intervallumba a határokat
is.  A záró határ lehet _+N_ alakú is, ahol _N_ egy szám, ekkor ez azt
a sort jelenti, amit kezdő határhoz _N_ sort hozzáadva kapunk.

Feltétel tagadása a feltételt követő felkiáltójellel lehetséges.

Fontosabb feltételtípusok:
\begin{itemize}
\item _N_ (ahol _N_ egy (akár többjegyű) szám): csak az _N_-edik sor
  feldolgozásakor igaz;
\item _$_: az utolsó sor feldolgozásakor igaz csak; %$ buta emacs
\item _/regkif/_: csak akkor igaz, ha a _regkif_ által leírt (a per
  jeleket levédve tartalmazó) reguláris kifejezés illeszkedik az
  aktuálisan sorra.
\end{itemize}
\subsubsection{Egyszerű \texttt{sed} parancsok}
\begin{itemize}
\item _q_: kiírja az aktuális minta buffert\footnote{Ha a \texttt{-n}
    opció nincs megadva.}, majd kilép;
\item _d_: törli az aktuális minta buffert és rögtön a következő sor
  végrehajtásával folytatja;
\item _p_: kiírja az aktuális minta buffert (_-n_ használata esetén jön jól);
\item _n_: lecseréli a minta buffert a következő input sorra, de
  előtte kiírja, ha az automatikus kiírás a _-n_-nel nincs letiltva;
\end{itemize}

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp$} seq 1 3 | sed ''
  1
  2
  3
  %underline{errge@pandora:~/tmp$} seq 1 3 | sed -n ''
  %underline{errge@pandora:~/tmp$} seq 1 3 | sed -n 'p'
  1
  2
  3
  %underline{errge@pandora:~/tmp$} seq 1 3 | sed '2d'
  1
  3
  %underline{errge@pandora:~/tmp$} seq 1 7 | sed '/2/,+3d'
  1
  6
  7
  %underline{errge@pandora:~/tmp$} seq 1 7 | sed '2,/5/!d'
  2
  3
  4
  5
  %underline{errge@pandora:~/tmp$} seq 1 7 | sed '3q'
  1
  2
  3
  %underline{errge@pandora:~/tmp$} seq 1 3 | sed -n 'p;1n'
  1
  3
  %underline{errge@pandora:~/tmp$} seq 1 3 | sed -n 'p;1d'
  1
  2
  3
  %underline{errge@pandora:~$} seq 1 4 | sed '$%{p;p;p%}' | tr '\n' ' ' ; echo
  1 2 3 4 4 4 4
\end{ProgalapVerbatim}

\subsubsection{Az \texttt{s/MIT/MIRE/OPCIÓK} parancs}
Ez az a parancs, ami közismert a _sed_-ből, a legtöbb felhasználója a
_sed_-nek az előbb említett parancsokat, sőt a címzések lehetőségét
nem is ismeri.  Végrehajtásakor a minta buffer első olyan része, amely
illeszkedik a _MIT_ reguláris kifejezésre kicserélődik a _MIRE_
részre.  Bármely részbe úgy írhatunk önmagát jelentő pert, ha
backslash-sel levédjük.

A _MIRE_ részben szerepelhet a már ismert _\1_, _\2_, stb. értékeken
kívűl az _&_ jel is, aminek jelentése a teljes _MIT_ részre illeszkedő
karakterlánc.  Ezekből az is következik, hogyha _\_ vagy _&_ jelet
szeretnénk használni a _MIRE_ részben, akkor azt backslash-sel le kell
védeni.

A különböző opciókat csak egymásután kell írni.  A fontosabbak:
\begin{itemize}
\item _N_, ahol _N_ egy (akár többjegyű) szám: nem az első, hanem az
  _N_. illeszkedő rész cseréje;
\item _g_: az összes illeszkedő rész cseréje, nem csak az elsőé;
\item _i_: a minta illesztése a kis- és nagybetű különbségek
  figyelmenkívűl hagyásával.
\end{itemize}

Fontos megjegyezni, hogy a reguláris kifejezések mindig mohók, ami azt
jelenti, hogy addig illesztenek, amíg csak tudnak, pl. a
_alma:szilva:mogyoro_ sztringre illeszkedik a
_^\(.*\):.*$_ %$ buta emacs
kifejezés, méghozzá úgy, hogy a _\1_ ``értéke'' _alma:szilva_ lesz és
nem csak _alma_.

\subsubsection{Escape szekvenciák}
Eddig a _sed_ programokban a backslash szerepe az volt, hogy a
speciális jelentéssel bíró karaktereket védje (pl. _\^_ vagy _\*_).
Azonban a backslash-t lehet használni ún. escape szekvenciák
létrehozására is, itt pont a backslash miatt nyer speciális szerepet
egy jel.  Pl. a _\n_ jelentése egy újsor karakter, míg a _\t_
jelentése egy tabulátor, a _\xXX_ jelentése (ahol _XX_ egy
hexadecimális szám) pedig az _XX_ kódú karakter.  A _sed_
programjainkba ezeket a speciális karaktereket egyszerűen be is
írhatnánk escape szekvenciák használata nélkül, azonban ez elrejtené
őket, kevésbé lenne átlátható a kód, illetve a ASCII 0-ás karaktert
nem egyszerű feladat beírni egy szkriptbe.

Az említett három szekvenciát az _echo_ is támogatja, azaz pl. így
egyszerre tudunk kiírni több soros szöveget is, de először a _-e_
kapcsolóval engedélyeznünk kell az értelmezésüket.

További információ a \texttt{sed} texinfo dokumentációjában az
``Escapes'' fejezetben, illetve a \texttt{bash-builtins} man oldal
\texttt{echo} fejezetében található.

\subsubsection{Gyakorló feladatok}
\begin{itemize}
\setlength{\itemsep}{0pt}
\SaveVerb{x}_echo $(($(tr '\n' + | sed 's/+$//')))_
\item Írjunk parancsfájlt, ami összeadja a standard bemenetén
  soronként kapott számokat!\footnote{\UseVerb{x}}
\SaveVerb{x}_sed -i 's/^\(.\)\1\(.*\)\(.\)$/\3\2\3/' barack.txt_ %$buta emacs
\item A barack.txt fájlban ha a sor elején 2 egyező karakter van,
  azokat cseréljük a sor végén lévő jelre.\footnote{\UseVerb{x}}
\SaveVerb{x}_sed 's/^\(.*\):\(.*\)$/\2:\1/'_ %$buta emacs
\item A _cut_ tárgyalásakor megjegyeztük, hogy hiába cseréljük a
  (mondjuk kettősponttal elválasztott) mezők felsorolását az
  opciólistában, attól a kiíráskori sorrendjük nem változik.  Hogyan
  cserélhetnénk akkor meg egy kimenetben minden sor 1. és 2. oszlopát,
  feltéve, hogy a fájl minden sora két oszlopos?\footnote{\UseVerb{x}}
\SaveVerb{x}_sed 's/^\(\([^:]*:\)\{2\}\)\([^:]*\):\(.*\):\(.*\)$/\1\5:\4:\3/'_ %$buta emacs
\item A 3. oszlopot és az utolsót, feltételezésekkel nem élve?\footnote{\UseVerb{x}}
\SaveVerb{x}_sed 's/./&\n/g' barack.txt | tr '[:upper:]' '[:lower:]' | grep '[[:lower:]]' | _
\SaveVerb{y}_sort | uniq -c | sort -n | tail -n1 | sed 's/[^a-z]//g'_
\item Adjuk meg a recept.txt fájl leggyakoribb betűjét, ha a kis- és
  nagybetűk között nem teszünk
  különbséget!\footnote{\UseVerb{x}\newline\hbox{}\hspace{5em}\UseVerb{y}}

\end{itemize}

\section{\texttt{find}}
A fájlok interaktív listázására az _ls_ parancs kitűnően megfelelt,
azonban parancsfájlokban való használatra nem volt túlságosan
alkalmas.  Nehézkes volt pl. a méret oszlop megszerzése, előtte a
szóközöket _tr_-rel össze kellett vonni, aztán le kellett számolni,
hogy pontosan melyik oszlopra van szükségünk, a szóközös fájlok
kezelése pedig extra figyelmet igényelt.

A _find_ ezekre a problémákra nyújt sokkal jobb megoldást, itt röviden
mutatjuk be és most is van a man oldalon kívűl texinfo dokumentáció
is, ami az _info find_ paranccsal, illetve HTML\footnote{A
  \url{http://www.gnu.org/software/findutils/manual/find.html} címen.}
vagy
PDF\footnote{A \url{http://www.gergely.risko.hu/progalap1/manpages/find-info.pdf} címen.}
formában is elérhető.

Használata: _find KÖNYVTÁRAK-ÉS-FÁJLOK... OPCIÓK TESZTEK-ÉS-TEVÉKENYSÉGEK..._

Vegyük észre, hogy a _find_ kilóg a többi parancsunk sorából, mivel ez
a könyvtárak és fájlok nevét, amin dolgoznia kell a parancssor elején
várja és máshol nem is fogadja el.  Alapértelmezés az aktuális
könyvtár, így a _find . -opc1 -opc2 ..._ helyett %buta latex
_find -opc1 -opc2 ..._ is írható.  Azért is kilóg a _find_ a többi
parancsunk közül, mert az opciók nevét egész szavas, hosszú opciók
esetén is csak egy _-_ jel kell és szabad, hogy megelőzze.

Az opciók (options) a _find_ teljes lefutását befolyásolják, így
azokat közvetlenül az argumentumok után kell felsorolni, míg a tesztek
(tests) és a tevékenységek (actions) egyszerűen igaz vagy hamis
értékkel bírnak minden fájlrendszerbejegyzésre (továbbiakban fájlra)
nézve.  Az operátorok segítségével a tesztek és a tevékenységek
összekapcsolhatóak, így nagyobb logikai kifejezések írhatóak.  A
tevékenységeknek a tesztekkel ellentétben mindig van mellékhatásuk is
(pl. letörölnek egy fájlt, kiírják a nevét vagy méretét, stb.).

A find alapértelmezés szerint rekurzívan listáz és csak a megtalált
fájlrendszerbejegyzések nevét írja ki, soronként egyet.  A rekurzív
listázás maximális és minimális mélysége a _-maxdepth_ és _-mindepth_
argumentumot váró opciókkal állítható, pl. a %buta latex
_find -maxdepth 1 -mindepth 1_ parancs pontosan ugyanazt csinálja,
mint a már ismert _ls -1_.

Fontos opció még a _-regextype_, mivel a find alapértelmezés szerint
más fajta reguláris kifejezéseket használ, mint amilyeneket a
_sed_-nél és a _grep_-nél megtanultunk, ezért ha olyan parancssort
használunk, ami reguláris kifejezéseket használ, akkor a parancssor
_OPCIÓK_ részében adjuk meg a _-regextype posix-basic_ opciót.

\subsection{Tesztek (tests)}

A számmal paraméterezhető tesztek esetén a szám megadható _+N_ vagy
_-N_ formában is, ahol az előbbi az _N_-nél szigorúan nagyobb, míg
utóbbi a kisebb értékeket jelenti.  A fontosabb tesztek:
\begin{itemize}
\item _-false_: mindig hamis,
\item _-true_: mindig igaz,
\item _-empty_: üres könyvtár vagy fájl,
\item _-group GROUP_: a fájl tulajdonosi csoportja _GROUP_,
\item _-user USER_: a fájl tulajdonosa _USER_,
\item _-name MINTA_: a fájl neve illeszkedik a _MINTÁ_-ra globbing
  értelemben (\ref{pathexp})\footnote{A rejtett fájlokat is figyelembe veszi.c},
\item _-iname MINTA_: mint az előző, de a kis- és nagybetű különbségeket figyelmen kívűl hagyva,
\item _-wholename MINTA_: a fájl teljes elérési útja illeszkedik a _MINTÁ_-ra, mint előbb,
\item _-iwholename MINTA_,
\item _-regex REGEX_: az _REGEX_ reg. kifejezés illeszkedik a fájl
  teljes elérési útjának egészére\footnote{Tehát ha az \texttt{a} vagy
    \texttt{b} karaktert az elérési útban bárhol tartalmazó fájlokra
    vagyunk kiváncsiak, akkor a \texttt{-regex '[ab]'} teszt helyett a
    \texttt{-regex '.*[ab].*'} tesztet kell használni.},
\item _-size Nc_: a fájl mérete _N_ bájt,
\item _-type c_: a fájl típusa _c_, ahol a _c_ lehet _d_ és ekkor
  könyvtárat jelent vagy _f_ és ekkor hagyományos fájlt.
\end{itemize}

\subsection{Tevékenységek (actions)}
A fontosabb tevékenységek:
\begin{itemize}
\item _-print_: mindig igaz, és kiírja az aktuális fájlnevet (ez
  alapértelmezés, ha nincs más tevékenység megadva),
\item _-printf FORMA_: mindig igaz, és kiírja a fájl tulajdonságait a
  megadott _FORMÁ_-nak megfelelően, amiben a következő speciális
  karakterek lehetnek:
  \begin{itemize}
  \item _\n_: újsor karakter, ui. nincs új sor a _-printf_-fel való
    kiírás végén automatikusan,
  \item _%s_: a fájl mérete bájtokban,
  \item _%f_: a fájl neve,
  \item _%p_: a fájl elérési útja,
  \item _%%_: egy _%_ karakter.
  \end{itemize}
\item _-exec PARANCS ;_: a PARANCS-ot végrehajtja (és a parancs
  visszatérési értéke ezen tevékenység igazságértéke), úgy, hogy a
  PARANCS-ban előforduló _{}_ jelsorozatokat az aktuális fájl elérési
  útjára cseréli.  Ne feledkezzünk meg arról, hogy a _;_ speciális a
  shell számára, így azt le kell védeni, hogy a _find_ egyáltalán
  megkapja, mint paramétert.\footnote{Rendszergazdajelöltek
    mindenképp nézzék meg az \texttt{-execdir} tevékenységet is!}

  Pl. letörölhetjük az aktuális könyvtár összes olyan fájlát, ami
  legalább egy _a_ betűt tartalmaz a következő paranccsal:\\
  _find -maxdepth 1 -mindepth 1 -type f -name '*a*' -exec rm {} \;_
\end{itemize}

Példa, fájlméretek összeadása:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp$} cat sum.sh
  #!/bin/bash

  echo $(($(tr '\n' + | sed 's/+$//')))
  %underline{errge@pandora:~/tmp$} find -maxdepth 1 -mindepth 1  -type f -printf '%%s:%%p\n'
  0:./fajl1
  0:./.a
  36:./output
  17:./xxx
  125:./rendez-sorszerint.sh
  2:./x  b
  117:./sdfg:sdfg
  177:./megfordit.sh
  284:./test.sh
  69:./sum.sh
  82:./barack.txt
  %underline{errge@pandora:~/tmp$} find -maxdepth 1 -mindepth 1  -type f -printf '%%s\n' | ./sum.sh
  909
\end{ProgalapVerbatim}

\subsection{Operátorok}

Az ismeretett teszteket és tevékenységeket a következő operátorokkal
köthetjük össze (csökkenő precendencia sorrendben):
\begin{itemize}
\item _( find-kifejezés )_: zárójelezés, a helyes precedencia-sorrend
  kikényszerítésére.  Ne feledkezzünk meg róla, hogy a zárójeleket a
  shell elől levédjük, hiszen ő is használ zárójeleket!  Például a
  _find ( -print )_ parancs helytelen, míg a _find \( -print \)_
  helyes.  Ugyanakkor ne gondoljuk, hogy a problémát megoldhatjuk az
  egész kifejezés aposztrófok közé írásával, ugyanis a paraméterek
  szavakra bontására a _find_ önállóan nem képes, azt a shellnek kell
  elvégeznie;
\item _! find-kifejezés_: tagadás, a kifejezés igazságértékének
  megfordítása.  Gondoljuk meg, hogy mit csinál a _find -print -print_
  és mit csinál a _find \! -print -print_ parancs;
\item _find-kif1 find-kif2_ vagy _find-kif1 -a find-kif2_: kifejezések
  logikai konjunkciója (ése).  A shellhez hasonlóan a find lusta, ha az első
  kifejezés meghiúsul, akkor a másodikat már meg sem próbálja.  Vegyük
  észre azt is, hogy ez az operátor az alapértelmezett, ha nem írunk
  semmit a tevékenységeink és teszteink közé.  Ezért kapjuk azt a
  természetes működést, hogy a _-type f -size 5 -owner errge_
  opciósorozat a jegyzet szerzőinek 5 bájtos fájlait jelenti, anélkül,
  hogy _-a_ operátorokat is be kellene szúrnunk;
\item _find-kif1 -o find-kif2_: diszjunkció (vagy) operátor, szintén rövidzár
  kiértékeléssel.
\end{itemize}

\subsection{Gyakorló feladatok}
\begin{itemize}
\item Listázzuk ki az aktuális könyvtár összes reguláris fájlának
  nevét!

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/A$} find . -maxdepth 1 -type f
  ./xxx
  ./x b
  ./.a
  %underline{errge@pandora:~/tmp/A$} find . -maxdepth 1 -type f \! -name '.*'
  ./xxx
  ./x b
  %underline{errge@pandora:~/tmp/A$} find . -maxdepth 1 -type f \! -name '.*' -printf '%%f\n'
  xxx
  x b
\end{ProgalapVerbatim}

\SaveVerb{x}_find ~ -name '*.html' -printf '%s:%f\n' | sort -n | tail -n1 | cut -d: -f2-_ %$buta emacs
\item Add meg a bejelentkezési könyvtár bármely mélységében lévő
  _.html_ dokumentumok közül a legnagyobb méretűnek a
  nevét!\footnote{\UseVerb{x}}

\SaveVerb{x}_find ~ -name '*.html' -size +99c -printf '%s %f\n'_ %$buta emacs
\item Add meg az összes említett _.html_ dokumentumok közül azokat a
  méretükkel együtt, amelyek legalább 100 bájt
  hosszúak!\footnote{\UseVerb{x}}

\SaveVerb{x}_find $DIR -mindepth 2 -maxdepth 2 -type f -printf '%h\n' | uniq -c | grep -v '^ *[1-4] '_
\SaveVerb{y}_| cut -d/ -f2_ %$buta emacs
\item Írj parancssort, ami a _DIR_ környezetváltozóban adott
  könyvtárban lévő alkönyvtárak közül kiírja azok nevét, melyekben van
  legalább 5 közönséges fájl.\footnote{\UseVerb{x}\newline\hbox{}\hspace{5em}\UseVerb{y}}

\SaveVerb{x}_find . -maxdepth 1 -type f -name '*.txt' -printf 'Nev: %f\n' -exec cat {} \; >nagy.sum_
\item Írj parancssort, ami az aktuális könyvtárban lévő összes _.txt_
  kiterjesztésű fájl tartalmát a _nagy.sum_ nevű fájlba írja, úgy,
  hogy minden fájlt megelőz egy egysoros fejléc, ami a fájl nevét
  tartalmazza.\footnote{\UseVerb{x}}

\item Írj parancssort, ami az aktuális könyvtárban közvetlenül lévő
  fájlok közül kiírja azok nevét, melyek alakja _unixPQR_, ahol _P_,
  _Q_ és _R_ számjegyek, és még az is teljesül, hogy _PQR_, mint
  3-jegyű szám hárommal osztható!

  A feladat első megoldása során egy sokszor használt, haladónak
  nevezhető trükkhöz folyamodunk.  Azt fogjuk csinálni, hogy program
  által előállítunk olyan parancsokat, amik pont a kívánt feladatot
  érik el.  Ezt a parancsokat tartalmazó kimenetet utána már csak bele
  kell irányítanunk a parancsfeldolgozóba, hogy az végre is hajtsa!

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/A$} find -maxdepth 1 -name 'unix[0-9][0-9][0-9]' -type f
  ./unix123
  ./unix133
  ./unix000
  ./unix998
  %underline{errge@pandora:~/tmp/A$} find -maxdepth 1 -name 'unix[0-9][0-9][0-9]' -type f \
    -printf '[ $(($(echo %%f | cut -c5-) %%%% 3)) -eq 0 ] && echo %%f\n'
  [ $(($(echo unix123 | cut -c5-) %% 3)) -eq 0 ] && echo unix123
  [ $(($(echo unix133 | cut -c5-) %% 3)) -eq 0 ] && echo unix133
  [ $(($(echo unix000 | cut -c5-) %% 3)) -eq 0 ] && echo unix000
  [ $(($(echo unix998 | cut -c5-) %% 3)) -eq 0 ] && echo unix998
  %underline{errge@pandora:~/tmp/A$} find -maxdepth 1 -name 'unix[0-9][0-9][0-9]' -type f \
    -printf '[ $(($(echo %%f | cut -c5-) %%%% 3)) -eq 0 ] && echo %%f\n' | bash
  unix123
  unix000
  %underline{errge@pandora:~/tmp/A$}
\end{ProgalapVerbatim}

Egy új szintaktikai apróságot is tanultunk, ha a sor végére rakunk egy
backslasht és rögtön újsort kezdünk, akkor azzal levédtük az újsor
karaktert, azaz nem jelenti a parancs végét többé és így a
parancsunkat a következő sorban tudjuk folytatni.  A való életben
persze erre nincs szükség sűrűn, hiszen bármilyen hosszú sorokat be
tudunk gépelni, azonban nyomtatásban szükség lehet rá, illetve a saját
programkódjaink is átláthatóbbak, ha nincsenek benne nagyon-nagyon
hosszú sorok.

\item A második megoldás egy parancsfájl lesz, ami szokványosabb és
  egyáltalán nem épít a _find_-ra, egy _for unix[0-9][0-9][0-9]_
  ciklust fog használni és a ciklusmag mindig csak azt vizsgálja, hogy
  jó (3-mal osztható) sort vizsgálunk éppen.

\begin{ProgalapVerbatim}
  #!/bin/bash
  shopt -s nullglob
  for i in unix[0-9][0-9][0-9]
  do
    if [ -f $i ] && [ $(($(echo $i | cut -c 5-) %% 3)) -eq 0 ]
    then
      echo $i
    fi
  done
\end{ProgalapVerbatim}
%$buta emacs

A parancsfájl első parancsának hatására amennyiben egy globbing
(\ref{pathexp}) nem illeszkedik, akkor nem a nyers minta, hanem az
üres sztring a kifejtés eredménye és így a _for_ ciklus nem csinál
semmit.  Más szóval az a sor csak azért kell, hogy a parancsfájl
működjön _unixPQR_ fájlt egyáltalán nem tartalmazó könyvtárakban is.
Persze ez a probléma máshogy is kezelhető, pl. a ciklusmagban
ellenőrízhetnénk, hogy a _$i_ %$ buta emacs
a _unix[0-9][0-9][0-9]_ sztring e és ha igen, akkor rögtön
kiléphetnénk egy _exit 0_ utasítással. Írjuk át ilyenre a parancsfájlt
és vegyük ki az _shopt_ kezdetű sort, majd ellenőrízzük egy pl. üres
könyvtárban, hogy ekkor valóban semmilyen hiba nem történik!
\end{itemize}

\section{\texttt{read}}
A _read_-del a standard bemenetről olvashatunk egy sort és ezt
ciklusba szervezve olyan feladatokat, amiket csak külöböző trükkökkel
tudtunk megoldani, szépen, átláthatóan programozhatunk le shellben.  A
_read_ parancsot nem lehet külön programként megvalósítani, csak a
shell részeként, ezért arról (és a többi _bash_-be épített parancsról)
a _man 7 builtins_ paranccsal kérhetünk részletesebb információt.

Legegyszerűbb és az általunk egyetlen megemlített felhasználási módja
a _read KÖRNYVÁLT_ forma, aminek hatására az olvasott sor bekerül a
_KÖRNYVÁLT_ valtozóba és a visszatérési érték _0_, amennyiben még nem
értük el a fájl végét.  Ha elértük, akkor a _KÖRNYVÁLT_ érétéke üres
lesz és a _read_ visszatérési értéke nem _0_.

Fontos megjegyezni, hogy a _while read_ ciklusok sokszor szükségesek,
de mindig érdemes elgondolkodni, ugyanis ha ki tudjuk kerülni a
használatukat, akkor általában a programunk erőforrásigénye (és így a futási idő
is) radikálisan lecsökken.

\subsection{Példák}
A standard inputon érkező számok összeadása (_sum.sh_) elég trükkös
volt korábban.  Most azonban könnyebben megoldhatjuk: a _sum_
környezetváltozót lenullázunk, majd egy _while_ ciklussal kombinált
_read_ utasítással soronként beolvassuk a számokat és mindet
hozzáadjuk a _sum_ aktuális értékéhez, melyet a ciklus után kiírunk.

\begin{ProgalapVerbatim}[label=\fbox{\texttt{sum.sh}}]
  #!/bin/bash

  sum=0
  while read SOR
  do
    sum=$(($sum+$SOR))
  done
  echo $sum
\end{ProgalapVerbatim}

Vegyük észre, hogy a ciklusfeltételül megadott _read_ akkor ad először
hamisat vissza, amikor az input fájl végét elérte és így pont jókor
lép ki.

Készítsünk egy következő programot, ami az első paraméterként kapott
fájl sorait egyesével bekeretezi pont akkora kerettel, amibe belefér,
majd egymás alatt kiírja a keretezett szövegeket.  Ügyeljünk a
szóközöket tartalmazó paraméterekre!  Így nézzen ki:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/A$} cat szilva.txt
  szilva
  korte      dio
  mogyoro
  %underline{errge@pandora:~/tmp/A$} ./keretez.sh szilva.txt
  +------+
  |szilva|
  +------+
  +--------------+
  |korte      dio|
  +--------------+
  +-------+
  |mogyoro|
  +-------+
\end{ProgalapVerbatim}

A megoldásban egyszerűen soronként olvassuk az inputot, majd minden
sorhoz elkészítjük a keret felső sorát, a középső sorát a tartalommal
és az alsó sorát!

\begin{ProgalapVerbatim}[label=\fbox{\texttt{keretez.sh}}]
  #!/bin/bash

  cat "$1" | while read SOR
  do
    echo -n + ; echo "$SOR" | sed 's/./-/g;s/$/+/'
    echo "|$SOR|"
    echo -n + ; echo "$SOR" | sed 's/./-/g' | sed 's/$/+/'
  done
\end{ProgalapVerbatim}

\noindent Avagy: {\footnotesize_cat $1 | sed -n 'p;p;p' | sed '2~3!{s/./-/g;s/^/+/;s/$/+/;};2~3{s/^/|/g;s/$/|/g}'_}
%$buta emacs

A _read_ parancsnak több változónevet megadva az olvasott sort az
_IFS_ környezetváltozóban lévő karakterek szerint\footnote{Az
  \texttt{IFS} alapértelmezés szerint csak a szóközt tartalmazza, és
  vigyázzunk az állítgatásakor, mert nagyon sok más parancsot is
  befolyásol, legjobb, ha csak a \texttt{read} idejére állítjuk át, ha
  ezt a különleges utat választjuk.} szétválasztja és az előfordulásuk
sorrendjében hozzárendeli a felsorolt változókhoz, az utolsó
környezetváltozóba kerül a sor megmaradt része.  Tehát az eddig látott
_while read VÁLTOZÓ_ alak egyszerűen ennek az esetnek speciális
előfordulása, ahol az egész sor ``hátramaradtnak'' számít.\footnote{Ha
  a \texttt{VÁLTOZÓ}-t is elhagyjuk, akkor a \texttt{REPLY}-ban lesz
  az egész válasz, mint hátramaradt rész.}

Felhasználva ezt szép kódot lehet írni, kényelmesen és tökéletesen (a
dupla szóközök is megmaradnak) feldolgozható az _ls_ kimenete:

\begin{ProgalapVerbatim}
  errge@pandora:~/tmp$ ls -l --time-style=long | tail -n +2 | \
    while read JOGOK LINKEK TULAJ CSOPORT MERET DATUM IDO NEV ; do echo "$MERET:$NEV" ; done
  6:alma korte
  0:atmenetifajl
  2:x  b
\end{ProgalapVerbatim}
%$buta emacs

Mégsem szokás ezt a lehetőséget felhasználni, hiszen az egész sor
később is feldolgozható a már tanult _tr_ és _cut_ parancsokkal, és
ugyanez a hatás elérhető.  Mi sem ezért említjük ezt meg, hanem azért,
mert ebből következik a _read_-nek egy idegesítő mellékhatása:
gondoljuk át azt az esetet, ha egy sor szóközökkel (az elválasztó
karakterrel) kezdődik, pl. be szereténénk keretezni magát a
_keretez.sh_-t!  Ezek a kezdő szóközök eltűnnek, hiszen az első oszlop
csak utánuk kezdődik és attól kezdve kell mindent elrakni a _SOR_ nevű
változóba.  Az utolsó oszloptól már megmaradnak az egymásutáni
szóközök, de előtte nem, pont ezért működött az _ls_ feldolgozásai is.
Próbáljuk ki, a keretezésben nem lesznek meg a sorkezdő szóközök!  A
hiba úgy orvosolható, ha az _IFS_-t csak a _read_ futtatásának az
idejére üresre állítjuk, azaz az első sort erre módosítjuk: %bl
_cat "$1" | while IFS="" read SOR_ %$buta emacs

Azt is figyeljük meg, hogy milyen fontos, hogy a _$SOR_ %$buta emacs
változóbehelyettesítéseket idézőjellel védjük!  Egyrészt emiatt lesz a
keret helyes sok szóköz előfordulásának esetén is, másrészt a keret
közepének első és utolsó karaktere shell környezetben védés nélkül a
csővezetéképítést jelenti, azaz védés nélkül szintaktikai hibákat
kapunk (amik ellenséges szövegfájl feldolgozásakor akár általunk nem
kívánt parancsok nevünkben való végrehajtását is eredményezhetik).

\section{\texttt{tempfile}}

Ezzel a (nem minden unixon megtalálható, széles körben sajnos még nem
elterjedt) paranccsal egyszerűen hozhatunk létre átmeneti fájlokat az
erre szolgáló _/tmp_ könyvtárban.

Példaként tükrözzük függőlegesen az inputot, sorainak felcserélésével!
Írjuk elé a sorok számát!  Az első megoldás egészen egyszerűen a
%% buta latex
_tac | cat -n_ pipeline, de most egy pillanatig tételezzük fel, hogy
se a _tac_ program, se a _cat_ _-n_ paramétere nem létezik.

Akkor a megoldás az lehetne, hogy az egész inputot kiírjuk egy fájlba
és utána egy ciklussal feldolgozzuk a fájlt visszafele.  Mondjuk így:
\begin{ProgalapVerbatim}[label=\fbox{\texttt{visszafeleszamoz.sh}}]
  #!/bin/bash

  cat >atmenetifajl

  SOROK=$(cat atmenetifajl | wc -l)
  for i in $(seq $SOROK)
  do
    echo -n "$i "
    tail -n $i atmenetifajl | head -n1
  done
  rm atmenetifajl
\end{ProgalapVerbatim}
%$buta emacs

Problémát okoz, ha van _atmenetifajl_ nevű fájlunk az aktuális
könyvtárban, illetve az egész szkript nem működik, ha az aktuális
könyvtárban nem tudunk fájlt létrehozni pl. jogosultság hiányában.
Ezért az ehhez hasonló átmeneti fájlok tárolására készítették van a
_/tmp_ könyvtárat, ahova mindenki ``szemetelhet'' és azt időnként
letörlik a rendszerek gazdái.  Óvatosan kell bánni ezzel, mivel ezt a
könyvtárat mindenki közösen használja.  A _cat >/tmp/atmenetifajl_
megoldást pl. két különböző felhasználó egyszerre futtatva problémát
okozhat egymásnak.  Illetve az így létrejövő fájl más által is
olvasható feldolgozás közben.  Sőt, ha valaki előre megtudja, hogy mit
fog a programunk csinálni akkor ügyesen még tetszőleges fájlunkat le is
letörölheti.

A helyes megoldás valahogy így néz ki:
\begin{ProgalapVerbatim}[label=\fbox{\texttt{visszafeleszamoz.sh}}]
  #!/bin/bash

  TMPFILE=$(tempfile)
  cat >$TMPFILE

  SOROK=$(cat $TMPFILE | wc -l)
  for i in $(seq $SOROK)
  do
    echo -n "$i "
    tail -n $i $TMPFILE | head -n1
  done
  rm $TMPFILE
\end{ProgalapVerbatim}

Persze ez a megoldás nem túl hatékony, nagyon sokszor végigolvassa a
fájlt, hogy kiírja az utolsó _i_ darab sort, pedig igazából egyszer
elég lenne, csak mindent el kellene tárolni és aztán visszafele
kiírni.  A _tac_ így működik, de mi is tudunk ilyen okosabb és
hatékonyabb, átmeneti fájl nélküli megoldást készíteni, felhasználva a
shell tömbjeit, melyekkel egy néven, de különböző indexekkel
tárolhatunk sok adatot, hasonlóan a matematika vektor fogalmához. A
tömbökről bővebb információ a \texttt{man 1 bash} paranccsal kapható,
az \texttt{Arrays} szóra kell keresni!

\begin{ProgalapVerbatim}[label=\fbox{\texttt{visszafeleszamoz-tomb.sh}}]
  #!/bin/bash

  i=0
  while read SOR[i]
  do
      i=$(($i+1))
  done

  for j in $(seq $(($i-1)) -1 0)
  do
    echo $(($i-$j)) $%{SOR[$j]%}
  done
\end{ProgalapVerbatim}

\section{A UNIX fájlrendszere}
Felhasználói szemszögből megismertük, hogy a legegyszerűbb
fájlrendszerbejegyzésekkel hogyan kell dolgoznunk, kezeltünk fájlokat
és könyvtárakat.  Érdekes kérdés, hogy hogyan biztosítja a rendszer
ezeket az egyszerű objektumokat.  Hiszen neki csak egy nagy összefüggő
adatterülete van, a háttértároló.  Mi pedig kis (és nagy) fájlokat
hozunk létre, amiket könyvtárakba teszünk, teljesen össze-vissza
némelyiknek megnöveljük (illetve lecsökkentjük) a méretét, másolgatjuk
és áthelyezzük őket.  Az informatika ezen területe állandóan fejlődik,
jelenleg is aktívan kutatják, újabb és újabb fájlrendszereket hoznak
létre, amik egyre jobban oldják meg a feladatokat.

Ugyanakkor kialakult egy elgondolás, ami alapján szervezik a
fájlrendszereket.  Újabban nem minden fájlrendszer követi ezt, de azok
is úgy viselkednek a külső szemlélő számára, mintha követnék, mivel
programok sokasága ráépült erre a régen kitalált működési elvre.

Az elgondolás alapja, hogy minden egyes fájlhoz létrehozunk egy
_inode_ nevű adatstruktúrát, amit egy azonosítóval, az _i-number_-rel
(avagy _ino_-val) címzünk egy nagy táblázatban.  A nevén kívűl minden
adatot (a bejegyzés típusát, a méretét, a jogosultságokat, a
tulajdonost és a csoportot, a különböző időbélyegeket, a fájlra mutató
fájlnevek számát, a tárolt adatok háttértárolón való helyét) ez az
adatszerkezet tárol a fájlról.

Ezzel az egy ötlettel készen is vagyunk, már csak annyit kell
hozzátenni, hogy a könyvtárak pedig olyan egyszerű fájlok, amik
albejegyzések nevét rendelik hozzá _ino_ számokhoz.  Ezekután
tetszőleges fájl _inode_ struktúrája megkereshető a gyökérkönyvtártól
kiindulva (aminek az _ino_ száma fix).  Megállapodtak abban is, hogy
minden könyvtár első és második bejegyzése a _._ és _.._, hogy az
relatív elérési utak is gyorsan értelmezhetőek legyenek.

\subsection{Hardlinkek}

A rendszer nem tiltja, hogy egy adott _ino_ számra több könyvtárból is
hivatkozást helyezzünk el, azonban az utolsó hivatkozás törlése után a
fájl már nem lesz elérhető és az ő adatai bármikor felülírhatók.
Ezért kell a számláló az _inode_ struktúrába, hogy tudjuk, hogy az
mikor válik nullává.  Ha egy _inode_-ra több fájlnév is hivatkozik,
akkor ezeket a fájlokat szokás egymás hardlinkjeinek hívni.  Nincs
közülük elsődleges, meg másodlagos, hiszen sehogy nem tudunk
különbséget tenni köztük, hogy ki hardlinkje kinek, ráadásul ha
bármelyik mentén módosítjuk a fájlt vagy tulajdonságait, azzal a
háttértároló megfelelő adatterületét, illetve az inode struktúrát
módosítjuk, tehát a módosítás a másik néven keresztül is látszódni
fog.  Az elmondottakból az is következik, hogy annak megállapítása,
hogy egy fájlra vannak-e hardlinkek könnyű feladat (nagyobb-e a
számlálója, mint 1), viszont annak megállapítása, hogy hol vannak ezek
a hardlinkek az egész fájlrendszer fájának teljes átkutatását igényli,
mivel egy utolsó eldugott kis könyvtárban is lehetnek hivatkozások az
adott _ino_ számra.

Az _ls -li_ paranccsal a fájllistában láthatóvá válnak a sorok elején
az _ino_ számok, az _ln FORRÁS CÉL_ paranccsal pedig létrehozhatunk
hardlinkeket.  Könyvtárak hardlinkelését csak a rendszergazda
végezheti el, de neki sem tanácsos, ugyanis ezzel a fájlrendszer
fastruktúrját tetszőleges gráffá változtathatja, aminek kezelése
később nagyon elbonyolodhat.

Az üres könyvtáraknak kettő a számlálójuk, hiszen egy hivatkozás van
maguktól (_._), egy pedig a szülőkönyvtárban, míg a hagyományos fájlok
számlálójának kiindulóértéke 1:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/x$} ls
  %underline{errge@pandora:~/tmp/x$} ls -lid .
  1363013114 drwxr-xr-x 2 errge progterv 6 2007-10-25 00:14 .
  %underline{errge@pandora:~/tmp/x$} echo valami >fajl1
  %underline{errge@pandora:~/tmp/x$} ls -li
  total 4
  1366509377 -rw-r--r-- 1 errge progterv 7 2007-10-25 00:15 fajl1
\end{ProgalapVerbatim}

Minden egyes újonnan létrehozott hardlinkkel 1-el nő:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/x$} ln fajl1 hardlink
  %underline{errge@pandora:~/tmp/x$} ls -li
  total 8
  1366509377 -rw-r--r-- 2 errge progterv 7 2007-10-25 00:15 fajl1
  1366509377 -rw-r--r-- 2 errge progterv 7 2007-10-25 00:15 hardlink
\end{ProgalapVerbatim}

Új alkönyvtár létrehozásával, a tartalmazó könyvtár számlálója eggyel
nő, hiszen az új könyvtár _.._ bejegyzése pont rá hivatkozik:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/x$} mkdir aldir
  %underline{errge@pandora:~/tmp/x$} ls -lid .
  1363013114 drwxr-xr-x 3 errge progterv 45 2007-10-25 00:15 .
  %underline{errge@pandora:~/tmp/x$} mkdir aldir2
  %underline{errge@pandora:~/tmp/x$} ls -lid .
  1363013114 drwxr-xr-x 4 errge progterv 58 2007-10-25 00:15 .
\end{ProgalapVerbatim}

Mint említettük, a tárterület közös:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/x$} ln hardlink aldir/xxx
  %underline{errge@pandora:~/tmp/x$} echo felulir >aldir/xxx
  %underline{errge@pandora:~/tmp/x$} cat fajl1
  felulir
  %underline{errge@pandora:~/tmp/x$} ls -li
  total 8
  1616090068 drwxr-xr-x 2 errge progterv 6 2007-10-25 00:16 aldir
  1774943086 drwxr-xr-x 2 errge progterv 6 2007-10-25 00:15 aldir2
  1366509377 -rw-r--r-- 3 errge progterv 8 2007-10-25 00:16 fajl1
  1366509377 -rw-r--r-- 3 errge progterv 8 2007-10-25 00:16 hardlink
\end{ProgalapVerbatim}

Azt is láthatjuk az _ls_ kimenetéből, hogy a _fajl1_ és _hardlink_
nevű fájlok egymás hardlinkjei, viszont ez nem minden, mert összesen
három link van arra a területre, azonban ha nem tudjuk, hogy az
_aldir_ nevű könyvtárunk _xxx_ fájla az, akkor elég nehéz megtalálni.
Persze a saját fájlaink hardlinkjei biztos a saját részünkben lesz a
Pandora gépen és tudjuk a számát is (1366509377), így _find_-dal
megtalálható:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/x$} find ~ -inum 1366509377
  /h/e/errge/tmp/x/fajl1
  /h/e/errge/tmp/x/hardlink
  /h/e/errge/tmp/x/aldir/xxx
\end{ProgalapVerbatim}

\subsection{Szimbolikus linkek}
Az, hogy egy tárterületre több néven is lehet hivatkozni olyan
közkedvelt lett, hogy egy másik ilyen lehetőséget is létrehoztak, a
szimbolikus linkeket (symlinkeknek és szoftlinkeknek is szokás nevezni
őket).  Ezek sokban különböznek a hardlinkektől, ők egyszerűen csak
egy speciális fájltípussal rendelkező fájlok, amiknek a tartalma a
mutatott fájl elérési útja.  Egy szimbolikus link célja meg lehet adva
abszolút elérési úttal (_/_ jellel kezdődően) vagy relatíven.  A
relatív linkek azért jók, mert egész könyvtárakat mozgatva, tömörítve,
kibontva helyesek maradnak az új helyükön is.

A symlinkek esetében látható, hogy meg lehet mondani, hogy ki symlink
kire, viszont azt továbbra is csak teljes kereséssel lehet
megállapítani, hogy egy fájlra vannak-e szimbolikus linkek.  Ha egy
fájlt letörlünk, amire szimbolikus linkek voltak, akkor azok a linkek
értelmetlenné válnak, hiszen a hivatkozott fájl már nem található meg.

A hardlinkekkel ellentétben, a szimbolikus linkek könyvtárakra is
hivatkozhatnak, illetve más háttértárolón is lehet a céljuk.  Ugyanis
annak ellenére, hogy a unix rendszerekben látszólag minden egy közös
gyökérkönyvtárban van, bizonyos könyvtárakra a rendszergazda kérheti
egy másik fájlrendszer (és így háttértároló) csatlakoztatását.  Ezután
a folyamat után a könyvtár normál tartalma láthatatlanná válik és a
rendszer úgy csinál, mintha a felcsatlakoztatott egység fájlai
lennének ott.  Természetes, hogy hardlinkek nem lehetnek különböző
fájlrendszerek között, hiszen ugyanaz az _ino_ szám ki lehet osztva
mindkét adattárolón.

Szoftlinkeket az _ln -s FORRÁS SYMLINK_ paranccsal hozhatunk létre,
_ls -l_-lel való listázáskor pedig egy nyíl jelzi, hogy ilyen fájlról
van szó.  A _find_ parancs _-type l_ tesztje akkor és csak akkor igaz,
ha a szóban forgó fájl egy szimbolikus link.

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp/x/aldir2$} ls -l
  total 0
  %underline{errge@pandora:~/tmp/x/aldir2$} echo mogyoro >alma
  %underline{errge@pandora:~/tmp/x/aldir2$} ln -s alma korte
  %underline{errge@pandora:~/tmp/x/aldir2$} ls -l
  total 4
  -rw-r--r-- 1 errge progterv 8 2007-10-25 00:45 alma
  lrwxrwxrwx 1 errge progterv 4 2007-10-25 00:46 korte -> alma
  %underline{errge@pandora:~/tmp/x/aldir2$} find -type l
  ./korte
  %underline{errge@pandora:~/tmp/x/aldir2$} cat korte
  mogyoro
  %underline{errge@pandora:~/tmp/x/aldir2$} echo atir >korte
  %underline{errge@pandora:~/tmp/x/aldir2$} cat alma
  atir
  %underline{errge@pandora:~/tmp/x/aldir2$} rm alma
  %underline{errge@pandora:~/tmp/x/aldir2$} ls -l
  total 0
  lrwxrwxrwx 1 errge progterv 4 2007-10-25 00:46 korte -> alma
  %underline{errge@pandora:~/tmp/x/aldir2$} cat korte
  cat: korte: No such file or directory
  %underline{errge@pandora:~/tmp/x/aldir2$} ln -s korte mogyoro
  %underline{errge@pandora:~/tmp/x/aldir2$} ln -s mogyoro alma
  %underline{errge@pandora:~/tmp/x/aldir2$} ls -l
  total 0
  lrwxrwxrwx 1 errge progterv 7 2007-10-25 00:48 alma -> mogyoro
  lrwxrwxrwx 1 errge progterv 4 2007-10-25 00:46 korte -> alma
  lrwxrwxrwx 1 errge progterv 5 2007-10-25 00:48 mogyoro -> korte
  %underline{errge@pandora:~/tmp/x/aldir2$} cat alma
  cat: alma: Too many levels of symbolic links
  %underline{errge@pandora:~/tmp/x/aldir2$} cat korte
  cat: korte: Too many levels of symbolic links
  %underline{errge@pandora:~/tmp/x/aldir2$} cat mogyoro
  cat: mogyoro: Too many levels of symbolic links
\end{ProgalapVerbatim}

Persze mint látható szimbolikus linkekből egy kör is létrehozható, ami
végülis nem hivatkozik semmire.  Az ebből fakadó problémákat úgy
oldották meg, hogy bizonyos számú ``átirányítás'' után a rendszer
magja nem folytatja a fájlnévhez tartozó _inode_ megkeresését, hanem
hibaüzenettel leáll.

Írjunk szkriptet, ami kideríti, hogy mennyi a mi rendszerünkön ez a
limit!
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/tmp$} bash ~/tmp/symlink-limit.sh
  6 melysegu symlink lancot mar nem bir.
  %underline{errge@pandora:~/tmp$}
\end{ProgalapVerbatim}

A szkript létrehoz egy _symlink-teszt_ nevű alkönyvtárat és abban
addig hoz létre nem kört tartalmazó, csak egyre hosszabb láncot, amíg
sikerül a feloldás:

\begin{ProgalapVerbatim}
  #!/bin/bash

  mkdir symlink-teszt
  cd symlink-teszt
  echo tartalom >fajl
  i=1
  ln -s fajl symlink1
  while [ "tartalom" = "$(cat symlink$i 2>/dev/null)" ]
  do
    ln -s symlink$i symlink$(($i+1))
    i=$(($i+1))
  done
  echo "$i melysegu symlink lancot mar nem bir."
  cd ..
  rm -rf symlink-teszt
\end{ProgalapVerbatim}

\subsection{Szimbolikus linkek kibogozása (nehéz)}
A 6 az nem túl sok és az otthoni rendszeremen is csak 9 ez az érték.
Gyakorlásképp írjunk egy másik szkriptet, ami bármekkora láncnak
megkeresi a végét, azaz kimenete az, amire a lánc vége mutat.
Figyeljünk arra, hogy ne kerüljön végtelen ciklusba, csak azért, mert
belefut egy körbe, hanem ekkor illedelmesen közölje (a hibacsatornán),
hogy ez bizony egy kör, így nincs vége és a visszatérési értéke ekkor
1 legyen.

Első gondolatunk az lehetne, hogy megjegyezzük az első szimbolikus
link fájlnevét és utána ha bármikor ezzel találkozunk mégegyszer, az
azt jelenti, hogy körbe jutottunk.  Azonban ez a stratégia csődöt
mond, ha nem a kör közepén indítják el a szkriptet, hanem annak egy
bevezető ``nyelén'', hiszen arra a nyélre már soha nem fogunk
visszajutni és így a végtelenségig kőrözünk.  Szintén rossz ötlet a
fájlnak a nevét megjegyezni, mivel az sokféleképp előfordulhat a
szimbolikus linkekben, a különböző relatív nevekkel
(_../aldir/../aldir/f1_, stb.), valamint a symlinkek közül némelyikek
lehetnek hardlinkek egymásra, akkor sem egyezik a nevük, mégis
ugyanazt a láncszemet jelentik.  Jegyezzük meg inkább az _ino_ számot!

A kör megtalálásához egy helyes stratégiát adott Robert W. Floyd, ez a
sokat eláruló ``a teknős és a nyúl'' nevet
viseli.\footnote{\url{http://en.wikipedia.org/wiki/Cycle_detection}} A
megoldás lényege, hogy elindítjuk a két állatot a startból, de a nyúl
mindig kétszer annyit lép.  Akkor és csak akkor találkoznak újra
(fognak ugyanazon az _ino_ számon tanyázni), ha kör van a
struktúrában.  Egyébként pedig a nyúl eléri a lánc végét és az ott
található fájlnév\footnote{Ami persze lehet nem létező is, de a
  lényeg, hogy már biztosan nem egy létező szoftlink neve.} az
eredmény.

Már most látható, hogy ez az eddigi legkomolyabb shell vállalásunk.
Ilyen algoritmikus problémát talán már nem is kényelmes shellben
megoldani, viszont mivel nagyon szorosan kapcsolódik a feladat az
operációs rendszerhez, valami magasabb szintű programozási nyelven is
nehézségekbe ütköznénk, ugyanis ott annak kiderítése lenne
pl. nehézkes, hogy mi szoftlink és mi nem vagy minek mi az _ino_
száma.

A program bonyolultságának további növekedését elkerülve
feltételezzük, hogy a kérdéses szoftlinkek mind egy könyvtárban
vannak.  A problémát az okozná, hogyha egy link mondjuk a
_../valami_-re mutat, az pedig a _./valami2_-re, akkor a _valami2_
nevű fájlt nyilván a _.._-ban kellene keresni, mi azonban a _._-ban
keresnénk, hiszen a mutatott név a _./valami2_.  Talán a
legegyszerűbben ez úgy orvosolható, ha készítünk egy programot, ami
első paramétereként egy relatív vagy abszolút útvonalat vár és ha
relatívat kap, akkor átalakítja abszolúttá pl. úgy, hogy elé írja a
_pwd_ parancs kimenetét.  Ezt felhasználva már a programban tudjuk
mindig teljes elérési úttal követni a fájlneveket.

\begin{ProgalapVerbatim}[label=\fbox{\texttt{veget-keres.sh}}]
  #!/bin/bash

  if [ "$#" -ne "1" ] || ! [ -h "$1" ]
  then
    echo "Hasznalat: $0 <kibogozando-symlink>"
  fi

  NYUL_F=$1
  TEKNOS_F=$1
  NYUL_INO=$(find "$1" -printf '%%i')
  TEKNOS_INO=$(find "$1" -printf '%%i')

  while true
  do
    # lepjen a nyul 1-et es nezze meg, hogy nincs-e vege
    NYUL_F=$(find "$NYUL_F" -printf '%%l')
    [ -h "$NYUL_F" ] || %{ echo $NYUL_F; exit 0; %}
    NYUL_INO=$(find "$NYUL_F" -printf '%%i')
    # lepjen a nyul meg 1-et es nezze meg, hogy nincs-e vege
    # ez csak az elozo 3 sor copy&paste-je
    NYUL_F=$(find "$NYUL_F" -printf '%%l')
    [ -h "$NYUL_F" ] || %{ echo $NYUL_F; exit 0; %}
    NYUL_INO=$(find "$NYUL_F" -printf '%%i')

    # lepjen a teknos 1-et
    # ez az elozo harom sor s/NYUL/TEKNOS/g csereje a 2. sor nelkul
    TEKNOS_F=$(find "$TEKNOS_F" -printf '%%l')
    TEKNOS_INO=$(find "$TEKNOS_F" -printf '%%i')

    # ha egyezik a ket INO, akkor kor van
    if [ "$TEKNOS_INO" = "$NYUL_INO" ]
    then
      echo kor van >&2
      exit 1
    fi
  done
\end{ProgalapVerbatim}

Stressztesztként létrehoztam egy $999 \rightarrow 998 \rightarrow
\ldots \rightarrow 001$ szoftlink láncot, amit ki is bogoz:
\begin{ProgalapVerbatim}
  errge@pandora:~/tmp/x/aldir2/x$ bash ../veget-keres.sh 999
  001
  errge@pandora:~/tmp/x/aldir2/x$ ln -s 681 001
  errge@pandora:~/tmp/x/aldir2/x$ bash ../veget-keres.sh 999
  kor van
\end{ProgalapVerbatim}
%$buta emacs

Ha esetleg megoldjuk, hogy a könyvtárak közötti szoftlinkek is
működjenek, akkor figyeljünk arra, hogy könnyen átléphetünk egy
fájlrendszer határt és ekkor ha hatalmas pechünk van, akkor két _ino_
szám akkor is egyezhet, ha azok egymástól teljesen független
fájlrendszeren lévő, csak azonos számmal rendelkező fájlok.  Ez ellen
úgy védekezhetünk, hogy a \texttt{TEKNOS\_INO} és \texttt{NYUL\_INO}
környezetváltozókban a _find_ _%D:%i_ formájú _printf_ eredményét
írjuk.  A _%d_ egy olyan szám, ami adattárolónként biztosan különböző
és így ők ketten kettősponttal elválasztva már biztosan egy teljesen
egyedi azonosítót képeznek az operációs rendszer minden fájlára.

\subsection{FIFO-k, eszközfájlok, socketek}
A unix rendszerben majdnem minden ábrázolható fájlként, ezért még
további speciális fájltípusok is vannak, amelyeknek ismerete nem
szükséges egyetlen felhasználó számára sem.  Azonban minden
rendszergazdának tisztában kell lennie ezekkel a fogalmakkal, nélkülük
nem boldogulhat.

\section{Processzek}
...

\section{Web automatizáció}
Egyre több feladatot intézünk egyetlen webböngésző segítségével.  Így
érhetjük el a menetrendeket, a közösségi oldalakon az üzeneteinket,
így kereshetünk a weben a googlevel, így olvashatjuk a wikipediat.
Sokan az elektronikus levelezésüket sem valamely erre a célra
kifejlesztett programmal, mint pl. a GNU/Emacs Gnus moduljával
intézik, hanem a rendszergazda által telepített webmailt használják
inkább.

Ez a megközelítés egészen addig működőképes, amíg nem szeretnénk a
weblap által nyújtott legegyszerűbb funkcióknál mi egy kicsit többet
vagy mást csinálni.  Például elképzelhető, hogy le szeretnénk menteni
az üzeneteinket, hogy mindig meg legyenek, akkor is, ha nincs
internet, vagy egy képgalériát nem a böngészőben szeretnénk átlapozni,
hanem az egészet letöltenénk és a repülőn a laptopunkon majd egy
rendes képnézegetőprogrammal megnéznénk az összes képet.

Ezeket az igényeket az oldalak üzemeletetői nem szokták kielégíteni,
mivel egyrészt számukra némelyik üzletrontó lenne, némelyikre pedig
egyszerűen nincs igény (ki törődik manapság azzal, ha az informatika
forgatagában elveszik pár levele?).

Némi shell és HTML (szélsőséges esetben Javascript) tudással és a
_wget_ program felhasználásával azonban mi magunk megoldhatjuk ezeket
a feladatokat.  Pár példán keresztül bemutatjuk, hogy hogyan.

\subsection{\texttt{wget}}
Újabban megtalálható a GNU/Linux rendszereken egy _wget_ nevű
segédprogram, amivel a weben megtalálható fájlok letöltése végezhető
el.  Például ezen jegyzet aktuális példányát letölti a %buta latex
_wget http://www.gergely.risko.hu_ parancs az aktuális könyvtárba.

\begin{ProgalapVerbatim}
  %underline{errge@home:~$} wget http://www.gergely.risko.hu/progalap1/jegyzet.pdf
  --23:18:30--  http://www.gergely.risko.hu/progalap1/jegyzet.pdf
  => `jegyzet.pdf'
  Resolving www.gergely.risko.hu... 62.112.193.66
  Connecting to www.gergely.risko.hu|62.112.193.66|:80... connected.
  HTTP request sent, awaiting response... 200 OK
  Length: 621,071 (607K) [application/pdf]

  100%%[==========================================================>] 621,071      950.58K/s

  23:18:31 (945.29 KB/s) - `jegyzet.pdf' saved [621071/621071]

  %underline{errge@home:~$} rm jegyzet.pdf
  %underline{errge@home:~$} wget -q http://www.gergely.risko.hu/progalap1/jegyzet.pdf
  %underline{errge@home:~$} ls -l jegyzet.pdf
  -rw-r--r-- 1 risko risko 621071 Oct 25 09:24 jegyzet.pdf
\end{ProgalapVerbatim}

A letöltés közben mindenféle üzeneteket ír ki (a standard
hibacsatornára), amiket a _-q_ opcióval letilthatunk.  A _-O célfájl_
opcióval beállítható a kimeneti fájl, illetve célfájlként a _-_
karaktert megadva a standard kimenetre írja a letöltött fájlt a
_wget_.  Más opciók megadásával elérhető pl. az is, hogy teljes
webstruktúrákat letöltsünk (a linkek automatikus követésével), a man
oldal most is bő információforrás.

\subsection{Google Fight}
A \url{http://www.googlefight.com/} oldalon ha megadunk két kulcsszót,
akkor az oldal mindkettőre rákeres a Googlén és megmutatja nekünk,
hogy melyikhez mennyi találat volt.  Kissé igénytelenül, de
használhatjuk ezt a szolgáltatást arra, hogy eldöntsünk egyszerű
helyesírási kérdéseket, pl., hogy az ``enthusiastic'' szóban valóban
van-e h betű.  Ugyanis feltehetjük, hogy az emberek többsége helyesen
írja ezt a szót, és így az elterjedtebb írásmód lesz a helyes.  Persze
némely esetben az ilyen gondolkodás tévútra is vihet.

Talán kicsit érdekesebb felhasználása ennek a weboldalnak annak
eldöntése, hogy vajon az interneten Newtonról vagy Einsteinről írnak
többet.

Mindenesetre valósítsuk meg ezt a szolgáltatást shell scriptben:

\begin{ProgalapVerbatim}
  %underline{errge@pandora:~$} ./google-fight.sh vajon valyon
  3420000:vajon
  82200:valyon
  vajon nyert
  %underline{errge@pandora:~$} ./google-fight.sh Newton Einstein
  62400000:Newton
  41100000:Einstein
  Newton nyert
\end{ProgalapVerbatim}

Először is azt kell ehhez észrevennünk, a webböngészőnkben
próbálkozva, hogy a Googlének a
\url{http://www.google.com/search?q=newton} URL-t kell megadni ahhoz,
hogy a ``newton'' sztringre keressen, az eredményoldalon pedig
látszik, hogy hány találat van.  Mentsük le ezt a példát a %buta latex
_wget -O proba-google http://www.google.com/search?q=newton_
paranccsal.  Az első probléma, amivel szembesülünk, hogy a _wget_
hibát ad vissza, ugyanis a Google nem szeretné, ha így használnák a
keresés funkciót, mivel magasszintű programozási nyelvekből
köny\-nyebben kezelhető interfészt is biztosítanak az automatikus
keresésekhez.  A weben minden letöltés alkalmával elküldik a
böngészők, hogy pontosan milyen milyen operációs rendszeren, milyen
verziójuk fut.  Ezt használja ki a Google, ha meglátja, hogy mi a
_wget_-tel próbálkozunk, akkor megtagadja a lekérdezést.  Szerencsére
a _-U ""_ opcióval utasíthatjuk a _wget_-et, hogy ne küldjön ilyen
információkat.  Töltsük így le a keresés eredményét és vegyük
szemügyre a létrejövő _proba-google_ nevű fájlt!

A következő feladatunk, hogy kitalálunk olyan parancssorozatot, ami
kinyeri a szükséges számértéket ebből a fájlból.  Ez a rész
természetesen feladatonként nagyon eltérő, de a tanult unix eszközök
alkalmazásával mindig célt érhetünk.  Jelen esetben:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~$} cat proba-google | sed 's/<font size=-1>/\n/g' | grep '^Results' \
    | cut -d\> -f 6 | cut -d\< -f1
  62,200,000
  %underline{errge@pandora:~$} cat proba-google | sed 's/<font size=-1>/\n/g' | grep '^Results' \
    | cut -d\> -f 6 | cut -d\< -f1 | sed s/,//g
  62200000
\end{ProgalapVerbatim}

Vegyük észre, hogy gyakorlatilag készen vagyunk, már csak meg kell
írni a szkriptet, ami mindezt összefogja, paramétereket dolgoz fel és
ellenőríz és segítséget ad, valamint megállapítja, hogy melyik szám a
nagyobb és győztest hirdet.

\begin{ProgalapVerbatim}[label=\fbox{\texttt{google-fight.sh}}]
  #!/bin/bash

  if [ "$#" -ne 2 ]
  then
    echo "Usage: $0 <string1> <string2>" >&2
    exit 1
  fi

  # az elso sztring lekerese, a talaltok szamanak kibanyaszasa
  A=$(wget -q -O - -U xxx "http://www.google.com/search?q=$1" | sed 's/<font size=-1>/\n/g' \
    | grep '^Results' | cut -d\> -f 6 | cut -d\< -f1 | sed s/,//g)
  # a masodik sztring
  B=$(wget -q -O - -U xxx "http://www.google.com/search?q=$2" | sed 's/<font size=-1>/\n/g' \
    | grep '^Results' | cut -d\> -f 6 | cut -d\< -f1 | sed s/,//g)

  # ha valamelyikre nincs talalat, akkor legyen 0
  if [ "$A" = "" ]; then A="0"; fi
  if [ "$B" = "" ]; then B="0"; fi

  # megjelenites
  echo "$A:$1"
  echo "$B:$2"

  if [ $A -gt $B ]
  then
    echo "$1 nyert"
  elif [ $A -lt $B ]
  then
    echo "$2 nyert"
  else
    echo "Dontetlen"
  fi
\end{ProgalapVerbatim}

Gondolkodjunk el azon, hogy hogyan tudnánk megoldani, hogy több
paramétert is meg lehessen adni, bármennyit.  Mindegyikre keressünk rá
és írjuk ki az értékeket hozzájuk, majd adjuk meg a vesztest és a
győztest!

Látható, hogy ezek a megoldások bizonyos szempontból nagyon szörnyűek.
Ugyanis ha a Google egy kicsit megváltoztatja azt, ahogy a keresés
HTML eredménye kinéz, akkor a programunk már nem működik.  És miért ne
változtatná, hiszen ő nem számít arra, hogy mi ezt programból
használjuk.  Kicsit javíthatunk a helyzeten, ha a html-ekből
információt (a pontszámot) kigyűjtő parancssort külön fájlba tesszük
és a _google-fight.sh_-ból csak használjuk.  Ugyanis ekkor külön
tudjuk tesztelni és ``fejleszteni'' azt a részt, amennyiben valami nem
működne.  A programunk többi része, ha már egyszer az a belső hosszú
parancs jó értéket ad vissza, nem kell, hogy változzon.

\subsection{Esnips képletöltés}
Kicsit bonyolultabb a helyzet akkor, ha az automatizálandó letöltés
személyünkhöz kötött (pl. jelszóval be kell lépni).  Ezek az oldalak
úgy működnek, hogy elhelyeznek egy ún. cookie-t (egy nagyon nagy
véletlen számot) a böngészőnkben és ahogy újabb képekre kattintunk, az
a cookie mindig visszaküldésre kerül.  Így a szerver tudja
ellenőrízni, hogy mi valóban jogosultak vagyunk a kép letöltésére,
hiszen birtokában vagyunk egy olyan cookienak, amit csak úgy
kaphattunk meg, ha korábban már beírtuk a jelszavunkat.

A \url{www.esnips.com} esetében nem felhasználói névvel és jelszóval
kell belépni, hanem emailben szoktam URL-eket kapni azoktól az
emberektől, akik meg akarják mutatni egy albumokat.  Ez a ``meghívó''
webcím, amit az esnips legyárt és emailben elküld nekem csak az adott
albumhoz ad hozzáférést.

A _wget_-nek vannak olyan opciói, amiket, ha megadjunk, akkor az
említett cookiek kilépéskor elmentésre kerülnek egy fájlba, majd
újboli elindításkor pedig betöltésre és így a weboldal úgy érzi,
mintha egy böngésző megjegyezte volna őket, ahogy azt eredetileg
kitalálták.

Az esnips képletöltést végző program kommentekkel ellátva:
\begin{ProgalapVerbatim}
  #!/bin/bash

  [ "$1" = "" ] && %{
    echo download URL must be given
    exit 1
  %}

  # fuggveny, ami a kis kepes listabol megcsinalja a nagy kepek letolto parancsait
  listimgs () %{
    # kikiserletezes es gondolkodas eredmenye
    # ha az esnips valtozik, ez elromlik
    cat tmp.html | fgrep /doc/ | grep CommandLink | cut -d"'" -f4-6 | \
    sed "s,^/doc/\(.*\)/\(.*\)'.*,wget $MY_WGET -O \2.jpg http://www.esnips.com/nsdoc/\1,"
  %}

  URL=$1
  # ezek az opciok kellenek ahhoz, hogy megtartsa a cookiekat a cook.txt-ben
  MY_WGET="--keep-session-cookies --load-cookies cook.txt --save-cookies cook.txt"

  # ha mar a NEXTPAGE ures lesz, akkor ez a feltetel igaz
  while [ "http://www.esnips.com/" != "$URL" ]
  do
    # letoltes atmeneti fajlba
    wget $MY_WGET -O tmp.html $URL
    # lefuttatjuk az aktualis oldal kepletolto parancsait
    listimgs | bash
    # ez a grep es cut csak akkor ad vissza valamit, ha meg van az oldalon Next
    # link, azaz ez nem az utolso lap a kepgaleriabol
    NEXTPAGE=$(cat tmp.html | grep Next | cut -d"'" -f2)
    # a kovetkezo lap URL-je igy kaphato
    URL=http://www.esnips.com/$NEXTPAGE
  done
  rm tmp.html cook.txt
\end{ProgalapVerbatim}


Csak 18 kódsor és mégis működik:

\begin{ProgalapVerbatim}
  %underline{errge@home:~$} cat esnipsget.sh | grep -v '^$' | grep -v '#.*' | wc -l
  18
  %underline{errge@home:~$} chmod a+x esnipsget.sh
  %underline{errge@home:~$} ./esnipsget.sh \
    'http://www.esnips.com/fm/7f85391b-f3a3-4648-af78-3a0b315134f4/?v=458337&source=ws'
  ...
  %underline{errge@home:~$} ls
  DSC_4833.jpg  DSC_4845.jpg  DSC_4854p1.jpg  DSC_4863.jpg  DSC_4873.jpg  DSC_4882.jpg
  DSC_4834.jpg  DSC_4846.jpg  DSC_4854p.jpg   DSC_4864.jpg  DSC_4874.jpg  DSC_4884.jpg
  DSC_4835.jpg  DSC_4847.jpg  DSC_4855.jpg    DSC_4865.jpg  DSC_4875.jpg  esnipsget.sh
  DSC_4836.jpg  DSC_4848.jpg  DSC_4856.jpg    DSC_4866.jpg  DSC_4876.jpg
  DSC_4838.jpg  DSC_4849.jpg  DSC_4858.jpg    DSC_4867.jpg  DSC_4877.jpg
  DSC_4839.jpg  DSC_4850.jpg  DSC_4859.jpg    DSC_4869.jpg  DSC_4878.jpg
  DSC_4842.jpg  DSC_4852.jpg  DSC_4860.jpg    DSC_4870.jpg  DSC_4879.jpg
  DSC_4843.jpg  DSC_4853.jpg  DSC_4862.jpg    DSC_4871.jpg  DSC_4881.jpg
\end{ProgalapVerbatim}

\subsection{IWIW üzenetek archiválása}
Lássuk, hogyan lehetne az IWIW-es üzeneteinket letölteni és emészthető
formában kiírni!  Először határozzuk el, hogy milyen formátumot
szeretnénk kapni a weben való olvasgatás helyett!

\begin{ProgalapVerbatim}
  From iwiw
  From: feladó
  Date: dátum
  Subject: az üzenet tárgya

  Az üzenet szövege

  From iwiw
  From: a 2. üzenet feladója
  Date: hozzá a dátum
  Subject: ...
  ...
\end{ProgalapVerbatim}

Ebben a formátumban az a pláne, hogyha a kiadjuk a %buta latex
_pine -f /h/.../iwiwfajl -i_ parancsot, akkor pine-ból olvashatjuk az
iwiw-ről letöltött üzeneteinket.  Ezt a levéltárolási formát Berkley
mailboxoknak, vagy röviden
mbox\footnote{\url{http://en.wikipedia.org/wiki/Mbox}}-oknak nevezik.

Első tevékenységként be kellene lépnünk az iwiw-re _wget_-tel, hogy
megkapjuk a cookiet, amivel később az leveleket egyesével lekérhetjük.
Az, hogy ezt hogy csináljuk természetesen teljesen iwiw specifikus,
megnézve a belépő oldal forrását, azt látjuk, hogy ún. POST kéréssel
kell belépni.  Erre a _wget_ _--post-data_ kapcsolója ad lehetőséget,
ahol az adatot úgy kell megadni, hogy minden mezőt a _&_, a mező nevét
és értékét pedig az _=_ választja el.\footnote{Az, hogy ez miért van
  így, messze túl mutatna a jegyzet keretein, HTML specin, vagy
  tetszőleges szakirodalom felhasználásával megtanulhatjuk mindezt.}

A szkriptünk eleje tehát így néz ki:
\begin{ProgalapVerbatim}[label=\fbox{\texttt{iwiw-letolt.sh}}]
  #!/bin/bash

  EMAIL=idekell@beirniazemail.cimet
  PASSWORD=idekellirniajelszot

  wget -q -O /dev/null --keep-session-cookies --save-cookies cookies.txt \
    --post-data="email=$EMAIL&password=$PASSWORD" \
    'http://www.iwiw.hu/pages/user/login.jsp?method=Login'
\end{ProgalapVerbatim}

Lássuk, hogy lehetne kinyerni azt, hogy hány oldalon keresztül
listázza a wiw a leveleinket:
\begin{ProgalapVerbatim}
  %underline{errge@pandora:~/iwiwget$} chmod a+x iwiw-letolt.sh
  %underline{errge@pandora:~/iwiwget$} ./iwiw-letolt.sh
  %underline{errge@pandora:~/iwiwget$} cat cookies.txt
  www.iwiw.hu     FALSE   /pages/main/    FALSE   1214309909      lastHit 1194309909160
  www.iwiw.hu     FALSE   /pages/user/    FALSE   1214309909      httpslogin      0
  www.iwiw.hu     FALSE   /pages/user/    FALSE   1214309909      email   iwiw@risko.hu
  www.iwiw.hu     FALSE   /pages/user/    FALSE   1214309909      forgetEmail     0
  www.iwiw.hu     FALSE   /pages/user/    FALSE   1209861909      autoLoginLimited        0
  www.iwiw.hu     FALSE   /       FALSE   0       JSESSIONID      1
  www.iwiw.hu     FALSE   /       FALSE   0       cachedSID       1194309909119_...
  %underline{errge@pandora:~/iwiwget$} wget -q --load-cookies cookies.txt -O - \
    'http://www.iwiw.hu/pages/message/inbox.jsp?page=0'  | grep pg_selector | \
    sed 's/page=/\n/g' | tail -n1 | cut -d'"' -f1
  4
\end{ProgalapVerbatim}

Tehát egy egyszerű _for_ és _seq_ kombinációval végig fogunk tudni
menni az oldalakon, amiken az üzeneteink listázva vannak.  Ez a lista
úgy néz ki, ha megtekintjük az oldal forrását, hogy minden üzenetnek
van egy azonosítószáma, amire linkeket generál a szerver.  Tehát
nekünk ezeket a linkeket kell kiválogatni és az előző példa képeihez
hasonlóan letölteni őket.  Ha ez készen van, akkor már csak egy kis
formai átalakítás lesz hátra, hogy mbox formátumot kapjunk.

Lássuk tehát azt a _for_ ciklust, ami végigmegy az oldalakon és minden
oldalon letölti az ott található üzeneteket (mindegyiket a saját
_sorszáma.html_ nevű fájlba):

\begin{ProgalapVerbatim}
  PAGES=$(wget $MY_WGET -q -O - 'http://www.iwiw.hu/pages/message/inbox.jsp?page=0' \
    | grep pg_selector | sed 's/page=/\n/g' | tail -n1 | cut -d'"' -f1)

  if [ "$PAGES" = "" ]; then PAGES=0; fi

  OLVASURL=http://www.iwiw.hu/pages/message/messageread.jsp
  for i in $(seq 0 $PAGES)
  do
    wget $MY_WGET -q -O - "http://www.iwiw.hu/pages/message/inbox.jsp?page=$i" \
      | tr '?' '\n' | grep messageID | sed 's/.*messageID=//' | \
      sed "s,\([0-9]*\).*,wget -O \1.html $MY_WGET '$OLVASURL?messageID=\1',"
  done | bash
\end{ProgalapVerbatim}
%$buta emcas

Készen van tehát az a programunk, ami egy könyvtárban létrehozza az
összes _.html_ fájlt, ami a leveleinket tartalmazza.  Foglaljuk ezt
össze és csináljuk meg, hogy létrehozzon egy _tmp_ nevű könyvtárat
kezdetben és oda szemeteljen:

\begin{ProgalapVerbatim}
  #!/bin/bash

  if [ -e tmp ]
  then
    echo "Mar letezik tmp nevu fajlbejegyzes"
    exit 1
  else
    mkdir tmp
    cd tmp
  fi

  EMAIL=usernev
  PASSWORD=jelszo
  MY_WGET="--keep-session-cookies --load-cookies cook.txt --save-cookies cook.txt"

  echo "Logging in..."
  wget -q -O /dev/null $MY_WGET --post-data="email=$EMAIL&password=$PASSWORD" \
    'http://www.iwiw.hu/pages/user/login.jsp?method=Login'

  echo "Getting number of pages..."
  PAGES=$(wget $MY_WGET -q -O - 'http://www.iwiw.hu/pages/message/inbox.jsp?page=0' \
    | grep pg_selector | sed 's/page=/\n/g' | tail -n1 | cut -d'"' -f1)
  if [ "$PAGES" = "" ]; then PAGES=0; fi

  echo "Getting message list..."
  OLVASURL=http://www.iwiw.hu/pages/message/messageread.jsp
  for i in $(seq 0 $PAGES)
  do
    wget $MY_WGET -q -O - "http://www.iwiw.hu/pages/message/inbox.jsp?page=$i" \
      | tr '?' '\n' | grep messageID | sed 's/.*messageID=//' \
      | sed "s,\([0-9]*\).*,wget -O \1.html $MY_WGET '$OLVASURL?messageID=\1',"
  done | bash
\end{ProgalapVerbatim}

Végül hozzuk valamennyire mbox formátumra a html fájlokat:
\begin{ProgalapVerbatim}
  for i in tmp/*.html
  do
    from=$(cat $i | tr -d '\n' | sed 's/.*class="">//' |  cut -d\< -f1)
    subject=$(cat $i | tr -d '\n' | sed 's/.*T.ma: <\/span>[ \t]*//' | cut -d\< -f1)
    date=$(cat $i | tr -d '\n' | sed 's/.*d.tuma: <\/span>[ \t]*//' | cut -d\< -f1)

    echo "From iwiw Thu Jan  1 00:00:00 CET 1970"
    echo "From: $from"
    echo "Date: $date"
    echo "Subject: $subject"
    echo
    cat $i | grep -A1 'caption">.zenet sz.vege' | tail -n1 | links -dump
    echo
    echo
  done
\end{ProgalapVerbatim}

Nem részletezzük ennek magyarázatát, több okból:
\begin{itemize}
\item mire az olvasóhoz jut ez a kód, valószínűleg az iwiwesek már
  változtattak valamit és az egész nem is működik,
\item az összes létező mboxokra és emailekre vonatkozó szabványt
  megszegi, amit csak lehet, nem foglalkoztunk az ékezetekkel, a
  dátumok átalakításával,
\item a Pine ezért elég hibásan, de kezeli az eredményt, a koncepció
  életképessége látható,
\item a szöveges böngésző (_links_) dump parancsa minden képet lenyel,
  így a levelek egy jó része, a mosolygók, az ajándék szó, az iwiw
  szó, meg minden, amit az okos wiwesek képekkel helyettesítenek
  elveszik,
\item végül ez a részfeladat valószínűleg sokkal korrektebbül
  megoldható valami olyan nyelven, ahol mboxok, dátumok, emailek és a
  különböző karakterkonverziók (ékezetek) kezelésére kész
  programmodulok vannak.
\end{itemize}

Az is egy megoldás lehet, hogy ezt az utolsó lépést nem csináljuk,
meghagyjuk a leveleinket htmlként, és egyszerűen webböngészővel
nézegetjük a html-eket és így a wiwes leveleinket.

Mindezen problémák jól mutatják, hogy az ebben a fejezetben ismeretett
vészmegoldásokat igazából sosem szabad használni, hanem az adat
tulajdonosát rá kell venni, hogy a szóban forgó adatot normális
külalakkal szolgáltassa.  A fényképeket egy nagy zip fájlban is az
album mellett, a wiw pedig biztosítson megoldást a tulajdon leveleink
archiválására.  Sajnos amíg ebben ellenérdekeltek és ez
feloldhatatlan, addig is jobban járhatunk az ilyen félmegoldásokkal,
mintha egyszerűen lemondunk pl. a biztonsági mentésről, a leveleink
_grep_-pel való kereshetőségéről, az offline elérhetőségről és a többi
előnyről, amit a web, mint platform mind elvesz tőlünk.

% adjuk meg a recept.txt leghosszabb szavat

% todo: beepitett parancsok, hogyan lehet megtudni, hogy valami beepitett-e

% notes: a pipeline minden resze kulon subshell, akkor mi legyen a valtozokkal?

\end{document}

