Патріс визначив джерело проблеми у своїй відповіді , але якщо ви хочете знати, як звідти дійти до того, чому ви це отримаєте, ось довга історія.
Поточний робочий каталог процесу - це те, що ви вважаєте занадто складним. Це атрибут процесу, який є ручкою до файлу каталогу типу, з якого починаються відносні шляхи (у системних викликах, здійснених процесом). При вирішенні відносного шляху ядро не потрібно знати повний шлях (a) до цього поточного каталогу, воно просто зчитує записи каталогів у цьому файлі каталогу, щоб знайти перший компонент відносного шляху (і ..це як і будь-який інший файл з цього приводу) і продовжується звідти.
Тепер, як користувач, вам іноді подобається знати, де знаходиться цей каталог у дереві каталогів. У більшості Unices дерево каталогів - це дерево, без циклу. Тобто, є лише один шлях від кореня дерева ( /) до будь-якого файлу. Цей шлях взагалі називають канонічним шляхом.
Щоб отримати шлях до поточного робочого каталогу, потрібно зробити лише процес вгору (добре вниз, якщо ви хочете бачити дерево з його коренем внизу) дерево назад до кореня, знаходячи назви вузлів на шляху.
Наприклад, процес, який намагається з'ясувати, що це його поточний каталог /a/b/c, відкриє ..каталог (відносний шлях, такий ..є запис у поточному каталозі) та шукатиме файл типу каталогів з тим самим номером вводу ., що і з'ясувати, що cзбігається, потім відкривається ../..і так далі, поки не знайде /. Там немає двозначності.
Ось що роблять або, принаймні, раніше виконують функції getwd()або getcwd()C.
У деяких системах, таких як сучасний Linux, є системний виклик, щоб повернути канонічний шлях до поточного каталогу, який здійснює пошук у просторі ядра (і дозволяє знайти поточний каталог, навіть якщо ви не маєте доступу для читання до всіх його компонентів) і ось що getcwd()дзвонить туди. У сучасному Linux ви також можете знайти шлях до поточного каталогу через readlink () на /proc/self/cwd.
Саме це робить більшість мов та ранніх оболонок, повертаючи шлях до поточного каталогу.
У вашому випадку, ви можете зателефонувати , cd aяк може раз , як ви хочете, тому що це символьне посилання ., поточний каталог не змінюється , так все getcwd(), pwd -P, python -c 'import os; print os.getcwd()', perl -MPOSIX -le 'print getcwd'повернеться ваш ${HOME}.
Тепер символьні посилання ускладнювали все це.
symlinksдозволити стрибки в дереві каталогів. В /a/b/c, якщо /aабо /a/bабо /a/b/cє символічним посиланням, то канонічний шлях /a/b/cбуде що - то зовсім інше. Зокрема, ..запис /a/b/cне обов'язково /a/b.
У оболонці Борна, якщо ви робите:
cd /a/b/c
cd ..
Або навіть:
cd /a/b/c/..
Немає гарантій, що ти опинишся /a/b.
Так як:
vi /a/b/c/../d
не обов'язково те саме, що:
vi /a/b/d
kshввели концепцію логічного поточного робочого каталогу, щоб якось обійти це. Люди звикли до цього, і POSIX в кінці кінців уточнив таку поведінку, яка означає, що більшість снарядів і сьогодні це роблять:
Для команд cdі pwdвбудованих команд ( і лише для них (хоча також для popd/ pushdна оболонках, які їх мають)) оболонка підтримує власне уявлення про поточний робочий каталог. Він зберігається в $PWDспеціальній змінній.
Коли ви робите:
cd c/d
навіть якщо cі c/dсимволічні посилання, в той час як $PWDcontaines /a/b, він додає c/dдо кінця так $PWDстає /a/b/c/d. А коли ви робите:
cd ../e
Замість того, щоб робити chdir("../e"), це робить chdir("/a/b/c/e").
І pwdкоманда лише повертає вміст $PWDзмінної.
Це корисно в інтерактивних оболонках, оскільки pwdвиводить шлях до поточного каталогу, який дає інформацію про те, як ви потрапили туди, і поки ви використовуєте лише ..аргументи до, cdа не інших команд, менша ймовірність вас здивує, оскільки cd a; cd ..або cd a/..взагалі поверне вас назад туди, де ти був.
Тепер, $PWDне змінюється, якщо ви не зробите це cd. До наступного виклику cdабо pwd, можливо, багато чого може статися, будь-який із компонентів системи $PWDможе бути перейменований. Поточний каталог ніколи не змінюється (це завжди той самий inode, хоча його можна видалити), але його шлях у дереві каталогів може повністю змінитися. getcwd()обчислює поточний каталог кожен раз, коли він викликається, йдучи по дереву каталогів, тому його інформація завжди точна, але для логічного каталогу, реалізованого оболонками POSIX, інформація в $PWDобробці може стати застарілою. Тож, бігаючи cdабо pwd, деякі снаряди можуть захотіти захиститись від цього.
У цьому конкретному випадку ви бачите різну поведінку з різними оболонками.
Деякі люблять ksh93ігнорувати проблему повністю, тому повертають невірну інформацію навіть після дзвінка cd(і ви б не бачили поведінки, яку ви бачите bashтам).
Деякі на кшталт bashабо zshперевіряють, що $PWDце все ще шлях до поточного каталогу cd, але не на pwd.
pdksh перевіряє і те, pwdі cd(але після pwd, не оновлює $PWD)
ash(принаймні той, який знайдено на Debian) не перевіряє, і коли ви це зробите cd a, він насправді робить cd "$PWD/a", тому, якщо поточний каталог змінився і $PWDбільше не вказує на поточний каталог, він фактично не зміниться на aкаталог у поточному каталозі , але один в $PWD(і повернути помилку, якщо її немає).
Якщо ви хочете пограти з ним, ви можете зробити:
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)
в різних оболонках.
У вашому випадку, оскільки ви використовуєте bashпісля cd a, bashперевіряє, що $PWDвсе ще вказує на поточний каталог. Для цього він закликає stat()значення $PWDперевірити його номер inode та порівняти його зі значенням ..
Але коли пошук $PWDшляху передбачає вирішення занадто багато символьних посилань, це stat()повертається з помилкою, тому оболонка не може перевірити, чи $PWDвсе ще відповідає поточному каталогу, тому він обчислює його ще раз getcwd()і оновлює $PWDвідповідно.
Тепер, щоб уточнити відповідь Патріса, перевірка кількості посилань, що виникають під час пошуку шляху, полягає у захисті від циклів символьних посилань. Найпростішу петлю можна зробити за допомогою
rm -f a b
ln -s a b
ln -s b a
Без цього безпечного охоронного cd a/xпристрою система повинна буде знаходити, де aпосилається, знаходить це bі є символьним посиланням, на яке посилається a, і це буде тривати нескінченно. Найпростіший спосіб уберегтися від цього - відмовитися після вирішення більше ніж довільну кількість символьних посилань.
Тепер повернемося до логічного поточного робочого каталогу і чому це не така гарна функція. Важливо усвідомити, що це лише для cdоболонки, а не для інших команд.
Наприклад:
cd -- "$dir" && vi -- "$file"
не завжди те саме, що:
vi -- "$dir/$file"
Ось чому іноді ви виявите, що люди рекомендують завжди використовувати cd -Pв скриптах, щоб уникнути плутанини (ви не хочете, щоб ваше програмне забезпечення обробляло аргументи, що ../xвідрізняються від інших команд лише тому, що воно написано оболонкою замість іншої мови).
-PОпція для відключення логічного каталогу обробки , так на cd -P -- "$var"самому справі дзвонити chdir()за змістом $var(крім випадків , коли $varце , -але це інша історія). І після того cd -P, $PWDбуде містити канонічний шлях.