
Pushd i popd, czyli historia odwiedzanych katalogów

Dziś pozwolę sobie przedstawić Państwu, w jaki sposób dość duża część programów jest kompilowana pod Linuksem. Przykładowe wywołania pseudokomend mogą wyglądać następująco. 1. Skopiuj repozytorium z systemu kontroli wersji. 2. Wejdź do katalogu repozytorium. 3. Wywołaj komendy konfiguracyjne i make. 4. Wejdź do katalogu work. 5. Przeczytaj log błędu. 6. Wejdź do katalogu repozytorium (poprzedni […]
Dziś pozwolę sobie przedstawić Państwu, w jaki sposób dość duża część programów jest kompilowana pod Linuksem. Przykładowe wywołania pseudokomend mogą wyglądać następująco.
1. Skopiuj repozytorium z systemu kontroli wersji.
2. Wejdź do katalogu repozytorium.
3. Wywołaj komendy konfiguracyjne i make.
4. Wejdź do katalogu work.
5. Przeczytaj log błędu.
6. Wejdź do katalogu repozytorium (poprzedni katalog).
7. Przeczytaj wygenerowane ustawienia.
8. Zmień katalog na katalog systemowych ustawień repozytoriów.
9. Dodaj repozytorium.
10. Zainstaluj brakującą bibliotekę.
11. Zmień katalog na katalog repozytorium (poprzedni katalog).
12. Wywołaj komendy budujące i instalujące.
Jak nie trudno się domyślić, mamy tutaj część wspólną, jaką bez wątpienia jest „skakanie” po katalogach. W swojej pracy co najmniej kilkadziesiąt razy dziennie wykonuję komendę cd
. Nie bez powodu cd
jest ustawiana jako jedna z najczęściej ignorowanych komend w historii powłoki. Po katalogach skacze się często i gęsto. Dlatego dzisiaj zajmiemy się tym, jak można to robić odrobinkę mądrzej.
CD $OLDPWD
Jeśli wykonując pracę, zechcemy się cofnąć do poprzedniego katalogu (np. katalog ustawień i katalog logów serwera), możemy użyć w tym celu komendy cd -
, która wykorzystuje zmienną $OLDPWD
. Niestety zadziała ona tylko raz. Jest to jednak o tyle ciekawe, że jeśli zmienimy z katalogu B na katalog A, to zmienna $OLDPWD
zostanie ustawiona na katalog B. Zatem znów wywołując cd -
wpadamy w nieskończoną pętlę :)
Prezentuje to ten trywialny przykład:
[root@Normandy ~]# cd /usr/ [root@Normandy usr]# while true; do cd -; sleep 1; done /root /usr /root /usr ^C
Co by nie mówić, domyślny mechanizm historii katalogów jest – dosłownie jedno katalogowy… Co bardziej zawstydzające, z reguły jest on naprawdę wystarczający. Może jednak bycie „power userem” Linuksa nie jest jednak aż takie trudne :)?
popd, pushd i dirs omówienie komend
Jeśli jednak chcielibyśmy poruszać się troszkę sprawniej po historii odwiedzonych przez nas katalogów, to możemy użyć komend popd
, pushd
oraz dirs
. Wszystkie te komendy działają na prostej strukturze algorytmicznej – stosie – i de facto nie są „komendami”, w rozumieniu zewnętrznych programów, lecz wbudowanymi poleceniami powłoki. Ze względu na mnogość opracowań tematu stosu, pozwolę sobie pominąć jego szerokie omówienie. Niemniej chciałbym przytoczyć anegdotę z moich studiów. Mianowicie, podczas omawiania stosu, prowadzący przedmiot chciał nam ułatwić intuicyjne zrozumienie struktury następującymi słowami: Wyobraźcie sobie stos, na przykład kartek. (chwila zamyślenia) Albo nie! Wyobraźcie sobie grobowiec rodzinny. Tak właśnie tworzył z nas informatyków. ;)
Wracając jednak do głównego wątku, komenda pushd
zmienia katalog, wrzucając go równocześnie na stos. popd
wykonuje operację pop na stosie katalogów (pobranie wartości z usunięciem jej ze stosu) i zmienia katalog na pobraną wartość. Z kolei dirs
wyświetla katalogi będące na stosie oraz pozwala w niewielkim stopniu zarządzać nim. Sam stos jest zaimplementowany przy pomocy tablicy (implementacja ta jest efektywna i prosta) znajdującej się w zmiennej $DIRSTACK
.
Na koniec warto wspomnieć, iż po każdym wywołaniu popd
i pushd
dostajemy wyjście identyczne, jak po wywołaniu komendy dirs
. W przypadku długich historii fakt ten zaczyna być po prostu irytujący.
Przykładowe użycie wraz z numerami linii wygląda następująco:
1 [root@garrus ~]# echo ${DIRSTACK[@]} 2 ~ 3 [root@garrus ~]# pushd /root/ 4 ~ ~ 5 [root@garrus ~]# echo ${DIRSTACK[@]} 6 ~ /root 7 [root@garrus ~]# pushd /root/ 8 ~ ~ ~ 9 [root@garrus ~]# echo ${DIRSTACK[@]} 10 ~ /root /root 11 [root@garrus ~]# pushd /etc/ 12 /etc ~ ~ ~ 13 [root@garrus etc]# echo ${DIRSTACK[@]} 14 /etc /root /root /root 15 [root@garrus etc]# popd 16 ~ ~ ~ 17 [root@garrus ~]# popd 18 ~ ~ 19 [root@garrus ~]# popd 20 ~ 21 [root@garrus ~]# popd 22 -bash: popd: directory stack empty 23 [root@garrus ~]# echo $? 24 1
Jako ciekawostkę chciałbym dodać, iż o ile sama komenda dirs
, której domyślne wyjście jest prezentowane po każdym popd
, jak i pushd
, zawsze zamienia $HOME
na ~
, tak w zmiennej $DIRSTACK
sytuacja taka następuje tylko, jeśli jest to element na szczycie stosu (wypisany jako pierwszy). Prezentują to linie 6, 10 i 14. W przykładzie zostało też uwzględnione wyjście $? w przypadku braku katalogów do cofnięcia się.
Jeśli zechcielibyśmy wyczyścić stos katalogów, możemy użyć do tego komendy dirs
z przełącznikiem -c
.
pushd i popd ulepszona wersja
Nie byłbym sobą, gdybym nie pokazał dużo lepszej, moim zdaniem, wersji pushd
i popd
:) Lubię podmienić standardowe cd
na zmodyfikowany pushd
wraz ze zmodyfikowanym popd
. W celu przysłonięcia komendy cd
w moim pliku inicjalizacyjnym basha ($HOME/.bashrc
) znajduje się następujący kod:
pushd() { if [ $# -eq 0 ]; then builtin pushd "$HOME" > /dev/null elif [ $1 == "-" ]; then popd else builtin pushd "$1" > /dev/null fi } popd() { builtin popd > /dev/null } alias cd='pushd' alias back='popd'
Pozwolę sobie wykonać przykładowe użycie:
[Alex@garrus ~]$ cd /usr/share/ [Alex@garrus share]$ dirs /usr/share ~ [Alex@garrus share]$ cd ~/workspace/ [Alex@garrus workspace]$ cd /etc/sysconfig/ [Alex@garrus sysconfig]$ dirs /etc/sysconfig ~/workspace /usr/share ~ [Alex@garrus sysconfig]$ back [Alex@garrus workspace]$ dirs ~/workspace /usr/share ~ [Alex@garrus workspace]$ cd - [Alex@garrus share]$ dirs /usr/share ~ [Alex@garrus share]$ popd [Alex@garrus ~]$ dirs ~ [Alex@garrus ~]$ cd - -bash: popd: directory stack empty [Alex@garrus ~]$
Chciałbym zauważyć, iż rozwiązanie to doskonale radzi sobie zarówno ze standardowym cd
, wracającym do naszego $HOME
, jak i z cd -
, które powinno użyć poprzedniego katalogu. Przy czym w naszej wersji będzie używał popd
zamiast cd $OLDPWD
.
Zakończenie
Mam nadzieję, że spodobał się Państwu ten krótki poradnik i sami zdecydują się Państwo wypróbować pushd
i popd
. Ze względu na fakt, iż cd
jest znacznie krótsze niż pushd
, zachęcam do użycia zaproponowanego przeze mnie aliasu. Zwalnia to z konieczności przestawienia się z cd
na inną komendę. Tym bardziej że jestem pewien, iż u każdego administratora cd
jest zapisane na poziomie pamięci mięśniowej :)