GNU/Linux >> Znalost Linux >  >> Linux

Rekurze symbolického odkazu – proč je „resetována“?

Napsal jsem malý bash skript, abych viděl, co se stane, když budu sledovat symbolický odkaz, který ukazuje na stejný adresář. Čekal jsem, že buď vytvoří velmi dlouhý pracovní adresář, nebo se zhroutí. Ale výsledek mě překvapil…

mkdir a
cd a

ln -s ./. a

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

Část výstupu je

${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

co se tady děje?

Přijatá odpověď:

Patrice ve své odpovědi identifikoval zdroj problému, ale pokud chcete vědět, jak se odtamtud dostat k tomu, proč tomu tak je, zde je dlouhý příběh.

Aktuální pracovní adresář procesu není nic, co byste považovali za příliš složité. Je to atribut procesu, který je popisovačem souboru typu adresář, odkud začínají relativní cesty (v systémových voláních prováděných procesem). Při řešení relativní cesty jádro nepotřebuje znát (a) úplnou cestu k tomuto aktuálnímu adresáři, pouze čte položky adresáře v tomto adresářovém souboru, aby našlo první komponentu relativní cesty (a .. je v tomto ohledu jako jakýkoli jiný soubor) a pokračuje odtud.

Nyní, jako uživatel, byste někdy rádi věděli, kde tento adresář v adresářovém stromu leží. U většiny Unices je strom adresářů strom bez smyčky. To znamená, že existuje pouze jedna cesta z kořene stromu (/ ) do libovolného souboru. Tato cesta se obecně nazývá kanonická cesta.

Aby proces získal cestu k aktuálnímu pracovnímu adresáři, musí jít nahoru (dobře dolů pokud chcete vidět strom s kořenem dole), strom zpět ke kořenu a najděte jména uzlů na cestě.

Například proces, který se snaží zjistit, že jeho aktuální adresář je /a/b/c , by otevřel .. adresář (relativní cesta, takže .. je záznam v aktuálním adresáři) a vyhledejte soubor typu adresář se stejným číslem inodu jako . , zjistěte, že c odpovídá a poté otevře ../.. a tak dále, dokud nenajde / . Není v tom žádná dvojznačnost.

To je to, co getwd() nebo getcwd() C funkce dělají nebo alespoň dělaly.

Na některých systémech, jako je moderní Linux, existuje systémové volání, které vrátí kanonickou cestu do aktuálního adresáře, což provede toto vyhledávání v prostoru jádra (a umožní vám najít váš aktuální adresář, i když nemáte přístup ke všem jeho komponentám) , a to je to, co getcwd() tam volá. Na moderním Linuxu můžete také najít cestu k aktuálnímu adresáři pomocí readlink() na /proc/self/cwd .

To je to, co dělá většina jazyků a prvních shellů, když vrací cestu do aktuálního adresáře.

Ve vašem případě můžete zavolat cd a kolikrát budete chtít, protože je to symbolický odkaz na . , aktuální adresář se nemění, takže všechny getcwd() , pwd -P , python -c 'import os; print os.getcwd()' , perl -MPOSIX -le 'print getcwd' vrátí váš ${HOME} .

Nyní to vše zkomplikovaly symbolické odkazy.

symlinks povolit skoky v adresářovém stromu. V /a/b/c , pokud /a nebo /a/b nebo /a/b/c je symbolický odkaz, pak kanonická cesta /a/b/c by bylo něco úplně jiného. Konkrétně .. záznam v /a/b/c není nutně /a/b .

V Bourne shell, pokud tak učiníte:

cd /a/b/c
cd ..

Nebo dokonce:

cd /a/b/c/..

Neexistuje žádná záruka, že skončíte v /a/b .

Stejně jako:

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

není nutně totéž jako:

vi /a/b/d

ksh představil koncept logického aktuálního pracovního adresáře nějak to obejít. Lidé si na to zvykli a POSIX nakonec specifikoval toto chování, což znamená, že většina současných shellů to dělá také:

Související:Linux – Rozumíte přihlašování v Linuxu?

Pro cd a pwd vestavěné příkazy (a pouze pro ně (i když také pro popd /pushd na shellech, které je mají)), si shell zachovává svou vlastní představu o aktuálním pracovním adresáři. Je uloženo v $PWD speciální proměnná.

Když to uděláte:

cd c/d

i když c nebo c/d jsou symbolické odkazy, zatímco $PWD obsahuje /a/b , připojí c/d až do konce, takže $PWD se změní na /a/b/c/d . A když to uděláte:

cd ../e

Místo provádění chdir("../e") , dělá to chdir("/a/b/c/e") .

A pwd příkaz vrátí pouze obsah $PWD proměnná.

To je užitečné v interaktivních shellech, protože pwd vypíše cestu k aktuálnímu adresáři, která poskytuje informace o tom, jak jste se tam dostali a pokud používáte pouze .. v argumentech k cd a ne jiné příkazy, je méně pravděpodobné, že vás to překvapí, protože cd a; cd .. nebo cd a/.. obecně by vás vrátil tam, kde jste byli.

Nyní $PWD se nezmění, pokud neuděláte cd . Dokud příště nezavoláte cd nebo pwd , může se stát spousta věcí, kterákoli ze součástí $PWD mohl být přejmenován. Aktuální adresář se nikdy nezmění (vždy je to stejný inode, i když může být smazán), ale jeho cesta ve stromu adresářů se může úplně změnit. getcwd() vypočítá aktuální adresář pokaždé, když je zavolán, procházením stromu adresářů, takže jeho informace jsou vždy přesné, ale pro logický adresář implementovaný shelly POSIX jsou informace v $PWD může zatuchnout. Takže po spuštění cd nebo pwd , některé shelly se proti tomu mohou chtít chránit.

V tomto konkrétním případě uvidíte různé chování s různými shelly.

Někteří jako ksh93 problém zcela ignorovat, takže vrátí nesprávné informace i po zavolání cd (a neuvidíte chování, které vidíte u bash tam).

Někteří mají rádi bash nebo zsh zkontrolujte, zda $PWD je stále cesta k aktuálnímu adresáři na cd , ale ne po pwd .

pdksh kontroluje obě pwd a cd (ale po pwd , neaktualizuje $PWD )

ash (alespoň ten, který se nachází v Debianu) nekontroluje, a když uděláte cd a , ve skutečnosti to dělá cd "$PWD/a" , takže pokud se aktuální adresář změnil a $PWD již neukazuje na aktuální adresář, ve skutečnosti se nezmění na a adresář v aktuálním adresáři, ale ten v $PWD (a vrátí chybu, pokud neexistuje).

Pokud si s tím chcete hrát, můžete:

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)

