103.4 Lecke 2
Tanúsítvány: |
LPIC-1 (101) |
---|---|
Verzió: |
5.0 |
Témakör: |
103 GNU és Unix parancsok |
Fejezet: |
103.4 Folyamok, csövek és átirányítások használata |
Lecke: |
2/2 |
Bevezetés
A Unix filozófia egyik aspektusa szerint minden programnak meghatározott céllal kell rendelkeznie, és nem szabad megpróbálnia a hatáskörén kívül eső funkciókat beépíteni. Az egyszerűség azonban nem jelenti azt, hogy a dolgok kevésbé kidolgozott eredményekkel járnak, mivel különböző programok összekapcsolhatók, hogy egy kombinált kimenetet állítsanak elő. A függőleges vonal karakter |
, más néven a pipe szimbólummal létrehozható egy olyan csővezeték, amely egy program kimenetét közvetlenül egy másik program bemenetéhez kapcsolja, míg a parancshelyettesítés (command substitution) lehetővé teszi, hogy egy program kimenetét egy változóban tároljuk, vagy közvetlenül egy másik parancs argumentumaként használjuk.
Csővezetékek
Az átirányítással ellentétben a csövek (pipe-ok) esetében az adatok balról jobbra haladnak a parancssorban, és a cél egy másik processz, nem pedig egy fájlrendszeri elérési útvonal, fájlleíró vagy Here document. A pipe karakter |
azt mondja a shellnek, hogy minden különböző parancsot egyszerre indítson el, és az előző parancs kimenetét balról jobbra haladva kösse össze a következő parancs bemenetével. Például az átirányítások használata helyett a cat
által a standard kimenetre küldött /proc/cpuinfo
fájl tartalmát a következő paranccsal a wc
stdin-jébe vezethetjük:
$ cat /proc/cpuinfo | wc 208 1184 6096
A fájl elérési útvonalának hiányában a wc
a sorok, szavak és karakterek számát számolja, amit az stdin-en kap, ahogy a példában is. Egy összetett parancsban több csővezeték is lehet. A következő példában két csővezetéket használunk:
$ cat /proc/cpuinfo | grep 'model name' | uniq model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
A cat /proc/cpuinfo
parancs által előállított /proc/cpuinfo
fájl tartalmát a grep 'model name'
parancshoz továbbítottuk, amely csak a model name
kifejezést tartalmazó sorokat választja ki. A példát futtató gépen sok CPU van, ezért többször is vannak model name
tartalmú sorok. Az utolsó cső a grep 'model name'-t a `uniq
parancshoz köti, amely az előzővel megegyező sorok kihagyásáért felelős.
A csővezetékek kombinálhatók átirányításokkal ugyanabban a parancsban. Az előző példa átírható egyszerűbb formára:
$ grep 'model name' </proc/cpuinfo | uniq model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
A grep
bemeneti átirányítása nem feltétlenül szükséges, mivel a grep
elfogadja a fájl elérési útvonalát argumentumként, de a példa jól szemlélteti, hogyan lehet ilyen kombinált parancsokat létrehozni.
A csövek és az átirányítások kizárólagosak, azaz egy forrás csak egy célhoz rendelhető hozzá. Mégis lehetséges egy kimenetet átirányítani egy fájlba, és láthatjuk azt a tee
segítségével a képernyőn. Ehhez az első program a kimenetét a tee
stdin-jébe küldjük, és az utóbbinak megadunk egy fájlnevet az adatok tárolásához:
$ grep 'model name' </proc/cpuinfo | uniq | tee cpu_model.txt model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz $ cat cpu_model.txt model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
A lánc utolsó, uniq
által generált programjának kimenete megjelenik, és a cpu_model.txt
fájlban tárolódik. Ahhoz, hogy a megadott fájl tartalmát ne írjuk felül, hanem adatokat csatoljunk hozzá, a tee
-nek meg kell adni az -a
opciót.
Egy csővezeték csak a processz standard kimenetét rögzíti. Tegyük fel, hogy egy hosszú fordítási folyamatot kell végigvinnünk a képernyőn, és ugyanakkor a standard kimenetet és a standard hibát is el kell mentenünk egy fájlba későbbi ellenőrzés céljából. Feltételezve, hogy az aktuális mappánkban nincs Makefile, a következő parancs hibát fog kiadni:
$ make | tee log.txt make: *** No targets specified and no makefile found. Stop.
Bár a képernyőn megjelenik, a make
által generált hibaüzenetet a tee
nem rögzítette, és a log.txt fájl üresen jött létre. Egy átirányítást kell végrehajtani, mielőtt a pipe rögzíteni tudja az stderr-t:
$ make 2>&1 | tee log.txt make: *** No targets specified and no makefile found. Stop. $ cat log.txt make: *** No targets specified and no makefile found. Stop.
Ebben a példában a make
stderr-je át lett irányítva az stdout-ra, így a tee
képes volt azt egy csővezeték segítségével rögzíteni, megjeleníteni a képernyőn és elmenteni a log.txt
fájlba. Ilyen esetekben hasznos lehet a hibaüzeneteket elmenteni a későbbi ellenőrzéshez.
Parancshelyettesítés
Egy másik módszer egy parancs kimenetének rögzítésére a command substitution (parancshelyettesítés). Ha egy parancsot backtickek (aposztrófok) közé helyezünk, a Bash helyettesíti azt a standard kimenetével. A következő példa azt mutatja, hogyan használhatjuk egy program stdoutját egy másik program argumentumaként:
$ mkdir `date +%Y-%m-%d` $ ls 2019-09-05
A date
program kimenete, az aktuális dátum év-hónap-nap formátumban, az mkdir
programmal történő mappa létrehozásához használt argumentum volt. Ugyanilyen eredményt kapunk, ha a $()
-t használjuk az backtickek helyett:
$ rmdir 2019-09-05 $ mkdir $(date +%Y-%m-%d) $ ls 2019-09-05
Ugyanez a módszer használható egy parancs kimenetének változóként való tárolására is:
$ OS=`uname -o` $ echo $OS GNU/Linux
Az uname -o
parancs kiadja az aktuális operációs rendszer általános nevét, amelyet az OS
munkamenetváltozóban tároltunk. Egy parancs kimenetének változóhoz rendelése nagyon hasznos a szkriptekben, lehetővé téve az adatok sokféleképpen történő tárolását és kiértékelését.
A helyettesített parancs által generált kimenettől függően előfordulhat, hogy a beépített parancshelyettesítés nem megfelelő. Kifinomultabb módszer egy program kimenetének egy másik program argumentumaként való felhasználására a xargs
nevű köztes eszközt használja. A xargs
program az stdin-en keresztül kapott tartalmat használja fel egy adott parancs futtatására, amelynek a tartalma az argumentuma. A következő példában az xargs
a identify
programot futtatja a find
program által megadott argumentumokkal:
$ find /usr/share/icons -name 'debian*' | xargs identify -format "%f: %wx%h\n" debian-swirl.svg: 48x48 debian-swirl.png: 22x22 debian-swirl.png: 32x32 debian-swirl.png: 256x256 debian-swirl.png: 48x48 debian-swirl.png: 16x16 debian-swirl.png: 24x24 debian-swirl.svg: 48x48
Az identify
program az ImageMagick része, amely egy parancssori eszközkészlet a legtöbb képfájltípus vizsgálatára, konvertálására és szerkesztésére. A példában az xargs
a find
által felsorolt összes elérési utat argumentumként adta meg az identify
-nak, amely ezután minden egyes fájlhoz a -format
kapcsoló által előírt módon formázott információt jelenít meg. A példában a find
által talált fájlok a Debian fájlrendszerben lévő disztribúciós logót tartalmazó képek. A -format
az identify
paramétere, nem az xargs
-é.
Az -n 1
opció a xargs
-t arra utasítja, hogy az adott parancsot egyszerre csak egy argumentummal futtassa. A példa esetében ahelyett, hogy a find
által talált összes elérési utat átadnánk az identify
-nak argumentumlistaként, az xargs -n 1
használatával az identify
parancsot minden egyes elérési útra külön-külön hajtjuk végre. Az -n 2
használata az identify
parancsot két útvonallal, az -n 3
parancsot három útvonallal és így tovább. Hasonlóképpen, amikor a xargs
többsoros tartalmakat dolgoz fel — mint a find
által megadott input esetében — a -L
opcióval korlátozható, hogy hány sor kerüljön argumentumként felhasználásra egy parancs végrehajtásakor.
Note
|
A |
Ha az elérési utak szóközöket tartalmaznak, fontos, hogy a find
-t a -print0
opcióval futtassuk. Ez az opció utasítja a find
-t, hogy minden egyes bejegyzés között null karaktert használjon, hogy a listát az xargs
helyesen tudja elemezni (a kimenet el lett nyomva):
$ find . -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 du | sort -n
A -0
opció azt mondja a xargs
-nak, hogy a null karaktert kell elválasztóként használni. Így a find
által megadott fájl elérési útvonalakat helyesen elemzi, még akkor is, ha üres vagy más speciális karaktereket tartalmaznak. Az előző példa azt mutatja, hogy a du
paranccsal hogyan lehet minden megtalált fájl lemezhasználatát megtudni, majd az eredményeket méret szerint rendezni. A tömörség kedvéért a kimenetet elnyomtuk. Vegyük figyelembe, hogy minden keresési feltételhez szükséges a find
-hez a -print0
kapcsolót megadni.
Alapértelmezés szerint az xargs
a végrehajtott parancs argumentumait az utolsó helyre helyezi. Ennek a viselkedésnek a megváltoztatásához a -I
opciót kell használni:
$ find . -mindepth 2 -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 -I PATH mv PATH ./
Az utolsó példában a find
által talált minden fájl az aktuális mappába kerül. Mivel a forrás elérési útvonal(aka)t a cél elérési útvonal előtt közölni kell az mv
-vel, az xargs
opció -I
-jének adunk egy helyettesítő kifejezést, amely aztán megfelelően az mv
mellé kerül. Mivel a null karaktert használjuk elválasztóként, a helyettesítő kifejezést nem szükséges idézőjelekkel körülvenni.
Gyakorló feladatok
-
Praktikus az automatizált szkriptek által végrehajtott műveletek végrehajtási dátumának elmentése. A
date +%Y-%m-%d
parancs az aktuális dátumot mutatja év-hónap-nap formátumban. Hogyan lehet egy ilyen parancs kimenetét egyTODAY
nevű shell változóban tárolni parancshelyettesítéssel? -
Hogyan küldhetjük el a
TODAY
változó tartalmát azecho
parancs segítségével ased s/-/./g
parancs standard bemenetére? -
Hogyan lehet a
date +%Y-%m-%d%d
parancs kimenete használható Here stringként ased s/-/./g
parancshoz? -
A
convert image.jpeg -resize 25% small/image.jpeg
parancs létrehozza aimage.jpeg
egy kisebb változatát, és az így kapott képet egy hasonló nevű fájlban helyezi el asmall
almappában. Azxargs
használatával hogyan lehet ugyanazt a parancsot végrehajtani afilelist.txt
fájlban felsorolt minden képre?
Gondolkodtató feladatok
-
Egy egyszerű mentési rutin rendszeresen létrehoz egy imaget a
/dev/sda1
partícióról add < /dev/sda1 > sda1.img
címmel. A későbbi adatintegritás-ellenőrzések elvégzéséhez a rutin egy SHA1 hash-t is generál a fájlról asha1sum < sda1.img > sda1.sha1
paranccsal. A csővezetékek és atee
parancs hozzáadásával hogyan lehetne ezt a két parancsot eggyé kombinálni? -
A
tar
parancs sok fájl egyetlen fájlba történő archiválására szolgál, megőrizve a mappaszerkezetet. A-T
opció lehetővé teszi az archiválandó elérési utakat tartalmazó fájl megadását. Például afind /etc -type f | tar -cJ -f /srv/backup/etc.tar.xz -T -T -
egy tömörített tar fájlt hoz létreetc.tar.xz
néven afind
parancs által megadott listából (a-T -
opció a szabványos bemenetet jelöli az elérési útvonalak listájaként). A szóközöket tartalmazó elérési utak miatti esetleges elemzési hibák elkerülése érdekében milyen parancsbeállításoknak kell jelen lenniük afind
és atar
parancsoknál? -
Ahelyett, hogy új távoli shell munkamenetet nyitna, az
ssh
parancs egyszerűen végrehajtja az argumentumként megadott parancsot:ssh user@storage "remote command"
. Mivel azssh
azt is lehetővé teszi, hogy egy helyi program standard kimenetét átirányítsuk a távoli program standard bemenetére, hogyan tudna acat
parancs egy helyietc.tar.gz
nevű fájlt a/srv/backup/etc.tar.tar.gz
-be auser@storage
-nél azssh
-n keresztül átvezetni?
Összefoglalás
Ez a lecke a Linux által alkalmazott hagyományos, folyamatok közötti kommunikációs technikákat tárgyalja. A command pipelining (parancs csővezetékezés) egyirányú kommunikációs csatornát hoz létre két folyamat között, a command substitution (parancshelyettesítés) pedig lehetővé teszi, hogy egy folyamat kimenetét egy shell-változóban tároljuk. A lecke a következő lépéseken ment keresztül:
-
Hogyan lehet a pipes (csővezetékek) segítségével egy folyamat kimenetét egy másik folyamat bemenetére továbbítani.
-
A
tee
ésxargs
parancsok célja. -
Hogyan lehet egy folyamat kimenetét a command substitution (parancshelyettesítés) segítségével rögzíteni, egy változóban tárolni vagy közvetlenül egy másik parancs paramétereként használni.
A tárgyalt parancsok és eljárások a következők voltak:
-
Parancs csővezetékezés a
|
-vel. -
Parancshelyettesítés backtickekkel és a
$()
. -
A
tee
,xargs
és afind
parancsok.
Válaszok a gyakorló feladatokra
-
Praktikus az automatizált szkriptek által végrehajtott műveletek végrehajtási dátumának elmentése. A
date +%Y-%m-%d
parancs az aktuális dátumot mutatja év-hónap-nap formátumban. Hogyan lehet egy ilyen parancs kimenetét egyTODAY
nevű shell változóban tárolni parancshelyettesítéssel?$ TODAY=`date +%Y-%m-%d`
vagy
$ TODAY=$(date +%Y-%m-%d)
-
Hogyan küldhetjük el a
TODAY
változó tartalmát azecho
parancs segítségével ased s/-/./g
parancs standard bemenetére?$ echo $TODAY | sed s/-/./g
-
Hogyan lehet a
date +%Y-%m-%d%d
parancs kimenete használható Here stringként ased s/-/./g
parancshoz?$ sed s/-/./g <<< `date +%Y-%m-%d`
vagy
$ sed s/-/./g <<< $(date +%Y-%m-%d)
-
A
convert image.jpeg -resize 25% small/image.jpeg
parancs létrehozza aimage.jpeg
egy kisebb változatát, és az így kapott képet egy hasonló nevű fájlban helyezi el asmall
almappában. Axargs
használatával hogyan lehet ugyanazt a parancsot végrehajtani afilelist.txt
fájlban felsorolt minden képre?$ xargs -I IMG convert IMG -resize 25% small/IMG < filelist.txt
vagy
$ cat filelist.txt | xargs -I IMG convert IMG -resize 25% small/IMG
Válaszok a gondolkodtató feladatokra
-
Egy egyszerű mentési rutin rendszeresen létrehoz egy képet a
/dev/sda1
partícióról add < /dev/sda1 > sda1.img
címmel. A későbbi adatintegritás-ellenőrzések elvégzéséhez a rutin egy SHA1 hash-t is generál a fájlról asha1sum < sda1.img > sda1.sha1
paranccsal. A csővezetékek és atee
parancs hozzáadásával hogyan lehetne ezt a két parancsot eggyé kombinálni?# dd < /dev/sda1 | tee sda1.img | sha1sum > sda1.sha1
-
A
tar
parancs sok fájl egyetlen fájlba történő archiválására szolgál, megőrizve a mappaszerkezetet. A-T
opció lehetővé teszi az archiválandó elérési utakat tartalmazó fájl megadását. Például afind /etc -type f | tar -cJ -f /srv/backup/etc.tar.xz -T -T -
egy tömörített tar fájlt hoz létreetc.tar.xz
néven afind
parancs által megadott listából (a-T -
opció a szabványos bemenetet jelöli az elérési útvonalak listájaként). A szóközöket tartalmazó elérési utak miatti esetleges elemzési hibák elkerülése érdekében milyen parancsbeállításoknak kell jelen lenniük afind
és atar
parancsoknál?A
-print0
és--null
kapcsolókkal:$ find /etc -type f -print0 | tar -cJ -f /srv/backup/etc.tar.xz --null -T -
-
Ahelyett, hogy új távoli shell munkamenetet nyitna, az
ssh
parancs egyszerűen végrehajtja az argumentumként megadott parancsot:ssh user@storage "remote command"
. Mivel azssh
azt is lehetővé teszi, hogy egy helyi program standard kimenetét átirányítsuk a távoli program standard bemenetére, hogyan tudna acat
parancs egy helyietc.tar.gz
nevű fájlt a/srv/backup/etc.tar.tar.gz
-be auser@storage
-nél azssh
-n keresztül átvezetni?$ cat etc.tar.gz | ssh user@storage "cat > /srv/backup/etc.tar.gz"
vagy
$ ssh user@storage "cat > /srv/backup/etc.tar.gz" < etc.tar.gz