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 :)

blank Autorzy

Artykuły na blogu są pisane przez osoby z zespołu EuroLinux. 80% treści zawdzięczamy naszym developerom, pozostałą część przygotowuje dział sprzedaży lub marketingu. Dokładamy starań, żeby treści były jak najlepsze merytorycznie i językowo, ale nie jesteśmy nieomylni. Jeśli zauważysz coś wartego poprawienia lub wyjaśnienia, będziemy wdzięczni za wiadomość.