Useful commands on a server with Linux

3 august 2023
Jakub Rojek Jakub Rojek
Photo by Sora Shimazaki from Pexels (https://www.pexels.com/pl-pl/zdjecie/crop-cyber-spy-hacking-system-podczas-pisania-na-laptopie-5935794/)
Categories: Administration, IT fundamentals, Guides

Wiemy o tym, że na serwerach dla aplikacji w dużej mierze instalowany jest system operacyjny Linux, który pod wieloma względami różni się od znanego prawie wszystkim Windowsa. Odnalezienie się w mechanizmach spod znaku pingwina nie dla wszystkich jest łatwe i osoby niedoświadczone różnie reagują na fakt, że muszą z nim mieć styczność. Niektórzy przyjmują postawę, że będą mieli okazję się czegoś nauczyć, a innych paraliżuje ze strachu, gdyż boją się, że każdy ich błąd będzie miał poważne konsekwencje. Jeszcze inni nie boją niczego i nie czytają dokumentacji, przez co faktycznie potem te poważne konsekwencje się ujawniają.

Tym niemniej, co jakiś czas staramy się Was przekonać, że Linux i środowisko serwerowe w podstawowym zakresie naprawdę nie są takie trudne, a ich znajomość choćby w fundamentalnych sprawach naprawdę się przydaje. Jest to wiedza, którą można wykorzystać nie tylko w pracy, ale też w projektach domowych czy przy próbie zrozumienia bardziej złożonych problemów, co przynosi okazję do rozwoju. Czasem naprawdę wystarczy umiejętność poruszania się po folderach, uruchomienie skryptu, zrestartowanie usługi, skopiowanie pliku i inne operacje, których poznanie nie wymaga dużo pracy, a już można w ten sposób dużo osiągnąć.

Aczkolwiek czasem trzeba ciut więcej i dzisiaj właśnie tym się zajmiemy. Pokażemy sobie parę komend, które mogą nie być na początku intuicyjne lub są na tyle złożone, że nie zawsze człowiek pamięta ich składnię i musi szukać w dokumentacji lub na forach. Jednocześnie, nie są to operacje, które są zarezerwowane dla administratorów - bo trudno nazwać takimi np. spakowanie plików czy przesłanie go z serwera do lokalnego komputera. Jednak nie wykonuje się ich codziennie, więc nie utrwaliły się w pamięci, a czasem się po prostu przydają. Spróbujemy dzisiaj krótko omówić parę takich komend, zdecydowanie skupiając się na praktyce i przez to upraszczając wiele aspektów. Warto zauważyć, że wiele z poniższych kwestii można w Windowsie przetrenować za pomocą choćby WSL-a.

Pakowanie i rozpakowywanie plików

Zaskakująco często pojawia się problem przy obsłudze plików archiwum w linii komend. Rzeczywiście - wymaga to zapamiętania określonej kolejności parametrów czy przełączników lub ciągłego zaglądania do manuala. Tym niemniej, warto się tego nauczyć lub gdzieś zapisać obok monitora, ponieważ przy kopiowaniu dużej ilości plików na lub z serwera naprawdę warto robić to w postaci zarchiwizowanych paczek. Kto często kopiuje w ten sposób tysiące plików, ten wie, o czym mowa.

Najczęściej spotykamy się z dwoma formatami - .zip oraz .tar.gz (lub .tgz zwany też tarballem). Ten pierwszy jest wygodniejszy, jednak wymaga zainstalowania na serwerze programów zip oraz unzip, a nie zawsze są one dostępne. Polecenia do obsługi drugiego formatu są dostępne wszędzie, aczkolwiek kształt zapytania jest mniej intuicyjny na pierwszy rzut oka.

Składnia komendy zip to w ogólności zip [opcje] nazwa_pliku.zip pliki_do_spakowania. W najprostszym przypadku, chcąc spakować wszystkie pliki w aktualnym folderze, zrobimy to tak:

zip test.zip *

Oczywiście, zamiast gwiazdki możemy podawać konkretne pliki lub foldery po spacji, choć uwaga - w tym drugim przypadku należy użyć opcji -r oznaczającą rekursywne przeszukiwanie podfolderów.

zip -r test.zip a.txt dir

Przydatne mogą być też komendy umożliwiające spakowanie wszystkiego z wyłączeniem konkretnych pozycji oraz spakowanie z hasłem (po wykonaniu komendy zostaniemy poproszeni o jego podanie).

zip -r test.zip * -x fileToIgnore.txt -x dirWithContentToIgnore/*
zip -e test.zip *

Aby rozpakować takie archiwum, najprościej jest wykonać komendę unzip test.zip, jednak to utworzy zawartość w tym samym folderze, w którym się znajdujemy (niekoniecznie tam, gdzie jest sam plik). Aby jawnie wskazać katalog docelowy, należy wykorzystać przełącznik -d:

unzip test.zip -d folderDoRozpakowania

Tak to wygląda dla rozszerzenia .zip. A jak jest w przypadku .tar.gz (.tgz)? Poniżej wszystkie powyższe komendy, jednak właśnie w wersji tarballowej:

tar cvfz test.tar.gz *
tar cvfz test.tar.gz a.txt dir # zwróćcie uwagę, że nie trzeba używać "-r" przy folderach
tar --exclude fileToIgnore.txt --exclude dirWithContentToIgnore cvfz test.tar.gz *
tar xvfz test.tar.gz
tar xvfz test.tar.gz -C folderDoRozpakowania # uwaga: folder musi wcześniej istnieć

Pewien problem pojawia się przy próbie nadania hasła archiwum, gdyż tarball nie ma wbudowanej takiej możliwości. Można wówczas zaszyfrować plik za pomocą narzędzia GPG i komendy gpg -c test.tar.gz.gpg test.tar.gz.

Wypada jeszcze wyjaśnić, co oznaczają te dziwne symbole przy komendzie tar. Poniżej wyjaśnienie poszczególnych opcji:

  • c - utworzenie archiwum.
  • x - rozpakowanie archiwum.
  • z - wykorzystanie Gunzipa, a więc uczynienie z .tar archiwum .tar.gz, stosującego kompresję.
  • v - od verbose, a więc wyświetlenia logu z operacji, w tym też przetwarzanych plików.
  • f - wskazanie nazwy pliku z utworzonym archiwum.

Przesyłanie plików przez SCP

W większości przypadków, chcąc przesłać pliki do serwera dostępnego przez SSH lub przeciwnie - pobrać z niego - korzystamy z programów graficznych pokroju WinSCP czy terminali z obsługą GUI, jak MobaXterm. Jednak czasem nie jest to możliwe lub staje się znacznie utrudnione poprzez konkretną metodę wejścia na taki serwer. W takim przypadku niektórzy nie bardzo wiedzą, w jaki sposób wówczas wczytać pliki. Czasem próbują pobrać z zewnętrznego źródła poprzez wget, inni zwyczajnie przepisują zawartość lub wklejają ją w edytor (przy okazji - czasem wystarczy po prostu zaznaczyć tekst, aby go skopiować i nacisnąć prawy przycisk myszy, aby wkleić). A przecież istnieje prostszy sposób - poznajcie polecenie scp.

Podobnie jak ssh, także scp pozwala połączyć się poprzez port 22, jednak nie służy do zalogowania się lub wykonania zdalnie określonej komendy - scp daje możliwość transferowania plików pomiędzy takim serwerem a naszą maszyną lub innym serwerem. Budowa jest prosta:

scp ścieżka/do/pliku user@serwer:folder/na/serwerze
scp user@serwer:folder/na/serwerze/plik ścieżka/do/folderu

W pierwszym przypadku wysyłamy plik na serwer, a w drugim - pobieramy z niego. Spójrzmy na praktyczny przykład, gdzie naszym użytkownikiem na serwerze jest "ubuntu", adres IP to 192.168.100.31, a plik "plik.txt" chcemy umieścić w folderze domowym w podkatalogu "backup". Zakładamy, że ten folder już istnieje.

scp plik.txt ubuntu@192.168.100.31:~/backup

I już. W ten sposób możemy też przesyłać całe foldery, o ile nie zapomnimy o przełączniku -r.

scp -r folder/* ubuntu@192.168.100.31:~/backup

Podobnie sprawa wygląda, gdy chcemy pobrać plik na dysk - w tym przypadku będzie to "plik.txt", który zapiszemy u nas jako "plik_z_serwera.txt".

scp ubuntu@192.168.100.31:~/backup/plik.txt plik_z_serwera.txt

Z pewnością zauważyliście, że kluczowa jest kolejność argumentów - na pierwszym miejscu jest miejsce, z którego plik lub folder jest wysyłany, a jako drugi zostaje podany odbiorca. Jeśli jednym z tych punktów jest zewnętrzny serwer, należy podać użytkownika, znak "@", adres serwera (domenowy lub IP), a następnie po dwukropku ścieżkę, która nas interesuje. Warto zauważyć, że na serwerze musi istnieć dana ścieżka i co więcej - terminal nie podpowie nam jej po naciśnięciu klawisza TAB. Dodatkowo, jeśli połączenie z serwerem wymaga podania jawnie klucza prywatnego (-i klucz.key), to również musimy dodać to do komendy scp, tak jakbyśmy korzystali z ssh.

Sprawdzanie miejsca na dysku

Maszyny serwerowe oferują pewną przestrzeń dyskową, która może się zapełnić - to oczywiste. Problem pojawia się wtedy, kiedy nagle zabrakło miejsca, a trzeba szybko zobaczyć, co tyle zajmuje, aby odzyskać trochę megabajtów. Najczęściej są to kryzysowe sytuacje (np. klient zgłasza, że nie może się zalogować do aplikacji lub przesłać żadnego pliku) i jeśli nie umiemy wówczas tego poprawnie zrobić, to trochę się pomęczymy. Pomijam standardowe wylistowanie plików z danego folderu wraz z m.in. rozmiarem poprzez komendę ls -l - to pozwala uzyskać nam pewien obraz sytuacji, ale tylko w kontekście danego katalogu.

Nas będzie bardziej interesować polecenie df -H, które pokaże rozmiar poszczególnych dysków, wolne i zajęte miejsce oraz procent zajętości. W ten sposób łatwo się zorientować w tym, czy zabrakło miejsca - jeśli w głównym dysku "Use%" stanowi 100%, to oznacza, że trzeba się czegoś pozbyć lub dołożyć pojemności. To polecenie pochodzi od "disk free" i "human readable" - można też zamienić ostatni parametr na "-h", aby pokazać wartości podzielone przez 1024, a nie 1000.

Warto zaznaczyć, że df skupia się na partycjach, co nie jest szczególnie użyteczne, gdy chcemy dowiedzieć się, który folder zajmuje tyle miejsca. Wówczas bardziej skłonimy się ku poleceniu du, które wykonane w odpowiednim folderze wypisze nam wszystkie podfoldery i pliki wraz z informacją o rozmiarze. Niestety, zrobi to w bardzo nieczytelnej formie, a w przypadku dużej liczby plików dostaniemy po prostu niekończący się wypis, który uniemożliwi nam szybką reakcję poza kombinacją CTRL + C.

Dlatego jeszcze bardziej warto zapamiętać taką komendę:

du -h --max-depth 1

Wypisze nam ona zajętość na dysku nie tylko w skróconym formacie (choć przy porównywaniu liczb czasem warto jednak zrezygnować z przełącznika "-h"), ale także tylko następnych podfolderów na konkretnej głębokości. Oznacza to, że jeśli folder, w którym się znajdujemy, zawiera katalogi X, Y i Z, a katalog X zawiera X1 oraz X2, katalog Y ma w sobie Y1 i Y2, to zobaczymy tylko zajętość X, Y i Z. Słowem - na tej podstawie możemy ocenić, który folder opłaca się dalej analizować, gdyż jest największy.

Jak się pewnie domyślacie, zmiana parametru --max-depth z 1 na 2, 3 itd. pogłębi informacje dotyczące poszczególnych podfolderów. Z drugiej strony ustawienie tego atrybutu na 0 lub zmienienie go na -s (du -hs) pokaże jedynie łączną zajętość całego folderu, co również może być czasem przydatne. Przypomina to wywołanie "Właściwości" na katalogu w systemie Windows - zobaczymy totalny rozmiar, razem z jego podfolderami i plikami.

Na uwagę zasługuje także to, że po dodaniu opcji --time uzyskamy czas ostatniej modyfikacji danego pliku lub folderu, a w ogóle możemy też wskazać ręcznie folder, dla którego chcemy obliczyć rozmiar - nie jesteśmy ograniczeni do katalogu, w którym aktualnie się znajdujemy.

du -h --max-depth 1 --time innyFolder

Szukanie plików i ich zawartości (find i grep)

Wyszukiwanie czegokolwiek na dysku często jest potrzebne, szczególnie, jeśli trafiamy na maszynę, o której nie mamy pojęcia, gdzie co się znajduje, jak np. serwer z aplikacją tworzoną przez inny zespół. Równie często interesuje nas nie tyle plik, co jego zawartość, gdyż wiemy, że tam znajduje się coś, czego szukamy (np. konfiguracja dostępu do bazy danych). Jak sobie wówczas poradzić? Większość dobrze wie, że istnieją polecenia find oraz grep, ale zdarza się, że jest problem z ich wykorzystaniem.

Powiedzmy, że jesteśmy w katalogu, w którym chcemy wyszukać wszystkie pliki z rozszerzeniem .xlsx. Jak to zrobimy?

find . -name '*.xlsx'

Po nazwie polecenia podajemy lokalizację, w której chcemy zacząć wyszukać. W naszym przypadku będzie to kropka, a więc aktualny katalog, w którym się znajdujemy. Jeśli zupełnie nie wiemy, gdzie coś się znajduje, w tym miejscu wstawimy zapewne slash (/), jednak musimy zdawać sobie sprawę z tego, że wyszukiwanie może potrwać bardzo długo.

Później czeka nas podanie wytycznych - u nas będzie to -name (zwracam uwage na pojedynczy myślnik), po którym następuje szablon nazwy wykorzystujący wzorzec regularny. Oczywiście, jeśli znamy dokładną nazwę pliku, to możemy spróbować wpisać ją bezpośrednio, bez żadnych gwiazdek imitujących dowolny ciąg znaków. Sam parametr -name nie jest jedyną opcją - jak się pewnie spodziewacie, można wyszukiwać po tym, czy jest to plik, czy folder (-type), nie zwracać uwagę na wielkość liter (-iname), wziąć pod uwagę czas ostatniej modyfikacji (-mtime -liczbadni), a także m.in. rozmiar (-size). Możliwości jest bardzo dużo, a same warunki można łączyć, także poprzez alternatywę. Wiele informacji i praktyczne przykłady znajdziecie pod tym linkiem.

Jeszcze jedna mała uwaga - przy przeszukiwaniu całego systemu plików często będziemy napotykali na komunikaty typu "permission denied", co jest zrozumiałe - zazwyczaj nie mamy uprawnień do odczytu wszystkich folderów, więc przy przeszukiwaniu ich Linux posłusznie poinformuje, że ich nie przejrzymy, bo nie. Jeśli takich uwag jest dość dużo, może być to męczące, jednak istnieje sposób, aby się ich pozbyć. W tym celu można wykorzystać "plik-pustkę" /dev/null (zresztą niekiedy występujący w informatycznych memach) i przekierować do niego tzw. wyjście błędu. Zdaję sobie sprawę, że dla niektórych brzmi to jak czarna magia, jednak wytłumaczenie tego w pełni wymagałoby podjęcia tematu standardowych wyjść, a to nie jest celem tego tekstu. Po więcej informacji odsyłam do tej strony.

find / -name '*.xlsx' -print 2>/dev/null

Jednak chyba częściej będziemy chcieli przeszukać zawartość plików za pomocą polecenia grep. Zacznijmy od prostego przypadku - załóżmy, że chcemy znaleźć słowo "zadanie" w konkretnym pliku i nie interesuje nas wielkość znaków. Użyjemy wówczas polecenia:

grep -i 'zadanie' plik.txt

Warto zauważyć, że nawet, jeśli słowo "zadanie" występuje jako element innego ciągu, to również zostanie znaleziony. Jeśli tego nie chcemy, warto użyć przełącznika -w. Trzeba też pamiętać, że narzędzie szuka po tekstowej zawartości plików - nie odnajdziemy w ten sposób ciągów w plikach binarnych.

To teraz przeszukajmy folder "arkusze" pod kątem ciągu "Ania" wraz z wypisaniem pliku oraz numerem wiersza:

grep -rn 'Ania' arkusze

Jeśli jakieś polecenie zwraca zawartość pliku (np. cat), to możemy je również wykorzystać jako bazę do przeszukania go pod kątem zawartości, wykorzystując tzw. pipe'y:

cat /proc/cpuinfo | grep -i 'Model'

W przypadku obu omawianych tutaj komend, wyników może być tak wiele, że trudno będzie je zmieścić na ekranie. W takim przypadku warto skorzystać z przekierowania rezultatu do określonego pliku:

grep -rn 'Ania' arkusze > output.txt

Bardzo wiele przykładów użycia komendy grep znajdziecie pod tym linkiem.

Usuwanie plików starszych niż X dni

Skoro już jesteśmy w temacie wyszukiwania plików według różnych kryteriów, to warto wspomnieć o możliwości usuwania takich, które zostaną znalezione. W tym momencie niektórzy przypomną, że przecież do kasowania plików służy polecenie rm, ale nie do końca umożliwia ono podanie wszystkich potrzebnych nam parametrów. Na szczęście, z pomocą przychodzi nam przed chwilą omówiony find - zobaczmy, jak możemy usunąć wszystkie pliki z kopią zapasową bazy SQL starsze niż 100 dni.

find backups/ -name "backup_*.sql" -type f -mtime +100 -delete

Kluczowy jest tutaj parametr -mtime, który pozwala odfiltrować pliki po wieku (zakładamy, że zrobienie kopii zapasowej to jednocześnie jej ostatnia modyfikacja), a także parametr -delete. Nie muszę chyba mówić, że przez to, iż ta komenda jest tak potężna, należy bardzo uważać z jej wywoływaniem?

Wyświetlenie części pliku

Czasem chcemy wszystko wyświetlić określony plik. Służy do tego polecenie cat, które zwyczajnie pokazuje zawartość na ekranie. Przykład:

cat plik.txt

Jednak problem polega na tym, że w przypadku bardzo długiego tekstu nasz terminal zostanie nim po prostu "zalany". Można to rozwiązać na różne sposoby, jak np. zamianę polecenia cat na more lub less, które pozwalają przesuwać się po pliku, w ten sposób stopniowo go przeglądając. Jest to również użyteczne przy plikach tak długich, że stanowią wyzwanie dla stabilności systemu (choć tutaj przydaje się również Vim). Jednak równie użyteczną komendą w przypadku długiej zawartości może być tail, który pozwala wyświetlić N ostatnich wierszy. Przykład dla 50 linii:

tail -n 50 /var/log/apache2/error.log

Użycie w przykładzie pliku z logiem nie jest przypadkowe - to właśnie w nich najczęściej interesują nas ostatnie linijki, gdy szukamy błędu, który przed chwilą wystąpił.

Lista aktywnych procesów

Bywa, że chcemy sprawdzić, jakie procesy są uruchomione na serwerze, aby np. upewnić się, że dane zadanie wystartowało i działa. W podstawowej wersji możemy użyć do tego polecenia ps, jednak w wielu przypadkach jest ono niewystarczające i warto dopisać dodatkowe trzy przełączniki, tworząc ps alx. Na takiej liście możemy zobaczyć nie tylko polecenia, które są wykonywane, ale też m.in. PID (ID procesu) czy status (np. czy uruchomiony, zatrzymany).

Istnieje jeszcze możliwość, która pokazuje również procent użycia procesora oraz pamięci - tutaj może się przydać ps aux, ale ciekawsze jest top. To drugie ma tę świetną cechę, że pokazuje listę procesów w sposób dynamiczny, pozwalając obserwować, jak się zmienia w czasie. Może być to bardzo ciekawe przy badaniu problemów wydajnościowych serwera lub operacji, które go blokują. Wyjść z tego okna można poprzez naciśnięcie klawisza "q". A jeśli ktoś potrzebuje czegoś ciut ładniejszego, to może dodatkowo doinstalować htop.

Załóżmy, że istnieje proces, który zakłóca działanie systemu i chcemy go zakończyć w najbardziej brutalnej formie - poprzez wysłanie sygnału KILL, dostępnego również pod kodem 9. Aby to zrobić, musimy znać PID danego zadania, co możemy odczytać poprzez jedną z poprzednich komend. Wówczas wiedząc, że interesujący nas proces znajduje się pod PID-em 6789 możemy wywołać:

kill -9 6789

Zdarzają się jednak sytuacje, w których problem dotyczy wielu podobnych procesów o takiej samej nazwie. Przykładem może być interpreter PHP-a, który uruchomił się 40 razy i we wszystkich przypadkach się zawiesił lub zakleszczył. Wówczas nie tyle interesuje nas PID, co nazwa procesu i polecenie killall. Przykładowo, dla zatrzymania php-fpm byłoby to:

killall -9 php-fpm

Oczywiście, także w tym przypadku należy zachować daleko idącą ostrożność.

Sprawdzenie aktywnych portów

Czasem chcemy zobaczyć, na których portach nasłuchuje nasz serwer, aby potwierdzić, że jakaś usługa sieciowa została uruchomiona. Wbrew pozorom, to pozornie proste zadanie potrafi czasem sprawić trochę kłopotów ze względu na to, że w różnych systemach zachowuje się to nieco inaczej. Najczęściej weryfikacji można dokonać poprzez:

netstat -anp tcp

co ukaże wszystkie procesy działające pod portami TCP wraz z numerem portów i informacją, czy nasłuchują. W przypadku długiej listy można posłużyć się wspomnianym wcześniej poleceniem grep:

netstat -anp tcp | grep LISTEN

A żeby ułatwić sobie zadanie, zawsze można spróbować wykryć otwarte porty na zewnątrz poprzez odpowiednie programy czy telnet.

Podsumowanie

W żaden sposób nie wyczerpaliśmy tematu. Zdaję sobie też sprawę, że administratorzy systemów linuksowych patrzą na ten artykuł z niesmakiem, gdyż takie rzeczy, a nawet trudniejsze, robią codziennie i to z zamkniętymi oczami. Tym niemniej, wiemy, że nie dla wszystkich są one oczywiste i takie ściągi bywają potrzebne. Nie zawsze mamy do dyspozycji graficzne narzędzia i interfejsy, które część pracy wykonają za nas - bywa, że jedyną drogą staje się linia komend. Dlatego warto ją poznawać i przestać sie jej bać. A przy okazji rozwijać swoją znajomość systemów unixowych, gdyż informatykowi na pewno to nie zaszkodzi, a może się przydać.

Pozdrawiam i dziękuję - Jakub Rojek.

We write not only blog articles, but also applications and documentation for our clients. See who we have worked with so far.

About author

Jakub Rojek

Lead programmer and co-owner of Wilda Software, with many years of experience in software creation and development, but also in writing texts for various blogs. A trained analyst and IT systems architect. At the same time he is a graduate of Poznan University of Technology and occasionally teaches at this university. In his free time, he enjoys playing video games (mainly card games), reading books, watching american football and e-sport, discovering heavier music, and pointing out other people's language mistakes.

Jakub Rojek