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/cabriria 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 ccorresponde, 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 acomo 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.
symlinkspermitir saltos na árvore de diretórios. Em /a/b/c, se /aou /a/bou /a/b/cé um link simbólico, o caminho canônico de /a/b/cseria algo completamente diferente. Em particular, a ..entrada /a/b/cnã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
kshintroduziu 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 cde pwdbuiltin comandos ( e só para eles (embora também para popd/ pushdde conchas que eles) têm), o shell mantém a sua própria ideia do diretório de trabalho atual. É armazenado na $PWDvariável especial.
Quando você faz:
cd c/d
mesmo que cou c/dsejam links simbólicos, embora $PWDcontenha /a/b, ele se anexa c/dao final que $PWDse 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 pwdcomando retorna apenas o conteúdo da $PWDvariável.
Isso é útil em conchas interativos porque pwdgera um caminho para o diretório atual que dá informações sobre como você chegou lá e, enquanto você usar somente ..em argumentos para cde 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, $PWDnão é modificado, a menos que você faça um cd. Até a próxima vez que você ligar cdou pwd, muitas coisas acontecerem, qualquer um dos componentes $PWDpoderá 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 $PWDpodem ficar obsoletas. Assim, ao executar cdou pwd, algumas conchas pode querer proteger contra isso.
Nesse caso em particular, você vê comportamentos diferentes com conchas diferentes.
Algumas pessoas ksh93ignoram completamente o problema e retornam informações incorretas mesmo depois de ligar cd(e você não vê o comportamento que está vendo bashlá).
Alguns gostam bashou zshverificam que $PWDainda é um caminho para o diretório atual cd, mas não para o diretório atual pwd.
O pdksh verifica ambos pwde cd(mas pwdnã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 $PWDnão aponta mais para o diretório atual, ele não será alterado para o adiretó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, bashverifica que $PWDainda aponta para o diretório atual. Para fazer isso, ele chama stat()o valor de $PWDpara verificar seu número de inode e compará-lo com o de ..
Porém, quando a busca do $PWDcaminho 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 $PWDainda corresponde ao diretório atual, portanto o calcula novamente getcwd()e atualiza de $PWDacordo.
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/xo sistema teria que encontrar para onde os alinks estão, encontra-os be é 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 cdo 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 -Pscripts para evitar confusão (você não quer que o seu software lide com argumentos de maneira ../xdiferente dos outros comandos, apenas porque está escrito em shell e não em outro idioma).
A -Popçã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, $PWDconterá um caminho canônico.