Patrice mengidentifikasi sumber masalah dalam jawabannya , tetapi jika Anda ingin tahu bagaimana mendapatkan dari sana ke mengapa Anda mendapatkannya, inilah cerita panjangnya.
Direktori proses yang sedang berjalan bukanlah hal yang Anda anggap terlalu rumit. Ini adalah atribut dari proses yang merupakan pegangan untuk file direktori jenis di mana jalur relatif (dalam panggilan sistem yang dibuat oleh proses) mulai dari. Ketika menyelesaikan path relatif, kernel tidak perlu mengetahui path lengkap (a) ke direktori saat ini, hanya membaca entri direktori dalam file direktori untuk menemukan komponen pertama dari path relatif (dan ..
seperti yang lainnya file dalam hal itu) dan berlanjut dari sana.
Sekarang, sebagai pengguna, Anda terkadang ingin tahu di mana direktori itu berada di pohon direktori. Dengan sebagian besar Unices, pohon direktori adalah pohon, tanpa loop. Artinya, hanya ada satu jalur dari root tree ( /
) ke file yang diberikan. Jalur itu umumnya disebut jalur kanonik.
Untuk mendapatkan path dari direktori kerja saat ini, apa yang harus dilakukan oleh suatu proses adalah berjalan ( turun jika Anda ingin melihat pohon dengan akarnya di bawah) pohon kembali ke root, mencari nama-nama node dalam perjalanan.
Sebagai contoh, suatu proses mencoba untuk mengetahui bahwa direktori saat ini adalah /a/b/c
, akan membuka ..
direktori (path relatif, begitu ..
juga entri dalam direktori saat ini) dan mencari file direktori tipe dengan nomor inode yang sama seperti .
, cari tahu bahwa c
cocok, lalu buka ../..
dan seterusnya sampai ditemukan /
. Tidak ada ambiguitas di sana.
Itulah yang getwd()
atau getcwd()
fungsi C melakukan atau setidaknya digunakan untuk melakukan.
Pada beberapa sistem seperti Linux modern, ada panggilan sistem untuk mengembalikan jalur kanonik ke direktori saat ini yang melakukan pencarian dalam ruang kernel (dan memungkinkan Anda untuk menemukan direktori Anda saat ini bahkan jika Anda tidak memiliki akses baca ke semua komponennya) , dan itulah yang getcwd()
disebut di sana. Di Linux modern, Anda juga dapat menemukan jalur ke direktori saat ini melalui readlink () pada /proc/self/cwd
.
Itulah yang dilakukan sebagian besar bahasa dan shell saat mengembalikan jalur ke direktori saat ini.
Dalam kasus Anda, Anda dapat memanggil cd a
sebagai mungkin kali seperti yang Anda inginkan, karena itu 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 kembali Anda ${HOME}
.
Sekarang, symlink menjadi rumit semua itu.
symlinks
memungkinkan lompatan di pohon direktori. Dalam /a/b/c
, jika /a
atau /a/b
atau /a/b/c
merupakan symlink, maka jalur kanonik /a/b/c
akan menjadi sesuatu yang sama sekali berbeda. Secara khusus, ..
entri /a/b/c
belum tentu /a/b
.
Dalam 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 saat ini logis untuk entah bagaimana mengatasi itu. Orang-orang terbiasa dengan hal itu dan POSIX akhirnya menentukan perilaku yang berarti sebagian besar shell saat ini melakukannya juga:
Untuk perintah cd
dan pwd
builtin ( dan hanya untuk mereka (meskipun juga untuk popd
/ pushd
pada shell yang memilikinya)), shell mempertahankan idenya sendiri dari direktori kerja saat ini. Ini disimpan dalam $PWD
variabel khusus.
Saat kamu melakukan:
cd c/d
bahkan jika c
atau c/d
yang symlink, sementara $PWD
containes /a/b
, itu menambahkan c/d
sampai akhir sehingga $PWD
menjadi /a/b/c/d
. Dan ketika Anda melakukannya:
cd ../e
Alih-alih melakukan chdir("../e")
, itu benar 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 ..
argumen untuk cd
dan bukan perintah lain, itu cenderung mengejutkan Anda, karena cd a; cd ..
atau cd a/..
biasanya akan membuat Anda kembali ke tempat Anda berada.
Sekarang, $PWD
tidak dimodifikasi kecuali Anda melakukan cd
. Sampai saat Anda menelepon lagi cd
atau pwd
, banyak hal bisa terjadi, komponen apa pun dari itu $PWD
bisa diganti namanya. Direktori saat ini tidak pernah berubah (selalu inode yang sama, meskipun bisa dihapus), tetapi jalurnya di pohon direktori bisa berubah sepenuhnya. getcwd()
menghitung direktori saat ini setiap kali dipanggil dengan berjalan di pohon direktori sehingga informasinya selalu akurat, tetapi untuk direktori logis yang diterapkan oleh shell POSIX, informasi di $PWD
mungkin menjadi basi. Jadi saat menjalankan cd
atau pwd
, beberapa kerang mungkin ingin berjaga-jaga terhadap itu.
Dalam contoh khusus itu, Anda melihat perilaku yang berbeda dengan kulit yang berbeda.
Beberapa suka ksh93
mengabaikan masalah sepenuhnya, sehingga akan mengembalikan informasi yang salah bahkan setelah Anda menelepon cd
(dan Anda tidak akan melihat perilaku yang Anda lihat di bash
sana).
Beberapa suka bash
atau zsh
tidak memeriksa bahwa $PWD
masih jalan ke direktori saat ini di atas cd
, tetapi tidak di atas pwd
.
pdksh memeriksa keduanya pwd
dan cd
(tetapi setelah pwd
, tidak memperbarui $PWD
)
ash
(setidaknya yang ditemukan di Debian) tidak memeriksa, dan ketika Anda melakukannya cd a
, itu benar-benar terjadi cd "$PWD/a"
, jadi jika direktori saat ini telah berubah dan $PWD
tidak lagi menunjuk ke direktori saat ini, itu sebenarnya tidak akan berubah ke a
direktori di direktori saat ini , tetapi yang ada di $PWD
(dan mengembalikan 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 kerang.
Dalam kasus Anda, karena Anda menggunakan bash
, setelah cd a
, bash
periksa yang $PWD
masih menunjuk ke direktori saat ini. Untuk melakukan itu, dibutuhkan stat()
nilai $PWD
untuk memeriksa nomor inode dan membandingkannya dengan .
.
Tetapi ketika mencari $PWD
jalan melibatkan penyelesaian terlalu banyak symlink, yang stat()
kembali dengan kesalahan, sehingga shell tidak dapat memeriksa apakah $PWD
masih sesuai dengan direktori saat ini, jadi ia menghitungnya lagi dengan getcwd()
dan memperbarui $PWD
sesuai.
Sekarang, untuk mengklarifikasi jawaban Patrice, pemeriksaan jumlah symlink yang ditemui saat mencari jalan adalah untuk menjaga terhadap loop symlink. Loop paling sederhana dapat dibuat dengan
rm -f a b
ln -s a b
ln -s b a
Tanpa pelindung yang aman itu, pada a cd a/x
, sistem harus menemukan ke mana a
tautan menuju, menemukannya b
dan merupakan symlink ke mana tautannya a
, dan itu akan berlangsung tanpa batas. Cara paling sederhana untuk mencegah hal itu adalah menyerah setelah menyelesaikan lebih dari jumlah symlink yang sewenang-wenang.
Sekarang kembali ke direktori yang berfungsi saat ini logis dan mengapa itu tidak begitu baik fitur. Sangat penting untuk menyadari bahwa itu hanya untuk cd
di shell dan bukan perintah lainnya.
Misalnya:
cd -- "$dir" && vi -- "$file"
tidak selalu sama dengan:
vi -- "$dir/$file"
Itulah sebabnya kadang-kadang Anda akan menemukan bahwa orang merekomendasikan untuk selalu menggunakan cd -P
dalam skrip untuk menghindari kebingungan (Anda tidak ingin perangkat lunak Anda menangani argumen yang ../x
berbeda dari perintah lain hanya karena itu ditulis dalam shell, bukan bahasa lain).
The -P
pilihan adalah dengan menonaktifkan direktori logis penanganan sehingga cd -P -- "$var"
benar-benar tidak memanggil chdir()
pada isi $var
(kecuali bila $var
adalah -
tapi itu cerita lain). Dan setelah a cd -P
, $PWD
akan berisi jalur kanonik.