pdf - Środowisko programisty

Transkrypt

pdf - Środowisko programisty
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
System plików
Na poprzednich zajęciach wytłumaczyliśmy jak poruszać się po systemie plików. Warto
jednak mieć świadomośc, że pliki są reprezentowane na dysku w trochę inny sposób.
Podział na partycje
Każdy dysk, na samym początku podzielony jest na partycje. Każda partycja to fragment
wydzielonej przestrzeni dysku, który system operacyjny obsługuje niezależnie od pozostałych
partycji. W ten sposób można na jednym fizycznym dysku zmieścić różne systemy plików
i różne systemy operacyjne.
Istnieją dwa podstawowe reprezentacje partycji:
∗ MBR (Master Boot Record) — wprowadzony w 1983. Informacja MBR zapisana jest
zawsze w pierwszym dostępnym sektorze dysku. Obsługuje dyski do pojemności 2 TB
operując 512-bajtowymi blokami, i standardowo pozwala na stworzenie maksymalnie
czterech partycji. Nowsze wersje pozwalają na przekroczenie tych limitów, ale takie
zabiegi nie są rozpoznawane przez wszystkie systemy operacyjne.
∗ GPT (GUID Partition Table) — wprowadzony z końcem XX wieku, obecnie obsługiwany
przez wszystkie nowsze systemy operacyjne. Informacja GPT też zapisywana jest w
pierwszych dostępnych sektorach dysku. Teoretyczna maksymalna pojemność dysku
obsługiwana przez GPT to 9.4ZB (264 × 512) ale może być zwiększona poprzez
zwiększenie wielkości bloku. GPT pozwala na stworzenie 128 partycji.
System operacyjny Windows przypisuje kolejne litery do kolejnych partycji, poczynając
od litery C. Każda pełna ścieżka zaczyna się od litery reprezentującą którąś z partycji.
Pod linuxem nie ma takiego twardego związku. Partycja systemowa przypisana jest do
katalogu głównego / ale pozostałe mogą być podpięte jako dowolny podkatalog, za pomocą
komendy mount. Linux pozwala też mountowanie innych partycji, także wyjmowalnych
lub połączonych przez sieć. Wpisując mount bez argumentów możemy zobaczyć wszystkie
aktualnie podpięte partycje.
$ mount | wc -l
44
Jak widać, w systemie TCS (na serwerze student) jest bardzo dużo różnych dysków
podpiętych w ten sposób.
I-węzły i bloki danych
Każda partycja ma swój niezależny system plików. Współczesne systemy linuxowe operują
w systemie ext2, ext3 lub ext4. Systemy te są do siebie podobne. Systemy przechowują
Strona 1/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
dwa rodzaje informacji:
∗ Informacje o plikach przechowywane są w tablicy i-węzłów (ang. i-node table).
∗ Dane zawarte w plikach przechowywane są w blokach, gdziekolwiek w przestrzeni
dyskowej.
Każdy I-węzeł (ang. inode) reprezentuje
dokładnie jeden plik. Przechowywane są
tam wszystkie informacje o danych, ale nie
sama zawartość pliku.
∗
∗
∗
∗
∗
∗
Rodzaj pliku (zwykły plik, katalog, ...)
Właściciel pliku
Prawa dostępu
Wielkość
Czas modyfikacji
Adresy bloków które przechowują dane
I-węzeł nie przechowuje nazwy pliku!
We wszystkich blokach do których odwołuje
się dany i-węzeł znajduje się fragment
zawartość pliku. Każdy blok ma z góry
ustaloną większość. Na systemach TCS jest
to 4KB. Do każdego bloku odwołuje się
zawsze dokładnie jeden i-węzeł. Nie jest możliwe podzielenie bloku na mniejsze kawałki.
W konsekewncji, jeśli wielkość pliku nie jest dokładną wielokrotnością wielkości bloku,
trochę przestrzeni dyskowej zostaje zmarnowane.
719
723
.
..
srodowisko
719
322
723
.
..
plik1 . txt
plik2 . txt
723
719
756
772
Katalog jest specjalnym rodzajem pliku. Iwęzeł odpowiadający za katalog ma taką
samą strukturę jak każdego innego pliku.
Zawartość katalogu zdefiniowana jest nie w
węźle, a w bloku danych. To tam wypisane
są nazwy zawieranych plików, i adres iwęzłów im odpowiadającym.
Każdy katalog, nawet gdy jest on pusty, ma
zawsze zdefiniowane dwie pozycje:
∗ . – wskazuje na swój własny i-węzeł.
∗ .. – wskazuje na i-węzeł odpowiadający
za katalog nadrzędny.
Podając parametr -i do komendy ls możemy podejrzeć informację o i-węzłach:
Strona 2/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
$ ls - ali
723 drwxrwxr - x
2 pdan pdan 4096 Oct
719 drwx - - - - - x 10 pdan users 4096 Oct
756 -rw - rw -r - -+ 1 pdan pdan
52 Oct
772 -rw - rw -r - 1 pdan pdan
68 Oct
9
9
6
6
06:42
06:42
07:56
06:39
.
..
plik1 . txt
plik2 . txt
Każda pozycja poprzedzona jest numerem i-węzła. Zwróćmy też uwage na liczbę występującą
bezpośrednio po prawach dostępu. Na poprzednich zajęciach powiedzieliśy, że jest to
“liczba połączeń”, ale nie wyjaśniliśmy czym te połączenia są. Liczba połączeń to liczba
krawędzi przychodzących z któregokolwiek katalogu do danego i-węzła. W przypadku
zwykłych plików, wartość ta zwykle wynosi 1, bo plik znajduje się w dokładnie jednym
katalogu. Katalogi natomiast mają wiele przychodzących krawędzi z powodu plików . i
..
Dowiązania
Dowiązania (ang. link ) to cecha niektórych systemów plików (w tym tych omawianych
przez nas) pozwalająca na tworzenie dodatkowych, niestandardowych połączeń w systemie
plików. Dodatkowe połączenia pozwalają na szybszą nawigację w strukturze plików: dany
plik można łatwo dowiązać do katalogu nas interesującego, mimo że tak na prawdę
znajduje się on zupełnie gdzie indziej. Dowiązania można stosować zarówny do poszczególnych
plików danych, jak i do całych katalogów.
Jednym z takich zastosowań jest ukrywanie wersji programów. Często nazwa programu
zawiera numer wersji. Na przykład na TCS/student zainstalowany jest kompilator języka
C w kilku wersjach:
∗ gcc-4.9 – stara wersja 4.9.3 z 2015 roku
∗ gcc-5 – aktualna stabilna wersja 5.4.0 z czerwca 2016 roku
∗ gcc-6 – eksperymentalna wersja 6.0.0, być może zawierająca błędy
Czasami, w szczególnych przypadkach użytkownik potrzebuje skompilować swój kod za
pomocą konkretnej wersji kompilatora. Jednakże, w większości liczbie przypadków nie
interesuje go jakie są wersje i potrzebuje po prostu najlepszej, stabilnej wersji która
jest aktualnie dostępna. Dlatego w systemie dowiązany jest plik gcc który wskazuje na
aktualną najlepszą wersję.
$ cd / usr / bin
$ ls - ali gcc *
128076 lrwxrwxrwx
128077 - rwxr - xr - x
9264709 - rwxr - xr - x
128079 - rwxr - xr - x
...
1
1
1
1
root
root
root
root
root
5 Jul 14
root 838008 Apr 14
root 915736 Aug 8
root 965264 Apr 15
17:36
04:23
17:22
23:44
gcc -> gcc -5
gcc -4.9
gcc -5
gcc -6
Możemy zauważyć, że plik gcc aktualnie wskazuje na gcc-5. W przyszłości, jak pojawi
się nowsza stabilna wersja, będzie wskazywał w nowe miejsce. Niezależnie od wersji,
Strona 3/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
użytkownik zawsze może wywołać gcc i mieć pewność że kompilator się uruchomi.
Zwróćmy też uwagę na nowy atrybut pliku gcc: l – który oznacza, że jest to dowiązanie
(link).
Inne zastosowanie dowiązań:
∗ Inkrementatywne kopie zapasowe.
∗ Szybkie podmienianie plików, np. przy zmianie konfiguracji lub instalowaniu aktualizacji
systemu
∗ Segregowanie tych samych zasobów według różnych kryteriów
Istnieją dwa rodzaje dowiązań:
∗ Dowiązanie symboliczne (symbolic link, symlink ) operuje na nazwach plików.
∗ Dowiązanie twarde (hard link ) operuje na numerach i-więzów.
Plik dowiązania symbolicznego przechowuje nazwę pliku docelowego. W przypadku gdy
dowolna aplikacja próbuje otworzyć plik poprzez dowiązanie, system operacyjny automatycznie
przeszuka drzewo katalogów by znaleźć właściwy plik docelowy. Na poziomie aplikacji, np.
w Waszym kodzie C/C++ nie potrzeba dokonywać żadnych zmian by poprawnie obsłużyć
takie dowiązania.
Plik docelowy nie jest “świadom” istnienia dowiązań symbolicznych nań wskazujących.
Jeśli plik docelowy zostanie usunięty bądź jego nazwa ulegnie zmianie, to nie będzie on
osiągalny poprzez dowiązanie. Próba użycia takiego dowiązania zakończy się błędem.
Dowiązania symboliczne tworzymy poleceniem: ln -s <plik docelowy> <nazwa dowiązania>
719
721
723
.
..
plik1 . txt
719
322
721
dane
.
..
hard - link
723
719
721
Plik dowiązania twardego, to po prostu nowy
wpis w katalogu używającego numer i-węzła,
który już jest wykorzystywany przez inny plik.
Twarde linki są nierozróżnialne od oryginału.
Wszystkie wpisy prowadzące do danego i-węzła
są równoważne.
Twarde dowiązanie nie jest jednak pełna kopia
pliku. Wszystkie pliki podpięte pod ten sam
i-węzeł używają tą samą, jedną przestrzeń
dyskową. Jest to ten sam plik, o wielu nazwach.
Naturalnie, zmiana nazwy jednego z połączeń,
łącznie z przeniesieniem pliku w inne miejsce, nie wpływa na pozostałe powiązania.
Co więcej, próba usunięcia pliku poprzez jedno z połączeń nie wpłynie negatywnie na
działanie pozostałych twardych dowiązań.
Dzieje się tak ponieważ komenda rm wcale nie usuwa pliku, tylko usuwa wpis w katalogu.
Strona 4/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
Jeśli po takiej operacji docelowy i-węzeł nie jest wskazywany przez żaden inny wpis,
plik zostanie naprawdę usunięty. Ponieważ zwykłe pliki są zazwyczaj wskazywane tylko
poprzez jedno połączenie, rm prowadzi do usunięcia takiego pliku. Jednakże, w kontekście
twardych dowiązań, nie jest to już prawdą. Pozostałe połączenia pozostają bez zmian.
Warto tu dodać, że gdy jakakolwiek aplikacja otwiera plik, tworzy ona podobne połączenie
z plikiem. Gdy w trakcie działania aplikacji wszystkie połączenia do pliku zostaną usunięte,
plik nadal pozostaje na dysku. Dopiero gdy aplikacja zamknię plik, zostaje on usunięty.
Takie podejście jest szczególnie przydane na przykład podczas aktualizacji plików systemowych
bez potrzeby restartowania komputera.
Twarde dowiązania tworzy się poleceniem ln <plik docelowy> <nazwa dowiązania> —
bardzo podobnie jak dowiązania symboliczne, ale bez parametru -s.
Twarde dowiązania nie mogą być używane dla katalogów, gdyż może to złamać drzewiastą
strukturę. Za pomocą takich dowiązań łatwo możnaby stworzyć cykle które mogłyby mieć
katastrofalne skutki dla niektórych operacji rekurencyjnych (np. find).
Twarde dowiązania nie mogą być też stosowane przy łączeniu plików z różnych partycji
czy dysków sieciowych. Każda partycja dysponuje własną numeracją i-węzłów. Numeracja
jest unikalna tylko w obrębie jednej partycji.
W obu tych przypadkach można bez problemów używać dowiązań symbolicznych.
Prawa dostępu
Każdy i-węzeł przechowuje informację o właścicielu pliku i prawach dostępu. Prawa dostępu
zdefiniowane są dla trzech kategorii ludzi:
∗ Właściciela pliku (u)
∗ Grupy przypisanej do pliku (g)
∗ Pozostałych użytkowników (o)
Dla każdej kategorii można zdecydować, czy użytkownik może plik odczytac (r ), zapisać
(w ) lub uruchomić (x ).
Właściciel pliku może zmienić uprawnienia za pomocą komendy chmod. Składnia komendy
to:
chmod KOU < nazwa >
gdzie
∗ K — znak lub kombinacja znaków definiujący kategorię użytkowników co do której
dokonujemy zmianę (u, g, o). Można też użyć a dla wszystkich, równoważne z ugo.
∗ O — znak operacji: prawa dostępu można nadać + albo udebrać -.
Strona 5/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
∗ U — znak lub kombinacja znaków definiująca rodzaju uprawnień — r, w lub x.
Przykłady nadawania praw:
chmod u+x nazwa
chmod ug+rx nazwa
chmod a-rx nazwa
daje użytkownikowi prawo do uruchamiania
nadaje użytkownikowi i grupie prawo do czytania i uruchamiania
zabiera prawo do czytania i uruchamiania dla wszystkich użytkowników
Można również użyć systemu ósemkowego, aby ustawić prawa. Czytaniu (read) odpowiada
4, pisaniu (write) odpowiada 2, a wykonaniu (execute) 1. Każda cyfra odpowiada jednemu
z kategorii użytkowników. Na przykład:
chmod 755 helloshell.sh
nadaje użytkownikowi prawa do pisania, czytania i uruchamiania (4+2+1), prawo czytania
i uruchamiania (4+0+1) grupie i innym.
Ponieważ prawa dostępu są zapisane w i-węźle, są one stałe, niezależnie przez które
dowiązania plik jest otwierany. Dowiązanie symboliczne jest osobnym i-węzłem ze swoimi
prawami, ale są one ignorowane – liczą się tylko i wyłącznie prawa dostępu zapisane w
i-węźle docelowego pliku który otwieramy.
Uprawnienia do katalogów nadaje się w dokładnie taki sam sposób co do zwykłych
plików. Jak już wyjaśniliśmy wcześniej, z punktu widzenia systemu plików, katalog to
po prostu specjalny rodzaj pliku. Jednakże, w konteście katalogów, warto wyjaśnić co
dane uprawnienia oznaczają w praktyce:
∗ r – odczyt pozwala użytkownikowi odczytać nazwy w katalogu.
∗ w – zapis pozwala użytkownikowi stworzyć, zmienić lub usunąć pliki z danego katalogu.
∗ x – pozwala uzyskac dostęp do plików powiązanych z nazwami w danym katalogu.
W szczególności, katalog z uprawnieniami r-- pozwoli nam zaledwie na wypisanie wszystkich
nazw plików, bez podania jakiejkolwiek informacji czym właściwie są te pliki. Innymi
słowy, mamy nazwy ale nie mamy dostępu do powiązanych z nimi i-węzłów.
Natomiast katalog z uprawnieniami --x pozwoli nam otworzyć dowolny plik, ale nie powie
nam jakie właściwie pliki tam są.
SUID i SGID
Istnieje jeszcze specjalna flaga SUID i SGID, włączana odpowienio przez u+s i g+s.
Plik wykonywalny z taką flagą dziedziczy uprawnienia właściciela pliku gdy zostanie
on uruchomiony. Programy z taką flagą pozwalają innym użytkownikom na wykonanie
operacji której normalnie wykonać by nie mogli.
Strona 6/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
Dla przykładu, program /bin/mount pozwala zamontowac nowy dysk czy zasób w określonym
miejscu w systemie plików. Operacja taka wymaga uprawnien głównego administratora
(tzw. roota). W normalnej sytuacji mount byłby dla mnie bezużyteczny, bardzo szybko
bym się przekonał, że nie mam wystarczających uprawnień.
Na szczęście mount ma ustawioną flagę SUID i root jest właścicielem pliku. Niezależnie
kto uruchomi ten program, jego działanie będzie miało uprawnienia roota, i niezbędne
zmiany będą mogły być dokonane.
Naturalnie, trzeba być bardzo ostrożnym nadając flagi SUID lub SGID szczególnie gdy
jest się administratorem. Jeśli program z taką flagą zawiera błąd, złośliwy użytkownik
może próbować uzyskać szerszy dostęp do komputera niż oryginalnie założono.
Drzewo katalogów w systemie Linux
Niezależne od wersji systemu Linux, katalog główny i niektóre podkatalogi wyglądają
bardzo podobnie.
/
/bin
/boot
/dev
/etc
/home
/initrd
/lib
/media
/opt
/proc
/root
/tmp
/usr
katalog główny. Tu wszystko się znajduje.
podstawowe programy systemu i powłoki. Część z komend które
przedstawiliśmy na poprzednich zajęciach to są tak na prawdę osobne
programy, które znajdują się właśnie tutaj.
pliki startowe i jądro linuxa
urządzenia peryferyjne komputera, reprezentowane jako specjalne
pliki, oznaczane literą c. Pliki te nie rezydują na żadnym z dysków,
ale wykorzystują te same funkcje zapisu i odczytu, pozwalając różnym
aplikacjom z łatwością komunikować się z tymi urządzeniami.
pliki konfiguracyjne
prywatne pliki użytkowników
zawiera informacje potrzebne do startu systemu. Na TCSie widnieje
jako symlink do jednego z plików w /boot.
zawiera biblioteki dynamiczne potrzebne do uruchamiania różnych
programów. Wasze programy w C też z tych plików korzystają.
tu domyślnie mountowane są dyski zewnętrzne (CD-ROMy, dyskietki,
pendrive-y)
dodatkowe programy niezależnych dystrybutorów, nie związanych z
daną wersją Linuxa. Na TCS-ie znajdziecie tutaj na przykład satori
wirtualny system plików reprezentujące aktualne zasoby systemu.
Tu można odczytać np. parametry procesora (/proc/cpuinfo) czy
pamięci (/proc/meminfo).
katalog domowy głównego administratora systemu.
pliki czasowe. Mogą ulec usunięciu np. przy restarcie systemu.
programy i biblioteki dla programów użytkownika. Dla programisty to
bardzo ważny katalog!
Strona 7/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
/usr/bin
narzędzia programistyczne. Tu znajdziecie m. in. kompilatory C i C++
/usr/games
bo każdy programista musi się czasem rozerwać :)
/usr/include pliki nagłówków do C i C++, zarówno te standardowe jak i
dodatkowych pakietów zainstalowanych na Linuxie
/usr/lib
pliki bibliotek
/usr/local
podobny do /usr. Ideowo tu znajdują się pliki bardziej lokalne dla
danej maszyny, ale rozróżnienie to jest często niejasne w praktyce.
/usr/share
domyślne pliki konfiguracyjne, rysunki, dokumentacje zainstalowanych
bibliotek, itp.
/usr/src
pliki źródłowe
/var
Tu pojawiają się m.in. niektóre logi, cache plików internetowych,
zadania dla drukarek, itp. Wszystkie te pliki mają cechę, że ich
zawartość i liczba często zmienia się w czasie w wyniku normalnego
działania systemu.
Drzewo typowego projektu w C/C++
Każdy programista w ciągu kariery spotyka się z większymi projektami, które składają
się z wielu plików.
Mały program w C lub C++ zawiera się w pojedyńczym pliku. Taki program program.c
kompilujemy za pomocą gcc program.c -o program – i gotowe!
Jednakże, większe projekty często podzielone są na większą liczbę plików. Zarządzanie i
kompilacja takich projektów jest bardziej skomplikowana.
Podstawowy proces kompilacji składa się z dwóch etapów:
∗ Każdy plik źródłowy .c lub .cpp jest kompilowany do pliku obiektowego .o. Zawiera
on kod maszynowy pojedyńczych funkcji oraz spis wszystkich funkcji które dany plik
zawiera (i je eksportuje) lub potrzebuje (importuje). Kod maszynowy zawiera luki:
adresy funkcji importowanych nie są znane.
∗ Wszystkie pliki obiektowe, wraz ze statycznymi bibliotekami, musza zostać połączone.
Jest to zadanie linkera, który łączy wszystkie funkcje w jeden plik programu. Adresy
wyeksportowane przez jeden plik są używane do uzupełnienia adresów funkcji importowanych
w innych plikach.
Pojedyńcze pliki kompilujemy za pomocą:
gcc plik . c -c -o plik . o
∗ <nazwa> – argument pozycyjny to nazwa pliku który kompilujemy.
∗ -c – oznacza, że wykonujemy samą kompilację bez linkowania.
∗ -o <nazwa> – precyzuje jak ma się nazywać plik który generujemy. Domyślnie jest to
nazwa pliku kompilowanego, z zastąpionym .c przez .o.
Strona 8/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
Jak już skompilujemy każdy plik z osobna, połączyć je można za pomocą linkera o nazwa
ld. Łatwiej jest jednak ponownie wywołać gcc z plikami .o jako argumentami. Program
gcc wywoła tego samego linkera, ale uzupełni argumenty o domyślne wartości dla języka
C.
gcc plik1 . o plik2 . o -o program
∗ <nazwa> – argument pozycyjny to nazwa pliku który chcemy łączyć. Należy tu wypisać
wszystkie pliki obiektowe które uprzegnio wygenerowaliśmy.
∗ -o <nazwa> – precyzuje jak ma się nazywać plik programu który generujemy. Domyślnie
jest to zawsze a.out.
Kompilując język C++ wystarczy zamienić gcc przez g++ w powyższych komendach. Oba
kompilatory przyjmują te same parametry i flagi.
Zauważmy, że projekt się rozrasta: dla każdego pliku źródłowego, generujemy nowy plik
pośredni .o. Jest to plik tworzony podczas kompilacji, ale gdy otrzymamy główny program
– taki plik nie jest już potrzebny. Z czasem może okazać się, że musimy też zawrzeć w
projekcie dokumentację, lub kod źródłowy bibliotek zewnętrznych napisanych przez kogoś
innego. Bardziej skomplikowane projekty moga też tworzyć jeszcze więcej różnych plików
pośrednich. Dlatego każdy projekt, nawet jeśli zawiera on tylko dwa pliki źródłowe, warto
podzielić na podkatalogi. Typowy podział to:
projekt
projekt/bin
projekt/build
projekt/contrib
projekt/data
projekt/doc
projekt/include
projekt/src
projekt/test
katalog główny. Bezpośrednio w tym katalogu umieszcza się
niewiele plików. Głównie jest tam skrypt kompilujący/instalujący,
krótki opis działania programu oraz informacja o prawach
autorskich i licencji.
Tu generowane są końcowe pliki binarne, takie jak programy.
Katalog na wszystkie pliki pośrednie dla procesu kompilacji.
Narzędzia innych osób na których dany projekt się opiera.
Dane na których program operuje
Dokumentacja, opis programu, notatki
Jeśli projekt jest biblioteką – tu umieszane są pliki nagłówków
(header file). Takie pliki zawierają deklaracje bez definicji.
Tu umieszczony jest kod źródłowy.
Tu jest kod testujący program, lub jego fragmenty.
Powyższa lista nie jest wyczerpująca. W danym projekcie może pojawić się więcej katalogów
w zależności od potrzeb. Te są najpopularniejsze.
Strona 9/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
Cytowanie w systemach UNIXowych
Cytowanie (ang. quoting) ma na celu usuwanie interpretacji znaków specjalnych przez
powłokę. Najprostszym sposobem usunięcia interpretacji jest użycie znaku maskującego:
\. W nastęþującym przykładzie shell nie interpretuje znaków końca linii.
$ echo This could be \
a very \
long line\!
This could be a very long line!
Poza tym mamy trzy rodzaje cudzysłowów: ’’, "" oraz ‘‘. Pojedyncze cudzysłowy, te
wymienione jako pierwsze maskują znaczenie wszystkich znaków specjalnych. Podwójne
cudzysłowy pozwalają na interpretacje niektórych cudzysłowów. Natomiast nie interpretują
innych takich, jak * albo ?. Ostatni rodzaj cudzysłowów pozwala na umieszczenie w tekście
wyniku działania pewnych komand.
Dla przykładu warto porównać dwie następujące komendy.
$ echo ’W tym katalogu teraz przebywasz: ‘pwd‘’
W tym katalogu teraz przebywasz: ‘pwd‘
$echo "W tym katalogu teraz przebywasz: ‘pwd‘"
W tym katalogu teraz przebywasz: /home/wrona.
xargs
-name "*.c"
find
xargs rm -f
Polecenie xargs służy do konstruowania listy argumentów
ze standardowego wejścia dla innych poleceń i
wykonywania tychże poleceń. Brzmi to dość abstrakcyjnie
i może enigmatycznie ale kilka zastosowań polecenia
xargs jest uważanych za jedne z najbardziej użytecznych
sekwencji komend w Linuksie.
Przykład. Skasuj wszystkie pliki z rozszerzeniem .c w
bieżącym katalogu. Oczywiście możemy zrobić to tak:
rm
rm *.c
Możemy, też tak:
find . -name "*.c" | xargs rm
Strona 10/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
To jednak nie zadziała gdy mamy pliki z białymi
znakami w nazwie. Możemy to poprawić tak, aby nie było komunikatu błędu:
find . -name ’*.c’ | xargs rm -f
Ale plik z białymi znakami w nazwie dalej pozostanie nieskasowany. Poprawnie zadziała
dopiero, gdy zmienimy terminatora nazwy plików na znak null. Do tego potrzebujemu
flagi -print0 w find oraz -0 w xargs.
find . -name ’*.c’ -print0 | xargs -0 rm
Wprawdzie polecenie find -exec rm pozwala nam usuwać znalezione pliki, to następuje
to wolniej niż skorzystanie z xargs. Jest tak, ponieważ find uruchamia dla każdego
pliku pojedynczo plecenie rm. Natomiast xargs przekazuje tyle argumentów ile polecenie
maksymalnie może przyjąć. W przypadku rm jest to na tyle duża liczba, że znacząco
zwiększa wydajność usuwania plików.
Przykład. Dla każdego pliku w bieżącym katalogu o rozszerzeniu .txt wydrukuj jedną
linię na standardowe wyjście zawierającą liczbę linii w tym pliku i jego nazwę.
ls -1 *.txt | xargs -n 1 wc -l
Flaga -n 1 w xargs, powyżej, pozwala na przekazanie tylko jednego argumentu na raz
do xargs.
Przykład. Przesuń wszystkie pliki o rozszerzeniu .bak do katalogu ~/old.files.
find . -name "*.bak" -print0 | xargs -0 -I {} mv {} ~/old.files
find . -name "*.bak" -print0 | xargs -0 -I file mv file ~/old.files
Zwróć uwagę na opcję -I. Po niej podawany jest znacznik, który ma być interpretowany w
poleceniu jako miejsce gdzie xargs ma wkleić dane otrzymane na standardowym wejściu.
Domyślnie xargs wstawia te dane na końcu tworzonego polecenia (patrz poprzednie
przykłady). Jednak polecenie mv wymaga aby najpierw była nazwa pliku (bądź plików)
które mają być przeniesione, a na końcu ma być miejsce przeniesienia. Powyższe dwie
linie różnią się jedynie ciągiem znaków podanym jako wspomniany znacznik.
Przykład. W każdej linii pliku „test.in” znajduje się jedno słowo. Utwórz pliki o nazwach
takich jak słowa z „test.in” Niestety polecenie touch nie wczytuje danych ze standardowego
wejścia, a jedynie jako argumenty. Więc jedynym sposobem jest użycie xargs:
cat test.in | xargs touch
Tak jak wcześniej omawialiśmy powyższa sekwencja nie zadziała dla nazw z białymi
znakami.
Przykład.
cat plik | xargs -n 1 -I {} sh -c "mkdir {}; touch {}/{}"
Strona 11/12
Środowisko programisty
Zestaw 3
Semestr zimowy 2016/2017
Kraków, 18-20 października 2016
Dla każdego słowa w pliku plik tworzy katalog o tej nazwie i pusty plik w tym katalogu o
tej samej nazwie. Zwróć uwagę, iż xargs wywołuje tutaj nową instancje powłoki poleceń
(poprzez polecenie sh). Dzięki temu możemy wykonać więcej niż jedno polecenie używając
argumentów przekazanych przez xargs.
Przydatne opcje find
(i) find . -name ’*.c’ -maxdepth 1 — drukuje nazwy plików i katalogów o nazwie
zakończonej .c w aktualnym katalogu ale nie w jego podkatalogach,
(ii) find . -name ’*.c’ -type f — drukuje nazwy plików o nazwie zakończonej .c
w aktualnym katalogu i podkatalogach,
(iii) find . -name ’*.c’ - type f -size 0 — drukuje nazwy pustych plików w aktualnym
katalogu i podkatalogach
Strona 12/12