GNU/Linux >> Belajar Linux >  >> Linux

Rekursi Tautan Simbolik – Apa yang Membuatnya “direset”?

Saya menulis skrip bash kecil untuk melihat apa yang terjadi ketika saya terus mengikuti tautan simbolis yang menunjuk ke direktori yang sama. Saya mengharapkannya untuk membuat direktori kerja yang sangat panjang, atau macet. Tapi hasilnya mengejutkan saya…

mkdir a
cd a

ln -s ./. a

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

Beberapa outputnya adalah

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

apa yang terjadi di sini?

Jawaban yang Diterima:

Patrice mengidentifikasi sumber masalah dalam jawabannya, tetapi jika Anda ingin tahu bagaimana pergi dari sana dan mengapa Anda mendapatkannya, inilah cerita panjangnya.

Direktori kerja saat ini dari suatu proses bukanlah hal yang menurut Anda terlalu rumit. Ini adalah atribut dari proses yang merupakan pegangan ke file dari direktori tipe di mana jalur relatif (dalam panggilan sistem yang dibuat oleh proses) dimulai. Saat menyelesaikan jalur relatif, kernel tidak perlu mengetahui (a) jalur lengkap ke direktori saat ini, kernel hanya membaca entri direktori dalam file direktori tersebut untuk menemukan komponen pertama dari jalur relatif (dan .. seperti file lain dalam hal itu) dan berlanjut dari sana.

Sekarang, sebagai pengguna, Anda terkadang ingin tahu di mana letak direktori itu di pohon direktori. Dengan sebagian besar Unices, pohon direktori adalah pohon, tanpa loop. Artinya, hanya ada satu jalur dari akar pohon (/ ) ke file apa pun. Jalur itu umumnya disebut jalur kanonik.

Untuk mendapatkan jalur direktori kerja saat ini, proses yang harus dilakukan hanyalah berjalan ke atas (well bawah jika Anda ingin melihat pohon dengan akarnya di bawah) pohon kembali ke akarnya, temukan nama-nama simpul di jalan.

Misalnya, sebuah proses mencoba untuk mengetahui bahwa direktori saat ini adalah /a/b/c , akan membuka .. direktori (jalur relatif, jadi .. adalah entri dalam direktori saat ini) dan cari file dengan tipe direktori dengan nomor inode yang sama dengan . , cari tahu bahwa c cocok, lalu buka ../.. dan seterusnya sampai menemukan / . Tidak ada ambiguitas di sana.

Itulah yang getwd() atau getcwd() Fungsi C melakukan atau setidaknya dulu melakukan.

Pada beberapa sistem seperti Linux modern, ada panggilan sistem untuk mengembalikan jalur kanonik ke direktori saat ini yang melakukan pencarian itu di ruang kernel (dan memungkinkan Anda menemukan direktori saat ini bahkan jika Anda tidak memiliki akses baca ke semua komponennya) , dan itulah yang getcwd() panggilan di sana. Di Linux modern, Anda juga dapat menemukan jalur ke direktori saat ini melalui readlink() di /proc/self/cwd .

Itulah yang dilakukan sebagian besar bahasa dan shell awal saat mengembalikan jalur ke direktori saat ini.

Dalam kasus Anda, Anda dapat memanggil cd a sesering yang Anda inginkan, karena ini adalah symlink ke . , direktori saat ini tidak berubah sehingga semua getcwd() , pwd -P , python -c 'import os; print os.getcwd()' , perl -MPOSIX -le 'print getcwd' akan mengembalikan ${HOME} . Anda .

Sekarang, symlink memperumit semua itu.

symlinks izinkan lompatan di pohon direktori. Dalam /a/b/c , jika /a atau /a/b atau /a/b/c adalah symlink, maka jalur kanonik /a/b/c akan menjadi sesuatu yang sama sekali berbeda. Khususnya, .. entri di /a/b/c belum tentu /a/b .

Di shell Bourne, jika Anda melakukannya:

cd /a/b/c
cd ..

Atau bahkan:

cd /a/b/c/..

Tidak ada jaminan Anda akan berakhir di /a/b .

Seperti:

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

belum tentu sama dengan:

vi /a/b/d

ksh memperkenalkan konsep direktori kerja logis saat ini entah bagaimana bekerja di sekitar itu. Orang-orang menjadi terbiasa dan POSIX akhirnya menentukan perilaku itu yang berarti sebagian besar shell saat ini juga melakukannya:

Terkait:Linux – Memahami login di Linux?

Untuk cd dan pwd perintah bawaan (dan hanya untuk mereka (meskipun juga untuk popd /pushd pada shell yang memilikinya)), shell mempertahankan idenya sendiri tentang direktori kerja saat ini. Itu disimpan di $PWD variabel khusus.

Ketika Anda melakukannya:

cd c/d

meskipun c atau c/d adalah symlink, sedangkan $PWD berisi /a/b , itu menambahkan c/d sampai akhir jadi $PWD menjadi /a/b/c/d . Dan ketika Anda melakukannya:

cd ../e

Alih-alih melakukan chdir("../e") , itu chdir("/a/b/c/e") .

Dan pwd perintah hanya mengembalikan konten $PWD variabel.

