Jeśli do tej pory nie pracowałeś z konsolą koniecznie przeczytaj artykuł opisujący początki pracy z linią poleceń. Mając podstawy opisane w tamtym artykule będzie Ci dużo łatwiej. Artykuł o początkach pracy z linią poleceń między innymi opisuje programy:
cd
ls
pwd
mkdir
rmdir
touch
echo
cat
clear
Specyficzne dla bash
‘a
Na tym etapie wiesz już czym jest ścieżka. Sporo programów akceptuje ścieżki jako parametry. W niektórych przypadkach niezbędne jest przekazanie wielu ścieżek. W takiej sytuacji z pomocą mogą przyjść wyrażenia glob.
Glob
bash
nie wspiera wyrażeń regularnych. Mam na myśli to, że sama powłoka nie pozwala na przykład na określenia ścieżki do pliku używając wyrażeń regularnych1. bash
używa wyrażeń „glob”, które są do nich podobne.
Historycznie glob był osobnym programem, który został wchłonięty przez bash’a. Wyrażenia glob pozwalają na odwoływanie się do plików/katalogów używając ?
, *
i []
. Znak ?
zastępuje jeden znak, *
zastępuje dowolną liczbę znaków. Na przykład wyrażenie glob *.txt
pasuje do wszystkich plików z rozszerzeniem .txt
w aktualnym katalogu. Wyrażenie glob ?.txt
pasuje do wszystkich plików których nazwa (przed rozszerzeniem) ma jeden znak.
[]
zawiera w sobie grupę dozwolonych znaków. Na przykład wyrażenie [ab].txt
pasuje do nazw plików a.txt
i b.txt
ale nie pasuje do nazwy ab.txt
. Grupy umieszczone wewnątrz []
mogą być zakresami znaków. Zakres znaków oddzielony jest -
, na przykład [a-d].txt
pasuje do nazw plików a.txt
, b.txt
, c.txt
i d.txt
. Jeśli chcesz dopasować -
dosłownie umieść go na początku, albo na końcu grupy, na przykład [-a]
albo [a-]
.
Podsumowując, w wyrażeniach glob możesz używać następujących wzorców:
?
oznacza dowolny pojedynczy znak (poza/
i.
na początku)*
oznacza dowolną liczbę znaków (poza/
i.
na początku)[…]
oznacza grupę znaków zgodnie z zawartością
Istotne jest to, że wyrażenia glob są interpretowane przez konsolę zanim zostanie uruchomiony właściwy program. Proszę rzuć okiem na przykład poniżej:
$ ls
a.txt b.txt c.csv
$ ls *.txt
a.txt b.txt
W pierwszym przypadku zostanie uruchomiony program ls
bez żadnego parametru. Domyślnie zatem zostanie użyty aktualny katalog (.
). Program wypisze zawartość aktualnego katalogu, w moim przypadku są to trzy pliki: a.txt, b.txt i c.csv. W drugim przypadku pojawia się wyrażenie glob *.txt
, które zostaje rozwinięte przez konsolę do a.txt b.txt
i przekazane jako argument do programu ls
. Zatem w przykładzie powyżej ls *.txt
jest tak na prawdę wywołaniem ls a.txt b.txt
.
Wyrażenia glob nie biorą pod uwagę plików/katalogów, których nazwa zaczyna się od kropki (.
). Jeśli wyrażenie glob nie może być rozwinięte (nie pasuje do żadnego pliku/katalogu) zostanie przekazane jako parametr bez zmian:
$ ls
exists.txt
$ echo *.txt
exists.txt
$ echo *.pdf
*.pdf
Pobierz opracowania zadań z rozmów kwalifikacyjnych
Przygotowałem rozwiązania kilku zadań algorytmicznych z rozmów kwalifikacyjnych. Rozkładam je na czynniki pierwsze i pokazuję różne sposoby ich rozwiązania. Dołącz do grupy ponad 6147 Samouków, którzy jako pierwsi dowiadują się o nowych treściach na blogu, a prześlę je na Twój e-mail.
Rozwijanie ~
W bash
‘u znak tyldy (~
) ma specjalne znaczenie. ~
oznacza katalog domowy użytkownika. Podobnie jak wyrażenia glob, tylda rozwijana jest do właściwej ścieżki przed przekazaniem jej jako parametr do programu. Proszę spójrz na przykład poniżej, w którym użyłem programu echo
:
$ echo ~
/home/mapi
$ echo ~/some/path
/home/mapi/some/path
Używając tyldy możesz także odwołać się do katalogu domowego dowolnego użytkownika. Na przykład ~root
oznacza katalog domowy użytkownika root
:
$ echo ~root
/root
Możesz użyć także rozwijania ~
do poznania aktualnego katalogu używając +
2:
$ cd /run/usr/1000
$ echo ~+
/run/user/1000
W podobny sposób -
pokazuje poprzedni katalog:
$ cd /tmp
$ cd
$ echo ~-
/tmp
Podobnie jak wyrażenia glob, także znak ~
jest rozwijany przez powłokę przed przekazaniem tego znaku jako parametr do uruchamianego programu.
Rozwijanie { }
Bash wspiera także mechanizm rozwijania { }
. Proszę spójrz na przykład poniżej:
$ echo some-{magic,long,complicated}-text
some-magic-text some-long-text some-complicated-text
Wywołanie programu echo
wyświetla przekazane argumenty używając standardowego wyjścia. Bash, w trakcie procesu rozwijania { }
zamienił pojedynczy parametr na trzy osobne parametry.
Wewnątrz nawiasów może znajdować się dowolna liczba elementów oddzielona znakiem ,
. Każdy z tych elementów będzie skutkował nowym „słowem” podstawionym przez bash’a.
Rozwijanie { }
może także służyć do generowania sekwencji numerów. Proszę spójrz na przykład, w którym generuję liczby od 7 do 10:
$ echo sequence-{7..10}
sequence-7 sequence-8 sequence-9 sequence-10
Użycie wiodącego 0
powoduje generowanie numerów o stałej szerokości:
$ echo sequence-{07..10}
sequence-07 sequence-08 sequence-09 sequence-10
Opcjonalnym, trzecim parametrem może być skok, który informuje o ile powinny różnić się kolejno generowane liczby:
$ echo sequence-{0..10..2}
sequence-0 sequence-2 sequence-4 sequence-6 sequence-8 sequence-10
Ten sam mechanizm można także użyć do generowania sekwencji liter:
$ echo sequence-{a..d}
sequence-a sequence-b sequence-c sequence-d
Najczęściej używam tej składni jeśli chcę skopiować albo przenieść plik czy folder:
$ ls
some_file.txt
$ mv some_file.txt{,.bak}
$ ls
some_file.txt.bak
Parametry specjalne
bash
posiada zestaw parametrów, które mają specjalne znaczenie. Możesz odwołać się do tych parametrów używając składni $<znak parametru>
, na przykład $?
. Są one traktowane jako specjalne, ponieważ służą wyłącznie do odczytu. Część z nich znajdziesz poniżej:
$#
- zawiera liczbę parametrów przekazanych do skryptu bash’a$?
- zawiera kod wyjścia poprzednio uruchomionego programu$$
- zawiera identyfikator procesu bash’a$_
- zawiera ostatni argument poprzedniej komendy
Proszę spróbuj trochę poeksperymentować z użyciem tych parametrów, wtedy zrozumienie ich działania będzie dużo łatwiejsze.
Historia
Bash posiada bardzo przydatną funkcję, pozwala ona na zapisywanie historii wykonywanych poleceń. Przy odpowiedniej konfiguracji (domyślnej na przykład w Ubuntu) w pliku ~/.bash_history
zapisywana jest historia poleceń. Historia ta jest aktualizowana w momencie zamykania okna terminala.
Historia jest przydatna, bo często możesz używać poleceń, których używałeś poprzednio. Pomocny może być skrót klawiaturowy Ctrl+R
, który pozwala na przeszukiwanie historii. Po użyciu tego skrótu klawiaturowego zmieni się standardowy znak zachęty. Możesz wtedy wpisywać fragmenty poleceń z historii. Jak zwykle, zachęcam Cię do eksperymentów:
(reverse-i-search)`':
To dzięki historii możesz też używać strzałek (góra/dół) do poruszania się po historii wykonywanych poleceń. Chociaż sam używam częściej programu history
albo wspomnianego skrótu Ctrl+R
.
history
Program history
wypisuje historię wykonywanych poleceń. Często zdarza mi się używać tego programu w połączeniu z grep
i potokami:
$ history | grep docker | tail -n 3
4500 docker run --rm -it alpine
4501 docker run --rm -it --entrypoint /bin/sh alpine/helm
4545 history | grep docker | tail -n 3
Przydatny może być też program fc
, który pozwala na edycję wprowadzonych do tej pory komend przed ich wywołaniem. W przykładzie poniżej fc 1170
uruchomi edytor tekstu z poleceniem git rebase -i master
. To polecenie znajduje się na 1170 miejscu w historii bash’a:
$ history | grep git | tail -n 4
1170 git rebase -i master
1174 git status
1176 git log -5
1178 git push
$ fc 1170
Modyfikowanie historii
Chociaż historia to dobra rzecz i nie raz może uratować skórę, zdarzają się przypadki, w których nie chcesz zostawiać po sobie śladu. Na przykład kiedy w linii poleceń wpisujesz hasło czy klucz do API.
To bardzo zła praktyka. Do przekazywania danych wrażliwych jak hasła czy tokeny dostępu używaj plików (przekazując ścieżkę do pliku z danymi wrażliwymi) albo zmiennych środowiskowych (zawierających dane wrażliwe albo ścieżkę do pliku z danymi wrażliwymi).
To rozwiązanie też nie jest idealne. Zmienne środowiskowe, podobnie jak pliki mogą być dostępne dla innych użytkowników systemu. Jednak takie rozwiązanie jest o niebo lepsze niż używanie danych wrażliwych bezpośrednio w konsoli.
W przypadku kiedy nie chcesz aby dana komenda została zapisana w historii poprzedź ją ` ` (spacją)3.
A co jeśli mleko już się rozlało i komenda została już zapisana w historii? Wówczas z pomocą przychodzi program history
z parametrem -d
:
$ history | tail -n 1
1190 curl https://admin:password1@ministry.gov
$ history -d 1190
$ history -w
Polecenie history -d 1190
usuwa z historii komendę z numerem 1190. hisory -w
zapisuje aktualną historię (z usuniętą komendą) w pliku historii.
Jeśli nie chcesz używać programu history
zawsze możesz edytować plik historii samodzielnie. Zmienna środowiskowa HISTFILE
przechowuje ścieżkę do pliku, w którym przechowywana jest historia poleceń:
$ vim $HISTFILE
Rozwijanie historii
Proszę spójrz na przykład poniżej, w którym użyłem podstawowego mechanizmu rozwijania historii:
$ history | grep git | tail -n 3
1174 git status
1176 git log -5
1178 git push
$ !1176
$ history | grep git | tail -n 3
1176 git log -5
1178 git push
1201 git log -5
Wywołanie !1176
spowodowało ponowne uruchomienie programu zapisanego w historii pod numerem 1176. Mechanizm ten jest dość rozbudowany. Jeśli chcesz poznać więcej jego możliwości odsyłam Cię do sekcji „History expansion” w dokumentacji bash
‘a.
Polecenia wbudowane
Do tej pory używałem głównie określenia „program”, jednak nie we wszystkich przypadkach było to do końca poprawne. Dzieje się tak za sprawą poleceń wbudowanych.
W dochodzeniu do prawdy pomocny będzie program which
:). Ten program zwraca ścieżki programów, które byłyby uruchomione dla każdego z przekazanych parametrów. Robi to oparciu o listę katalogów przechowywanych w zmiennej środowiskowej PATH
. Proszę spójrz na przykład:
$ which ls
/bin/ls
W tym przykładzie which
zwraca absolutną ścieżkę programu, który zostanie uruchomiony po wywołaniu ls
. W tym przypadku jest to /bin/ls
.
W ten sam sposób możesz sprawdzić inne programy:
$ which which mount cron
/usr/bin/which
/bin/mount
/usr/sbin/cron
A teraz spróbuj zrobić to samo dla innych „programów”, których używasz cd
czy history
:
$ which cd history
Hmm ;), which
nie pokazało nic. Dzieje się tak z tego powodu, że zarówno cd
jak i history
to polecenia wbudowane w bash
‘a. Takich poleceń jest więcej. Jednym z wbudowanych poleceń jest type
, które rzuca więcej światła na tę sprawę:
$ type -a history
history is a shell builtin
Użyłem tu przełącznika -a
, który zwraca wszystkie możliwe opcje, a jest ich kilka :). Proszę spójrz na kolejny przykład:
$ type -a kill pwd
kill is a shell builtin
kill is /bin/kill
pwd is a shell builtin
pwd is /bin/pwd
Jak widzisz istnieją także „programy”, które są zarówno poleceniami wbudowanymi jak i zwyczajnymi programami. W dalszej części artykuł nadal będę używał określenia „program” odnosząc się zarówno do programów jak i poleceń wbudowanych.
Wywoływanie programów w tle
Może się zdarzyć, że chcesz wywołać program, który działa bardzo długo a nie chcesz zajmować aktualnego okna konsoli. Z pomocą przychodzi operator &
:
$ ping www.samouczekprogramisty.pl > ~/ping_output.txt &
[1] 11410
W przykładzie powyżej wywołałem program ping
i przekierowałem standardowe wyjście do pliku ~/ping_output.txt
. Kolejna linia [1] 11410
informuje o tym, że zadanie [1]
działające w tle zostało uruchomione. Zadanie to działa jako proces 11410.
W każdym momencie możesz sprawdzić listę zadań używając programu jobs
:
$ jobs
[1]+ Running ping www.samouczekprogramisty.pl > ~/ping_output.txt &
W tym przypadku uruchomione jest jedno zadanie w tle, które ma status Running
. Możesz „przywołać” to zadanie używając programu fg
(od ang. foreground):
$ fg %1
ping www.samouczekprogramisty.pl > ~/ping_output.txt
W zarządzaniu zadaniami pomocny jest też skrót klawiaturowy <Ctrl+Z>
, który usypia aktualny program4:
$ fg %1
ping www.samouczekprogramisty.pl > ~/ping_output.txt
^Z # tu użyłem Ctrl+Z
[1]+ Stopped ping www.samouczekprogramisty.pl > ~/ping_output.txt
Jak widzisz w tym przypadku zadanie [1]
ma status Stopped
. Jeśli chcesz wznowić zatrzymany program w tle użyj programu bg
(od ang. background):
$ jobs
[1]+ Stopped ping www.samouczekprogramisty.pl > ~/ping_output.txt
$ bg %1
[1]+ ping www.samouczekprogramisty.pl > ~/ping_output.txt
$ jobs
[1]+ Running ping www.samouczekprogramisty.pl > ~/ping_output.txt &
Zmienne środowiskowe
Uruchomienie programu wiąże się z uruchomieniem procesu. Proces nadzorowany jest przez system operacyjny. Każdy proces posiada, między innymi, swój zestaw zmiennych środowiskowych.
Można powiedzieć, że zmienne środowiskowe są podobne do zmiennych w językach programowania. Zmienne środowiskowe zawierają dane, które dostępne są dla procesu (programu). Zazwyczaj nazwy zmiennych środowiskowych używają wielkich liter, choć nie jest to wymagane. Kilka przykładowych zmiennych środowiskowych:
PATH
– zawiera listę katalogów, w których poszukiwane są programy do uruchomienia. To dzięki tej zmiennej możesz napisaćls
bez podawania pełnej ścieżki programu (/bin/ls
),HOME
– zawiera ścieżkę do katalogu domowego użytkownika,EDITOR
– zawiera ścieżkę do preferowanego edytora tekstu,PPID
– zawiera identyfikator procesu nadrzędnego (tego, który uruchomił aktualny proces).
Możesz sprawdzić aktualną listę zmiennych środowiskowych wywołując program set
bez żadnych parametrów5:
$ set | head -n 1
BASH=/bin/bash
Przykład poniżej pokazuje użycie zmiennych środowiskowych:
$ echo $HOME
/home/mapi
$ echo $HOMEsweetHOME
$ echo ${HOME}sweetHOME
/home/mapisweetHOME
Pierwsza komenda wyświetla zawartość zmiennej HOME
. Druga zawartość zmiennej HOMEsweetHOME
. Zauważ, że w tym przypadku bash
nie wie gdzie kończy się nazwa zmiennej środowiskowej. Dlatego właśnie wyświetla pustą linię – zmienna HOMEsweetHOME
nie jest zdefiniowana. W trzecim przypadku użyłem składni ${}
6 otaczając nawiasami klamrowymi nazwę zmiennej.
Możesz też definiować swoje zmienne środowiskowe używając składni NAZWA_ZMIENNEJ=wartosc zmiennej
:
$ echo $NEW_VARIABLE
$ NEW_VARIABLE="some value"
$ echo $NEW_VARIABLE
some value
Zmienne środowiskowe w procesach potomnych
Wiesz już, że zmienne środowiskowe przypisane są do procesu. Każdy proces ma swoją kopię zmiennych środowiskowych. Uruchamiając nowy proces eksportowane zmienne środowiskowe kopiowane są do procesu potomnego. Oznacza to tyle, że proces potomny ma dostęp wyłącznie do podzbioru zmiennych aktualnie zdefiniowanych.
Zmienną środowiskową możesz eksportować używając programu export
. Proszę spójrz na przykład:
$ VARIABLE_1=value1
$ export VARIABLE_2=value2
$ echo $VARIABLE_1 $VARIABLE_2 $PPID
value1 value2 2855
$ bash # uruchamia nowy proces
$ echo $VARIABLE_1 $VARIABLE_2 $PPID
value2 10189
W przykładzie możesz zobaczyć dwie zmienne: VARIABLE_1
i VARIABLE_2
. Druga z nich została wyeksportowana. Dzięki temu jest dostępne w procesie potomnym.
Dodatkowe materiały do nauki
Podobnie jak w poprzednim artkule z serii jako pierwsze źródło polecę Ci dokumentację. Znów odsyłam cię do programu man
lub wbudowanej dokumentacji, którą możesz przeczytać uruchamiając <program> --help
.
W przypadku tego artykułu nieocenionym źródłem wiedzy będzie dokumentacja programu bash
, którą możesz przeczytać po uruchomieniu man bash
lub online.
Możesz też rzucić okiem na stronę https://explainshell.com, która pozwoli Ci lepiej zrozumieć bardziej skomplikowane komendy.
Niezmiennie zachęcam Cię do samodzielnych eksperymentów. Najwięcej nauczysz się samodzielnie bawiąc się linią poleceń.
Podsumowanie
Po lekturze tego artykułu możesz spokojnie używać linii poleceń w codziennej pracy. Udało Ci się poznać zestaw przydatnych cech bash
‘a. Potrafisz swobodnie poruszać się po historii poleceń i ją modyfikować w razie potrzeby. Wiesz więcej o zmiennych środowiskowych i rozumiesz jaka jest zależność pomiędzy procesem a zmienną środowiskową. Gratulacje! :)
To tyle na dzisiaj, dziękuję za lekturę, trzymaj się i do następnego razu! A… zapomniałbym, jeśli uważasz, że materiał może się przydać komuś z Twoich znajomych proszę podziel się z nim odnośnikiem do artykułu. W ten sposób pomożesz mi dotrzeć do nowych czytelników, z góry dziękuję! Jeśli nie chcesz pomiąć kolejnych artykułów dopisz się do samouczkowego newslettera i polub Samouczka Programisty na Facebooku.
Do następnego razu!
-
Zupełnie inną sprawą są programy, które pozwalają na używanie wyrażeń regularnych w przekazanych parametrach. ↩
-
Chociaż szczerze mówiąc częściej używam zmiennej środowiskowej
PWD
lub wywołuję programpwd
;) ↩ -
Ten mechanizm zależy od wartości zmiennej środowiskowej
HISTCONTROL
. ↩ -
Tak na prawdę to wysyła sygnał do procesu. To w jaki sposób ten sygnał jest obsłużony do inna sprawa. Domyślnie program jest „usypiany”. ↩
-
Program ten wyświetla też listę dostępnych funkcji. ↩
-
To mechanizm rozwijania parametrów, podobny do rozwijania
~
czy rozwijania{}
. ↩
Pobierz opracowania zadań z rozmów kwalifikacyjnych
Przygotowałem rozwiązania kilku zadań algorytmicznych z rozmów kwalifikacyjnych. Rozkładam je na czynniki pierwsze i pokazuję różne sposoby ich rozwiązania. Dołącz do grupy ponad 6147 Samouków, którzy jako pierwsi dowiadują się o nowych treściach na blogu, a prześlę je na Twój e-mail.
Zostaw komentarz