Патрис определил источник проблемы в своем ответе , но если вы хотите узнать, как узнать, почему вы это получили, вот длинная история.
Текущий рабочий каталог процесса - это не то, что вы считаете слишком сложным. Это атрибут процесса, который является дескриптором файла каталога типа, откуда начинаются относительные пути (в системных вызовах, выполняемых процессом). При разрешении относительного пути ядру не нужно знать полный (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
.
В оболочке Bourne, если вы делаете:
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
on оболочек, у которых они есть)) оболочка поддерживает собственное представление о текущем рабочем каталоге. Он хранится в $PWD
специальной переменной.
Когда вы делаете:
cd c/d
даже если c
или c/d
являются символическими ссылками, в то время как $PWD
содержит /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
может быть переименован. Текущий каталог никогда не меняется (это всегда один и тот же индекс, хотя его можно удалить), но его путь в дереве каталогов может полностью измениться. 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
, после a cd a
, bash
проверяет, что $PWD
все еще указывает на текущий каталог. Для этого он вызывает stat()
значение, $PWD
чтобы проверить его номер инода и сравнить его с номером .
.
Но когда поиск $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
будет содержать канонический путь.