GNU/Linux >> Linux Esercitazione >  >> Linux

Ricorsività del collegamento simbolico:cosa lo rende "reimpostato"?

Ho scritto un piccolo script bash per vedere cosa succede quando continuo a seguire un collegamento simbolico che punta alla stessa directory. Mi aspettavo che creasse una directory di lavoro molto lunga o si bloccasse. Ma il risultato mi ha sorpreso...

mkdir a
cd a

ln -s ./. a

for i in `seq 1 1000`
do
  cd a
  pwd
done

Parte dell'output è

${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a
${HOME}/a/a
${HOME}/a/a/a
${HOME}/a/a/a/a
${HOME}/a/a/a/a/a
${HOME}/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a

cosa sta succedendo qui?

Risposta accettata:

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 di 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 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 (e .. è come qualsiasi altro file al riguardo) 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 Unice, l'albero delle directory è un albero, senza loop. Cioè, c'è solo un percorso dalla radice dell'albero (/ ) in un determinato file. Quel percorso è generalmente chiamato percorso canonico.

Per ottenere il percorso della directory di lavoro corrente, ciò che un processo deve fare è semplicemente salire (beh giù se ti piace vedere un albero con la sua radice in basso) l'albero torna alla radice, trovando i nomi dei nodi lungo il percorso.

Ad esempio, un processo che cerca di scoprire che la sua directory corrente è /a/b/c , aprirebbe il .. directory (percorso relativo, quindi .. è la voce nella directory corrente) e cerca un file di tipo directory con lo stesso numero di inode di . , scopri che c corrisponde, quindi apre ../.. e così via finché non trova / . Non c'è ambiguità lì.

Questo è ciò che il getwd() o getcwd() Le funzioni C lo fanno o almeno lo facevano.

Su alcuni sistemi come il moderno Linux, 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 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, puoi chiamare cd a quante volte vuoi, perché è un collegamento simbolico a . , la directory corrente non cambia, quindi tutto getcwd() , pwd -P , python -c 'import os; print os.getcwd()' , perl -MPOSIX -le 'print getcwd' restituirebbe il tuo ${HOME} .

Ora, i collegamenti simbolici hanno complicato tutto questo.

symlinks consentire i salti nell'albero delle directory. In /a/b/c , se /a o /a/b o /a/b/c è un collegamento simbolico, quindi il percorso canonico di /a/b/c sarebbe qualcosa di completamente diverso. In particolare, il .. voce in /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'è alcuna garanzia che finirai in /a/b .

Proprio come:

vi /a/b/c/../d

non è necessariamente uguale a:

vi /a/b/d

ksh introdotto un concetto di directory di lavoro corrente logica per aggirare in qualche modo questo. Le persone si sono abituate e POSIX ha finito per specificare quel comportamento, il che significa che anche la maggior parte delle shell al giorno d'oggi lo fa:

Correlati:Linux:capisci l'accesso a Linux?

Per il cd e pwd comandi integrati (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 nel $PWD variabile speciale.

Quando lo fai:

cd c/d

anche se c o c/d sono collegamenti simbolici, mentre $PWD contiene /a/b , aggiunge c/d alla fine quindi $PWD diventa /a/b/c/d . E quando lo fai:

cd ../e

Invece di fare chdir("../e") , fa chdir("/a/b/c/e") .

E il pwd il comando restituisce solo il contenuto del $PWD variabile.

È utile nelle shell interattive perché pwd restituisce un percorso alla directory corrente che fornisce informazioni su come ci sei arrivato e purché utilizzi solo .. negli argomenti di cd e non altri comandi, è meno probabile che ti sorprenda, perché cd a; cd .. o cd a/.. generalmente ti riporterebbe dove eri.

Ora, $PWD non viene modificato a meno che tu non faccia un cd . Alla prossima volta che chiamerai cd o pwd , potrebbero succedere molte cose, qualsiasi componente di $PWD potrebbe essere rinominato. La directory corrente non cambia mai (è sempre lo stesso inode, anche se potrebbe essere cancellato), ma il suo percorso nell'albero delle 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 potrebbe diventare stantio. Quindi, dopo aver eseguito cd o pwd , alcune shell potrebbero voler proteggersi da questo.

In quel caso particolare, vedi comportamenti diversi con shell diverse.

Alcuni come ksh93 ignora completamente il problema, quindi restituirà informazioni errate anche dopo aver chiamato cd (e non vedresti il ​​comportamento che stai vedendo con bash lì).

Ad alcuni piace bash o zsh controlla che $PWD è ancora un percorso alla directory corrente su cd , ma non su pwd .

pdksh controlla entrambi pwd e cd (ma su pwd , non aggiorna $PWD )

ash (almeno quello trovato su Debian) non controlla, e quando lo fai cd a , in realtà esegue cd "$PWD/a" , quindi se la directory corrente è cambiata e $PWD non punta più alla directory corrente, in realtà non cambierà in a directory nella directory corrente, ma quella 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 vari gusci.

Nel tuo caso, dato che stai usando bash , dopo un cd a , bash controlla che $PWD punta ancora alla directory corrente. Per farlo, chiama stat() sul valore di $PWD per controllare il suo numero di inode e confrontarlo con quello di . .

Ma quando si cerca il $PWD path implica la risoluzione di troppi collegamenti simbolici, che stat() restituisce con un errore, quindi la shell non può verificare se $PWD corrisponde ancora alla directory corrente, quindi la calcola nuovamente con getcwd() e aggiorna $PWD di conseguenza.

Ora, per chiarire la risposta di Patrice, il controllo del numero di collegamenti simbolici incontrati durante la ricerca di un percorso serve a proteggersi dai loop di collegamenti simbolici. Il ciclo più semplice può essere realizzato con

rm -f a b
ln -s a b
ln -s b a

Senza quella protezione, su un cd a/x , il sistema dovrebbe trovare dove a link a, trova che è b ed è un collegamento simbolico che rimanda a a , e questo andrebbe avanti all'infinito. Il modo più semplice per evitarlo è rinunciare dopo aver risolto più di un numero arbitrario di collegamenti simbolici.

Correlati:Steam:le mod vengono applicate su più computer quando collego l'account Nexus all'account Steam?

Ora torniamo alla directory di lavoro corrente logica e perché non è una caratteristica così buona. È importante rendersi conto che è solo per cd nella shell e non in altri comandi.

Ad esempio:

cd -- "$dir" &&  vi -- "$file"

non è sempre uguale a:

vi -- "$dir/$file"

Ecco perché a volte scoprirai che le persone consigliano di usare sempre cd -P negli script per evitare confusione (non vuoi che il tuo software gestisca un argomento di ../x diversamente dagli altri comandi solo perché è scritto in shell invece che in un'altra lingua).

Il -P l'opzione è disabilitare la directory logica gestendo così cd -P -- "$var" in realtà chiama chdir() sul contenuto di $var (almeno finché $CDPATH non è impostato, e tranne quando $var è - (o possibilmente -2 , +3 ... in alcuni gusci) ma questa è un'altra storia). E dopo un cd -P , $PWD conterrà un percorso canonico.


Linux
  1. Cosa rende Linux il sistema operativo sostenibile

  2. Cosa rende speciale la comunità Linux?

  3. Creazione di una nuova directory in C

  4. Node.js:verifica se il file è un collegamento simbolico durante l'iterazione sulla directory con 'fs'

  5. Perché il mio collegamento simbolico crea un file e non una cartella?

Che cosa sono i collegamenti simbolici in Linux? Come creare collegamenti simbolici?

Percorso assoluto vs relativo in Linux:qual è la differenza?

Linux:aggiungi una directory a PATH

Creazione di un programma in bin

Cosa succede se elimino lost+found

Cosa significa NT_STATUS_BAD_NETWORK_NAME in Samba?