v různých skořápkách.

Ve vašem případě, protože používáte bash , za cd a , bash zkontroluje, že $PWD stále ukazuje na aktuální adresář. K tomu volá stat() na hodnotu $PWD zkontrolovat jeho číslo inodu a porovnat ho s číslem . .

Ale při vyhledávání $PWD cesta zahrnuje vyřešení příliš mnoha symbolických odkazů, které stat() vrátí s chybou, takže shell nemůže zkontrolovat, zda $PWD stále odpovídá aktuálnímu adresáři, takže jej vypočítá znovu pomocí getcwd() a aktualizace $PWD podle toho.

Abychom nyní objasnili Patriceovu odpověď, kontrola počtu symbolických odkazů, na které narazíte při hledání cesty, slouží k ochraně před smyčkami symbolických odkazů. Nejjednodušší smyčku lze vytvořit pomocí

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

Bez této ochrany, na cd a/x , systém by musel najít místo a odkazuje na, zjistí, že je to b a je to symbolický odkaz, který odkazuje na a a tak by to šlo donekonečna. Nejjednodušší způsob, jak se tomu bránit, je vzdát se po vyřešení více než libovolného počtu symbolických odkazů.

Související:Steam – Použijí se mody na více počítačích, když propojím účet Nexus s účtem Steam?

Nyní zpět do logického aktuálního pracovního adresáře a proč to není tak dobrá funkce. Je důležité si uvědomit, že je to pouze pro cd v shellu a ne v jiných příkazech.

Například:

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

není vždy stejné jako:

vi -- "$dir/$file"

Proto někdy zjistíte, že lidé doporučují vždy používat cd -P ve skriptech, abyste předešli zmatkům (nechcete, aby váš software zpracovával argument ../x odlišně od ostatních příkazů jen proto, že je napsán v shellu místo v jiném jazyce).

-P možností je zakázat logický adresář zpracování tak cd -P -- "$var" ve skutečnosti volá chdir() na obsah $var (alespoň tak dlouho jako $CDPATH není nastaveno a kromě případů $var je - (nebo možná -2 , +3 … v některých skořápkách), ale to je jiný příběh). A po cd -P , $PWD bude obsahovat kanonickou cestu.


Linux
  1. Node.js:Zkontrolujte, zda je soubor symbolickým odkazem při iteraci přes adresář s 'fs'

  2. Proč můj symbolický odkaz vytváří soubor a ne složku?

  3. Jak odstranit symbolický odkaz na adresář?

  1. Použití / při použití cd

  2. Co je výstupem pwd?

  3. Co znamená NT_STATUS_BAD_NETWORK_NAME v Sambě?

  1. Čím je linuxová komunita výjimečná?

  2. Vytvoření nového adresáře v C

  3. Vytvořte symbolický odkaz na adresář v Ubuntu