Patrice ha identificato l'origine del problema nella sua risposta , ma se vuoi sapere come arrivare da lì al perché lo ottieni, ecco la lunga storia.
L'attuale directory di lavoro di un processo non è nulla che potresti pensare troppo complicato. È un attributo del processo che è un handle per un file di tipo directory da cui iniziano i percorsi relativi (nelle chiamate di sistema effettuate dal processo). Quando si risolve un percorso relativo, il kernel non ha bisogno di conoscere il (a) percorso completo di quella directory corrente, legge semplicemente le voci di directory in quel file di directory per trovare il primo componente del percorso relativo (ed ..
è come qualsiasi altro file in tal senso) e continua da lì.
Ora, come utente, a volte ti piace sapere dove si trova quella directory nell'albero delle directory. Con la maggior parte degli Unices, l'albero delle directory è un albero, senza loop. Cioè, c'è un solo percorso dalla radice dell'albero ( /
) a un dato file. Quel percorso è generalmente chiamato il percorso canonico.
Per ottenere il percorso della directory di lavoro corrente, ciò che un processo deve fare è semplicemente camminare su (ben giù se ti piace vedere un albero con la sua radice in basso) l'albero di nuovo alla radice, trovando i nomi dei nodi sulla strada.
Ad esempio, un processo che prova a scoprire che è la sua directory corrente /a/b/c
, aprirebbe la ..
directory (percorso relativo, quindi ..
è la voce nella directory corrente) e cercherebbe un file di tipo directory con lo stesso numero di inode di .
, scoprire che c
corrisponde, quindi si apre ../..
e così via fino a quando non trova /
. Non c'è ambiguità lì.
Questo è quello che fanno le funzioni getwd()
o getcwd()
C o almeno lo fanno.
Su alcuni sistemi come Linux moderno, c'è una chiamata di sistema per restituire il percorso canonico alla directory corrente che fa quella ricerca nello spazio del kernel (e ti permette di trovare la tua directory corrente anche se non hai accesso in lettura a tutti i suoi componenti) , ed è quello che getcwd()
chiama lì. Su Linux moderno, puoi anche trovare il percorso della directory corrente tramite un readlink () su /proc/self/cwd
.
Questo è ciò che fanno la maggior parte delle lingue e delle prime shell quando restituiscono il percorso alla directory corrente.
Nel tuo caso, si può chiamare cd a
come può le volte che vuoi, perché è un link simbolico .
, la directory corrente non cambia in modo che tutti getcwd()
, pwd -P
, python -c 'import os; print os.getcwd()'
, perl -MPOSIX -le 'print getcwd'
sarebbe restituire la tua ${HOME}
.
Ora, i collegamenti simbolici hanno complicato tutto ciò.
symlinks
consentire i salti nella struttura di directory. In /a/b/c
, se /a
o /a/b
o /a/b/c
è un collegamento simbolico, allora il percorso canonico di /a/b/c
sarebbe qualcosa di completamente diverso. In particolare, l' ..
ingresso /a/b/c
non è necessariamente /a/b
.
Nella shell Bourne, se lo fai:
cd /a/b/c
cd ..
O anche:
cd /a/b/c/..
Non c'è garanzia in cui finirai /a/b
.
Proprio come:
vi /a/b/c/../d
non è necessariamente lo stesso di:
vi /a/b/d
ksh
introdotto un concetto di una directory di lavoro corrente logica per aggirare in qualche modo quello. Le persone si sono abituate e POSIX ha finito per specificare quel comportamento, il che significa che anche la maggior parte delle shell lo fanno anche oggi:
Per i comandi cd
e pwd
builtin ( e solo per loro (anche se anche per popd
/ pushd
su shell che li hanno)), la shell mantiene la propria idea della directory di lavoro corrente. È memorizzato nella $PWD
variabile speciale.
Quando lo fai:
cd c/d
anche se c
o c/d
sono collegamenti simbolici, mentre $PWD
contiene /a/b
, si aggiunge c/d
alla fine così $PWD
diventa /a/b/c/d
. E quando lo fai:
cd ../e
Invece di farlo chdir("../e")
, lo fa chdir("/a/b/c/e")
.
E il pwd
comando restituisce solo il contenuto della $PWD
variabile.
Questo è utile nelle shell interattive perché pwd
genera un percorso alla directory corrente che fornisce informazioni su come ci sei arrivato e fintanto che usi solo ..
argomenti cd
e non altri comandi, è meno probabile che ti sorprenda, perché cd a; cd ..
o cd a/..
ti riprenderà in genere dove eri.
Ora, $PWD
non viene modificato a meno che non si esegua un cd
. Fino alla prossima chiamata cd
o pwd
, potrebbero accadere molte cose, qualsiasi componente di $PWD
potrebbe essere rinominato. La directory corrente non cambia mai (è sempre lo stesso inode, anche se potrebbe essere eliminata), ma il suo percorso nella struttura della directory potrebbe cambiare completamente. getcwd()
calcola la directory corrente ogni volta che viene chiamata camminando lungo l'albero delle directory in modo che le sue informazioni siano sempre accurate, ma per la directory logica implementata dalle shell POSIX, le informazioni in $PWD
potrebbero diventare obsolete. Quindi, correndo cd
o pwd
, alcune conchiglie potrebbero voler proteggerlo.
In quel particolare caso, vedi comportamenti diversi con diverse shell.
Ad alcuni piace ksh93
ignorare completamente il problema, quindi restituiranno informazioni errate anche dopo aver chiamato cd
(e non vedresti il comportamento che stai vedendo bash
lì).
Ad alcuni piace bash
o zsh
verifica che $PWD
sia ancora un percorso della directory corrente su cd
, ma non su pwd
.
pdksh verifica entrambi pwd
e cd
(ma su pwd
, non aggiorna $PWD
)
ash
(almeno quello trovato su Debian) non controlla, e quando lo fai cd a
, lo fa effettivamente cd "$PWD/a"
, quindi se la directory corrente è cambiata e $PWD
non punta più alla directory corrente, in realtà non cambierà nella a
directory nella directory corrente , ma quello in $PWD
(e restituisce un errore se non esiste).
Se vuoi giocarci, puoi fare:
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)
in varie conchiglie.
Nel tuo caso, poiché stai usando bash
, dopo un cd a
, i bash
controlli che $PWD
puntano ancora alla directory corrente. Per fare ciò, chiama stat()
il valore di $PWD
per verificare il suo numero di inode e confrontarlo con quello di .
.
Ma quando la ricerca del $PWD
percorso implica la risoluzione di troppi collegamenti simbolici, ciò stat()
restituisce un errore, quindi la shell non può verificare se $PWD
corrisponde ancora alla directory corrente, quindi la calcola di nuovo con getcwd()
e si aggiorna di $PWD
conseguenza.
Ora, per chiarire la risposta di Patrice, che il controllo del numero di collegamenti simbolici incontrati durante la ricerca di un percorso è di proteggersi dagli anelli dei collegamenti simbolici. Il loop più semplice può essere realizzato con
rm -f a b
ln -s a b
ln -s b a
Senza quella guardia sicura, su un cd a/x
, il sistema dovrebbe trovare dove si a
collegano, trova b
ed è un collegamento simbolico a cui si collega a
e che andrebbe avanti indefinitamente. Il modo più semplice per proteggersi è quello di rinunciare dopo aver risolto più di un numero arbitrario di symlink.
Ora torniamo alla directory di lavoro logica corrente e perché non è una funzionalità così buona. È importante rendersi conto che è solo per cd
la shell e non altri comandi.
Per esempio:
cd -- "$dir" && vi -- "$file"
non è sempre lo stesso di:
vi -- "$dir/$file"
Ecco perché a volte scoprirai che le persone raccomandano di usare sempre cd -P
negli script per evitare confusione (non vuoi che il tuo software gestisca un argomento in modo ../x
diverso dagli altri comandi solo perché è scritto in shell anziché in un'altra lingua).
L' -P
opzione è disabilitare la gestione della directory logica in modo da cd -P -- "$var"
fare appello chdir()
al contenuto di $var
(tranne quando $var
è -
ma questa è un'altra storia). E dopo a cd -P
, $PWD
conterrà un percorso canonico.