Patrice identificou a fonte do problema em sua resposta , mas se você quiser saber como chegar a esse ponto, aqui está a longa história.
O diretório de trabalho atual de um processo não é nada que você pensaria muito complicado. É um atributo do processo, que é um identificador para um arquivo do tipo diretório onde os caminhos relativos (nas chamadas do sistema feitas pelo processo) começam. Ao resolver um caminho relativo, o kernel não precisa conhecer o (a) caminho completo para o diretório atual, apenas lê as entradas do diretório nesse arquivo de diretório para encontrar o primeiro componente do caminho relativo (e ..
é como qualquer outro arquivo a esse respeito) e continua a partir daí.
Agora, como usuário, às vezes você gosta de saber onde esse diretório se encontra na árvore de diretórios. Na maioria dos Unices, a árvore de diretórios é uma árvore, sem loop. Ou seja, existe apenas um caminho da raiz da árvore ( /
) para qualquer arquivo. Esse caminho é geralmente chamado de caminho canônico.
Para obter o caminho do diretório de trabalho atual, o que um processo precisa fazer é apenas subir (bem abaixo, se você quiser ver uma árvore com sua raiz na parte inferior) da árvore de volta à raiz, localizando os nomes dos nós a caminho.
Por exemplo, um processo que tenta descobrir qual é seu diretório atual /a/b/c
abriria o ..
diretório (caminho relativo, assim ..
como a entrada no diretório atual) e procuraria por um arquivo do tipo diretório com o mesmo número de inode que .
descobriria c
corresponde, abre ../..
e assim por diante até encontrar /
. Não há ambiguidade lá.
É o que as funções getwd()
ou getcwd()
C fazem ou pelo menos costumavam fazer.
Em alguns sistemas como o Linux moderno, há uma chamada do sistema para retornar o caminho canônico ao diretório atual, que faz essa pesquisa no espaço do kernel (e permite encontrar o diretório atual, mesmo que você não tenha acesso de leitura a todos os seus componentes) , e é isso que getcwd()
chama lá. No Linux moderno, você também pode encontrar o caminho para o diretório atual através de um readlink () em /proc/self/cwd
.
É o que a maioria dos idiomas e shells iniciais fazem ao retornar o caminho para o diretório atual.
No seu caso, você pode chamar cd a
como pode vezes quiser, porque é um link simbólico para .
o diretório atual não muda assim todos getcwd()
, pwd -P
, python -c 'import os; print os.getcwd()'
, perl -MPOSIX -le 'print getcwd'
voltaria seu ${HOME}
.
Agora, os links simbólicos foram complicando tudo isso.
symlinks
permitir saltos na árvore de diretórios. Em /a/b/c
, se /a
ou /a/b
ou /a/b/c
é um link simbólico, o caminho canônico de /a/b/c
seria algo completamente diferente. Em particular, a ..
entrada /a/b/c
não é necessariamente /a/b
.
No shell Bourne, se você fizer:
cd /a/b/c
cd ..
Ou até:
cd /a/b/c/..
Não há garantia de que você acabará /a/b
.
Assim como:
vi /a/b/c/../d
não é necessariamente o mesmo que:
vi /a/b/d
ksh
introduziu o conceito de um diretório de trabalho atual lógico para, de alguma forma, solucionar esse problema. As pessoas se acostumaram e o POSIX acabou especificando esse comportamento, o que significa que a maioria dos shells atualmente também o faz:
Para os cd
e pwd
builtin comandos ( e só para eles (embora também para popd
/ pushd
de conchas que eles) têm), o shell mantém a sua própria ideia do diretório de trabalho atual. É armazenado na $PWD
variável especial.
Quando você faz:
cd c/d
mesmo que c
ou c/d
sejam links simbólicos, embora $PWD
contenha /a/b
, ele se anexa c/d
ao final que $PWD
se torna /a/b/c/d
. E quando você faz:
cd ../e
Em vez de fazer chdir("../e")
, faz chdir("/a/b/c/e")
.
E o pwd
comando retorna apenas o conteúdo da $PWD
variável.
Isso é útil em conchas interativos porque pwd
gera um caminho para o diretório atual que dá informações sobre como você chegou lá e, enquanto você usar somente ..
em argumentos para cd
e não outros comandos, é menos provável que surpreendê-lo, porque cd a; cd ..
ou cd a/..
faria geralmente ter você de volta para onde você estava.
Agora, $PWD
não é modificado, a menos que você faça um cd
. Até a próxima vez que você ligar cd
ou pwd
, muitas coisas acontecerem, qualquer um dos componentes $PWD
poderá ser renomeado. O diretório atual nunca muda (é sempre o mesmo inode, embora possa ser excluído), mas seu caminho na árvore de diretórios pode mudar completamente. getcwd()
calcula o diretório atual cada vez que é chamado, percorrendo a árvore de diretórios para que suas informações sejam sempre precisas, mas para o diretório lógico implementado pelos shells POSIX, as informações $PWD
podem ficar obsoletas. Assim, ao executar cd
ou pwd
, algumas conchas pode querer proteger contra isso.
Nesse caso em particular, você vê comportamentos diferentes com conchas diferentes.
Algumas pessoas ksh93
ignoram completamente o problema e retornam informações incorretas mesmo depois de ligar cd
(e você não vê o comportamento que está vendo bash
lá).
Alguns gostam bash
ou zsh
verificam que $PWD
ainda é um caminho para o diretório atual cd
, mas não para o diretório atual pwd
.
O pdksh verifica ambos pwd
e cd
(mas pwd
não atualiza $PWD
)
ash
(pelo menos o encontrado no Debian) não verifica e, quando o faz cd a
, ele realmente faz cd "$PWD/a"
; portanto, se o diretório atual mudou e $PWD
não aponta mais para o diretório atual, ele não será alterado para o a
diretório no diretório atual , mas o que está dentro $PWD
(e retorne um erro se ele não existir).
Se você quiser brincar com ele, pode:
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)
em várias conchas.
No seu caso, desde que você esteja usando bash
, após a cd a
, bash
verifica que $PWD
ainda aponta para o diretório atual. Para fazer isso, ele chama stat()
o valor de $PWD
para verificar seu número de inode e compará-lo com o de .
.
Porém, quando a busca do $PWD
caminho envolve a solução de muitos links simbólicos, isso stat()
retorna com um erro, de modo que o shell não pode verificar se $PWD
ainda corresponde ao diretório atual, portanto o calcula novamente getcwd()
e atualiza de $PWD
acordo.
Agora, para esclarecer a resposta de Patrice, essa verificação do número de links simbólicos encontrados ao procurar um caminho é para evitar loops de links simbólicos. O loop mais simples pode ser feito com
rm -f a b
ln -s a b
ln -s b a
Sem essa proteção, cd a/x
o sistema teria que encontrar para onde os a
links estão, encontra-os b
e é um link simbólico ao qual os links estão a
, e isso continuaria indefinidamente. A maneira mais simples de evitar isso é desistir depois de resolver mais do que um número arbitrário de links simbólicos.
Agora, de volta ao diretório de trabalho atual lógico e por que esse recurso não é tão bom. É importante perceber que é apenas para cd
o shell e não para outros comandos.
Por exemplo:
cd -- "$dir" && vi -- "$file"
nem sempre é o mesmo que:
vi -- "$dir/$file"
É por isso que às vezes você acha que as pessoas recomendam o uso sempre de cd -P
scripts para evitar confusão (você não quer que o seu software lide com argumentos de maneira ../x
diferente dos outros comandos, apenas porque está escrito em shell e não em outro idioma).
A -P
opção é desativar o manuseio do diretório lógico, para que cd -P -- "$var"
realmente chame chdir()
o conteúdo de $var
(exceto quando $var
é, -
mas isso é outra história). E depois de a cd -P
, $PWD
conterá um caminho canônico.