Itu berguna dalam shell interaktif karena pwd menampilkan jalur ke direktori saat ini yang memberikan informasi tentang bagaimana Anda sampai di sana dan selama Anda hanya menggunakan .. dalam argumen ke cd dan bukan perintah lain, kemungkinan kecil Anda akan terkejut, karena cd a; cd .. atau cd a/.. biasanya akan membawa Anda kembali ke tempat Anda sebelumnya.

Sekarang, $PWD tidak dimodifikasi kecuali Anda melakukan cd . Sampai waktu berikutnya Anda menelepon cd atau pwd , banyak hal bisa terjadi, salah satu komponen $PWD bisa diganti namanya. Direktori saat ini tidak pernah berubah (selalu inode yang sama, meskipun dapat dihapus), tetapi jalurnya di pohon direktori dapat berubah sepenuhnya. getcwd() menghitung direktori saat ini setiap kali dipanggil dengan menelusuri pohon direktori sehingga informasinya selalu akurat, tetapi untuk direktori logis yang diimplementasikan oleh shell POSIX, informasi di $PWD mungkin menjadi basi. Jadi setelah menjalankan cd atau pwd , beberapa shell mungkin ingin mencegahnya.

Dalam contoh khusus itu, Anda melihat perilaku yang berbeda dengan cangkang yang berbeda.

Beberapa seperti ksh93 abaikan masalah sepenuhnya, sehingga akan mengembalikan informasi yang salah bahkan setelah Anda memanggil cd (dan Anda tidak akan melihat perilaku yang Anda lihat dengan bash sana).

Beberapa menyukai bash atau zsh periksa apakah $PWD masih merupakan jalur ke direktori saat ini pada cd , tetapi tidak pada pwd .

pdksh memeriksa keduanya pwd dan cd (tetapi setelah pwd , tidak memperbarui $PWD )

ash (setidaknya yang ditemukan di Debian) tidak memeriksa, dan ketika Anda melakukan cd a , sebenarnya cd "$PWD/a" , jadi jika direktori saat ini telah berubah dan $PWD tidak lagi menunjuk ke direktori saat ini, itu sebenarnya tidak akan berubah menjadi a direktori di direktori saat ini, tetapi yang ada di $PWD (dan kembalikan kesalahan jika tidak ada).

Jika Anda ingin bermain dengannya, Anda dapat melakukan:

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)

dalam berbagai cangkang.

Dalam kasus Anda, karena Anda menggunakan bash , setelah cd a , bash memeriksa bahwa $PWD masih menunjuk ke direktori saat ini. Untuk melakukan itu, ia memanggil stat() pada nilai $PWD untuk memeriksa nomor inodenya dan membandingkannya dengan . .

Tetapi ketika mencari $PWD path melibatkan penyelesaian terlalu banyak symlink, yang stat() kembali dengan kesalahan, sehingga shell tidak dapat memeriksa apakah $PWD masih sesuai dengan direktori saat ini, sehingga menghitungnya lagi dengan getcwd() dan memperbarui $PWD sesuai.

Sekarang, untuk memperjelas jawaban Patrice, pemeriksaan jumlah symlink yang ditemui saat mencari jalur adalah untuk menjaga dari loop symlink. Loop paling sederhana dapat dibuat dengan

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

Tanpa pengaman itu, di atas cd a/x , sistem harus menemukan di mana a link ke, menemukan itu b dan merupakan symlink yang tertaut ke a , dan itu akan berlangsung tanpa batas. Cara paling sederhana untuk menghindarinya adalah dengan menyerah setelah menyelesaikan lebih dari jumlah symlink yang berubah-ubah.

Terkait:Steam – Apakah mod diterapkan di beberapa komputer saat saya menautkan akun Nexus ke akun Steam?

Sekarang kembali ke direktori kerja logis saat ini dan mengapa itu bukan fitur yang bagus. Penting untuk disadari bahwa ini hanya untuk cd di shell dan bukan perintah lain.

Misalnya:

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

tidak selalu sama dengan:

vi -- "$dir/$file"

Itulah mengapa Anda terkadang menemukan bahwa orang menyarankan untuk selalu menggunakan cd -P dalam skrip untuk menghindari kebingungan (Anda tidak ingin perangkat lunak Anda menangani argumen ../x berbeda dari perintah lain hanya karena ditulis dalam shell, bukan bahasa lain).

-P pilihannya adalah menonaktifkan direktori logis menangani jadi cd -P -- "$var" sebenarnya memanggil chdir() pada konten $var (setidaknya selama $CDPATH itu tidak disetel, dan kecuali ketika $var adalah - (atau mungkin -2 , +3 ... di beberapa cangkang) tapi itu cerita lain). Dan setelah cd -P , $PWD akan berisi jalur kanonik.


Linux
  1. Apa yang membuat Linux menjadi OS yang berkelanjutan

  2. Apa yang membuat komunitas Linux istimewa?

  3. Cd Ke Direktori Nama-Tidak Dikenal Di Jalur yang Diketahui?

  1. Buat tautan simbolik direktori di Ubuntu

  2. Cara membuat tautan ke direktori

  3. Mengapa tautan simbolis saya membuat file dan bukan folder?

  1. Bagaimana cara menghapus tautan simbolik ke direktori?

  2. Apa yang terjadi jika mv terganggu?

  3. Penggunaan / saat menggunakan cd