Patrice zidentyfikował źródło problemu w swojej odpowiedzi , ale jeśli chcesz dowiedzieć się, jak się stąd dowiedzieć, dlaczego to robisz, oto długa historia.
Bieżący katalog roboczy procesu nie jest niczym zbyt skomplikowanym. Jest to atrybut procesu będący dojściem do pliku katalogu typu, od którego zaczynają się ścieżki względne (w wywołaniach systemowych wykonywanych przez proces). Podczas rozwiązywania ścieżki względnej jądro nie musi znać (a) pełnej ścieżki do bieżącego katalogu, po prostu odczytuje pozycje katalogu w tym pliku katalogu, aby znaleźć pierwszy składnik ścieżki względnej (i ..
jest jak każdy inny w tym zakresie) i kontynuuje od tego momentu.
Teraz, jako użytkownik, czasami lubisz wiedzieć, gdzie ten katalog znajduje się w drzewie katalogów. W większości Unices drzewo katalogów jest drzewem bez pętli. Oznacza to, że istnieje tylko jedna ścieżka od katalogu głównego tree ( /
) do dowolnego podanego pliku. Ścieżka ta jest ogólnie nazywana ścieżką kanoniczną.
Aby uzyskać ścieżkę do bieżącego katalogu roboczego, proces musi po prostu pójść w górę (dobrze w dół, jeśli chcesz zobaczyć drzewo z jego korzeniem u dołu), drzewo z powrotem do katalogu głównego, znajdując nazwy węzłów w drodze.
Na przykład proces próbujący dowiedzieć się, że jego bieżącym katalogiem jest /a/b/c
, otworzyłby ..
katalog (ścieżka względna, podobnie ..
jak wpis w bieżącym katalogu) i szukałby katalogu typu o tym samym numerze i-węzła, jak .
, aby dowiedzieć się, że c
dopasowuje, a następnie otwiera ../..
i tak dalej, aż znajdzie /
. Nie ma w tym dwuznaczności.
To właśnie robią lub przynajmniej robią funkcje getwd()
lub getcwd()
C.
W niektórych systemach, takich jak współczesny Linux, istnieje wywołanie systemowe, które zwraca kanoniczną ścieżkę do bieżącego katalogu, który wykonuje to wyszukiwanie w przestrzeni jądra (i pozwala znaleźć twój bieżący katalog, nawet jeśli nie masz dostępu do odczytu wszystkich jego składników) i to właśnie getcwd()
tam wzywa. W nowoczesnym systemie Linux można również znaleźć ścieżkę do bieżącego katalogu za pomocą readlink () na /proc/self/cwd
.
Tak właśnie robi większość języków i wczesnych powłok, zwracając ścieżkę do bieżącego katalogu.
W twoim przypadku można nazwać cd a
ponieważ może razy, ile chcesz, bo to dowiązanie do .
, bieżący katalog nie zmienia się więc wszyscy getcwd()
, pwd -P
, python -c 'import os; print os.getcwd()'
, perl -MPOSIX -le 'print getcwd'
by przywrócić ${HOME}
.
Teraz dowiązania symboliczne skomplikowały to wszystko.
symlinks
zezwól na skoki w drzewie katalogów. W /a/b/c
, jeśli /a
lub /a/b
lub /a/b/c
jest dowiązaniem symbolicznym, wówczas kanoniczna ścieżka /a/b/c
byłaby czymś zupełnie innym. W szczególności ..
wpis /a/b/c
nie jest konieczny /a/b
.
W powłoce Bourne'a, jeśli:
cd /a/b/c
cd ..
Lub nawet:
cd /a/b/c/..
Nie ma gwarancji, że skończysz /a/b
.
Tak jak:
vi /a/b/c/../d
niekoniecznie jest taki sam jak:
vi /a/b/d
ksh
wprowadził koncepcję logicznego bieżącego katalogu roboczego, aby jakoś obejść ten problem. Ludzie się do tego przyzwyczaili, a POSIX ostatecznie określił takie zachowanie, co oznacza, że większość powłok również to robi:
Dla cd
i pwd
wbudowanych poleceń ( i tylko dla nich (choć także dla popd
/ pushd
na muszli, które mają je)), powłoka utrzymuje swój własny pomysł bieżącego katalogu roboczego. Jest przechowywany w $PWD
specjalnej zmiennej.
Kiedy to zrobisz:
cd c/d
nawet jeśli c
lub c/d
są dowiązaniami symbolicznymi, podczas gdy $PWD
zawiera /a/b
, dołącza c/d
się do końca, tak $PWD
staje się /a/b/c/d
. A kiedy to zrobisz:
cd ../e
Zamiast robić chdir("../e")
, to robi chdir("/a/b/c/e")
.
A pwd
polecenie zwraca tylko zawartość $PWD
zmiennej.
Jest to przydatne w interaktywnych powłokach, ponieważ pwd
wyświetla ścieżkę do bieżącego katalogu, która zawiera informacje o tym, jak się tam dostałeś, i dopóki używasz tylko ..
argumentów, cd
a nie innych poleceń, rzadziej cię zaskoczy, ponieważ cd a; cd ..
lub cd a/..
ogólnie by cię odzyskał gdzie byłeś
Teraz $PWD
nie jest modyfikowany, chyba że wykonasz cd
. Do czasu następnego połączenia cd
lub pwd
wielu rzeczy może się zdarzyć, $PWD
można zmienić nazwę dowolnego elementu . Bieżący katalog nigdy się nie zmienia (zawsze jest to ten sam i-węzeł, chociaż można go usunąć), ale jego ścieżka w drzewie katalogów może ulec całkowitej zmianie. getcwd()
oblicza bieżący katalog za każdym razem, gdy jest wywoływany, idąc po drzewie katalogów, dzięki czemu jego informacje są zawsze dokładne, ale w przypadku katalogu logicznego implementowanego przez powłoki POSIX, informacje w nim $PWD
mogą stać się nieaktualne. Więc po uruchomieniu cd
lub pwd
niektóre pociski mogą chcieć się przed tym uchronić.
W tym konkretnym przypadku widać różne zachowania z różnymi powłokami.
Niektórzy lubią ksh93
całkowicie zignorować problem, więc zwrócą nieprawidłowe informacje nawet po zadzwonieniu cd
(i nie zobaczylibyśmy takiego zachowania bash
).
Niektórzy lubią bash
lub zsh
zrobić sprawdzić, czy $PWD
nadal jest ścieżka do katalogu bieżącym momencie cd
, ale nie po pwd
.
pdksh sprawdza zarówno pwd
i cd
(ale pwd
nie aktualizuje $PWD
)
ash
(przynajmniej ten znaleziony w Debianie) nie sprawdza, a kiedy to robisz cd a
, faktycznie tak się dzieje cd "$PWD/a"
, więc jeśli bieżący katalog się zmienił i $PWD
nie wskazuje już na bieżący katalog, faktycznie nie zmieni się na a
katalog w bieżącym katalogu , ale ten w $PWD
(i zwraca błąd, jeśli nie istnieje).
Jeśli chcesz się nim bawić, możesz:
cd
mkdir -p a/b
cd a
pwd
mv ~/a ~/b
pwd
echo "$PWD"
cd b
pwd; echo "$PWD"; pwd -P # (and notice the bug in ksh93)
w różnych skorupkach.
W twoim przypadku, ponieważ używasz bash
po cd a
, bash
sprawdza, które $PWD
nadal wskazują na bieżący katalog. W tym celu wywołuje stat()
wartość $PWD
sprawdzania numeru i-węzła i porównywania go z wartością .
.
Ale gdy wyszukiwanie $PWD
ścieżki wiąże się z rozwiązaniem zbyt wielu dowiązań symbolicznych, to stat()
zwraca błąd, więc powłoka nie może sprawdzić, czy $PWD
nadal odpowiada bieżącemu katalogowi, więc oblicza go ponownie getcwd()
i $PWD
odpowiednio aktualizuje .
Teraz, aby wyjaśnić odpowiedź Patrice, sprawdzenie liczby napotkanych dowiązań symbolicznych podczas wyszukiwania ścieżki ma na celu ochronę przed pętlami dowiązań symbolicznych. Najprostszą pętlę można wykonać
rm -f a b
ln -s a b
ln -s b a
Bez tej bezpiecznej ochrony cd a/x
system musiałby znaleźć miejsce, do którego prowadzi a
link, znajduje go b
i jest dowiązaniem symbolicznym, do którego prowadzi a
, i który trwałby bez końca. Najprostszym sposobem, aby się temu zapobiec, jest poddanie się po rozwiązaniu więcej niż dowolnej liczby dowiązań symbolicznych.
Wróćmy do logicznego bieżącego katalogu roboczego i dlaczego nie jest to tak dobra funkcja. Ważne jest, aby zdawać sobie sprawę, że to tylko dla cd
powłoki, a nie innych poleceń.
Na przykład:
cd -- "$dir" && vi -- "$file"
nie zawsze jest taki sam jak:
vi -- "$dir/$file"
Dlatego czasami zdarza się, że ludzie zalecają zawsze używać cd -P
skryptów, aby uniknąć zamieszania (nie chcesz, aby twoje oprogramowanie obsługiwało argumenty ../x
odmienne od innych poleceń tylko dlatego, że jest napisane w powłoce zamiast w innym języku).
-P
Rozwiązaniem jest wyłączenie katalog logiczny obsługi tak cd -P -- "$var"
faktycznie nie zadzwonić chdir()
na treść $var
(z wyjątkiem, gdy $var
jest -
ale to inna historia). A po cd -P
, $PWD
będzie zawierać kanoniczną ścieżkę.