Patrice ระบุที่มาของปัญหาในคำตอบของเขาแต่ถ้าคุณต้องการทราบวิธีการได้รับจากที่นั่นกับสาเหตุที่คุณได้รับนั่นคือเรื่องราวที่ยาวนาน
ไดเรกทอรีทำงานปัจจุบันของกระบวนการคือสิ่งที่คุณคิดว่าซับซ้อนเกินไป มันเป็นคุณสมบัติของกระบวนการซึ่งเป็นที่จับไปยังไฟล์ของไดเรกทอรีประเภทที่เส้นทางสัมพัทธ์ (ในการเรียกระบบโดยกระบวนการ) เริ่มต้นจาก เมื่อแก้ไขพา ธ สัมพัทธ์เคอร์เนลไม่จำเป็นต้องรู้ว่า (ก) พา ธ เต็มไปยังไดเรกทอรีปัจจุบันนั้นเพียงแค่อ่านรายการไดเรกทอรีในไฟล์ไดเรกทอรีนั้นเพื่อค้นหาองค์ประกอบแรกของเส้นทางสัมพัทธ์ (และ..
เหมือนกับที่อื่น ๆ ยื่นเรื่องนั้น) และดำเนินการต่อจากที่นั่น
ตอนนี้ในฐานะผู้ใช้บางครั้งคุณอยากรู้ว่าไดเรกทอรีนั้นอยู่ที่ใดในโครงสร้างไดเรกทอรี ด้วย Unices ส่วนใหญ่แผนผังไดเรกทอรีจะเป็นแผนผังโดยไม่มีลูป นั่นคือมีเพียงเส้นทางเดียวจากรากของต้นไม้ ( /
) ไปยังไฟล์ที่กำหนด เส้นทางนั้นโดยทั่วไปเรียกว่าเส้นทางแบบบัญญัติ
เพื่อให้ได้เส้นทางของไดเรกทอรีการทำงานปัจจุบันสิ่งที่กระบวนการที่มีการทำคือเพียงแค่เดินขึ้น (ดีลงถ้าคุณต้องการที่จะเห็นต้นไม้ที่มีรากที่ด้านล่าง) ต้นไม้กลับไปที่รากหาชื่อของโหนด ระหว่างทาง
ตัวอย่างเช่นกระบวนการที่พยายามค้นหาว่าไดเรกทอรีปัจจุบันคือ/a/b/c
จะเปิด..
ไดเรกทอรี (เส้นทางสัมพัทธ์ดังนั้นจึง..
เป็นรายการในไดเรกทอรีปัจจุบัน) และค้นหาไฟล์ของไดเรกทอรีประเภทที่มีหมายเลข inode เดียวกัน.
พบว่าc
แมตช์ที่แล้วเปิด../..
ไปเรื่อย ๆ /
จนกว่าจะพบ ไม่มีความกำกวมอยู่ที่นั่น
นั่นคือสิ่งที่ฟังก์ชั่นgetwd()
หรือgetcwd()
C ทำหรืออย่างน้อยก็เคยทำ
ในบางระบบเช่น Linux รุ่นใหม่มีการเรียกระบบเพื่อส่งเส้นทาง canonical ไปยังไดเรกทอรีปัจจุบันซึ่งทำการค้นหาในพื้นที่เคอร์เนล (และอนุญาตให้คุณค้นหาไดเรกทอรีปัจจุบันของคุณแม้ว่าคุณจะไม่ได้อ่านการเข้าถึงส่วนประกอบทั้งหมดของมัน) และนั่นคือสิ่งที่getcwd()
เรียกว่า บนลินุกซ์ที่ทันสมัยนอกจากนี้คุณยังสามารถหาเส้นทางไปยังไดเรกทอรีปัจจุบันผ่าน readlink (ที่) /proc/self/cwd
บน
นั่นคือสิ่งที่ภาษาและเปลือกต้นส่วนใหญ่ทำเมื่อส่งคืนพา ธ ไปยังไดเรกทอรีปัจจุบัน
ในกรณีของคุณคุณสามารถโทรหาcd a
ที่อาจครั้งตามที่คุณต้องการเพราะมันเป็น symlink ไป.
ที่ไดเรกทอรีปัจจุบันไม่เปลี่ยนแปลงดังนั้นทั้งหมดgetcwd()
, pwd -P
, python -c 'import os; print os.getcwd()'
, จะกลับมาของคุณperl -MPOSIX -le 'print getcwd'
${HOME}
ตอนนี้การเชื่อมโยงระบบเชื่อมโยงกันซับซ้อนขึ้น
symlinks
อนุญาตให้ข้ามในไดเรกทอรีต้นไม้ ใน/a/b/c
ถ้า/a
หรือ/a/b
หรือ/a/b/c
เป็น symlink แล้วเส้นทางบัญญัติของ/a/b/c
จะเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง โดยเฉพาะอย่างยิ่ง..
ในรายการไม่จำเป็นต้องเป็น/a/b/c
/a/b
ในเชลล์เป้าหมายถ้าคุณทำ:
cd /a/b/c
cd ..
หรือแม้กระทั่ง:
cd /a/b/c/..
/a/b
มีการรับประกันคุณจะจบลงในไม่ได้
เหมือนกับ:
vi /a/b/c/../d
ไม่จำเป็นต้องเหมือนกับ:
vi /a/b/d
ksh
แนะนำแนวคิดของไดเร็กตอรี่ปัจจุบันที่ใช้ในการทำงานเพื่อแก้ไขสิ่งนั้น. ผู้คนเคยชินกับมันและ POSIX ก็จบลงด้วยการระบุพฤติกรรมซึ่งหมายความว่าเชลล์ส่วนใหญ่ทุกวันนี้ก็ทำได้เช่นกัน:
สำหรับคำสั่งcd
และpwd
builtin ( และสำหรับคำสั่งเหล่านั้นเท่านั้น (แม้ว่าจะใช้กับpopd
/ pushd
บนเชลล์ที่มี)) เชลล์จะเก็บรักษาแนวคิดของตนเองเกี่ยวกับไดเร็กทอรีการทำงานปัจจุบัน มันถูกเก็บไว้ใน$PWD
ตัวแปรพิเศษ
เมื่อคุณทำ:
cd c/d
แม้ว่าc
หรือc/d
มี symlinks ขณะ$PWD
containes /a/b
มันผนวกc/d
ไปยังจุดสิ้นสุดเพื่อให้กลายเป็น$PWD
/a/b/c/d
และเมื่อคุณ:
cd ../e
แทนการทำก็ไม่chdir("../e")
chdir("/a/b/c/e")
และpwd
คำสั่งจะส่งคืนเนื้อหาของ$PWD
ตัวแปรเท่านั้น
สิ่งนี้มีประโยชน์ในเชลล์แบบโต้ตอบเนื่องจากpwd
เอาต์พุตเส้นทางไปยังไดเร็กทอรีปัจจุบันที่ให้ข้อมูลว่าคุณไปถึงที่นั่นได้อย่างไรและตราบใดที่คุณใช้เฉพาะ..
ในอาร์กิวเมนต์เพื่อcd
และไม่ใช่คำสั่งอื่น ๆ มีโอกาสน้อยที่จะทำให้คุณประหลาดใจเพราะcd a; cd ..
หรือcd a/..
โดยทั่วไปแล้ว ไปยังที่ที่คุณอยู่
ตอนนี้ไม่ได้แก้ไขจนกว่าคุณจะทำ$PWD
cd
จนกว่าจะถึงครั้งต่อไปที่คุณโทรcd
หรือpwd
อาจมีสิ่งต่าง ๆ เกิดขึ้นมากมายส่วนประกอบของ$PWD
สามารถเปลี่ยนชื่อได้ ไดเรกทอรีปัจจุบันจะไม่เปลี่ยนแปลง (เป็น inode เดียวกันเสมอแม้ว่าจะสามารถลบได้) แต่เส้นทางในไดเรกทอรีต้นไม้อาจเปลี่ยนแปลงได้อย่างสมบูรณ์ getcwd()
คำนวณไดเรกทอรีปัจจุบันทุกครั้งที่เรียกใช้โดยการเดินทรีไดเรกทอรีเพื่อให้ข้อมูลมีความถูกต้องอยู่เสมอ แต่สำหรับโลจิคัลไดเร็กทอรีที่นำมาใช้โดยเชลล์ POSIX ข้อมูลใน$PWD
อาจไม่เสถียร ดังนั้นเมื่อวิ่งcd
หรือpwd
กระสุนบางนัดอาจต้องการป้องกันสิ่งนั้น
ในตัวอย่างนั้นคุณจะเห็นพฤติกรรมที่แตกต่างกับกระสุนที่แตกต่างกัน
บางคนชอบksh93
เพิกเฉยปัญหาทั้งหมดดังนั้นจะส่งคืนข้อมูลที่ไม่ถูกต้องแม้หลังจากที่คุณโทรcd
(และคุณจะไม่เห็นพฤติกรรมที่คุณเห็นด้วยbash
)
บางคนชอบbash
หรือzsh
ไม่ตรวจสอบว่า$PWD
ยังคงเป็นเส้นทางไปยังไดเรกทอรีปัจจุบันเมื่อแต่ไม่เมื่อcd
pwd
pdksh ตรวจสอบทั้งpwd
และcd
(แต่pwd
ไม่อัปเดต$PWD
)
ash
(อย่างน้อยหนึ่งที่พบใน Debian) ไม่ได้ตรวจสอบและเมื่อคุณทำcd a
มันจริง ๆ แล้วcd "$PWD/a"
ดังนั้นหากไดเรกทอรีปัจจุบันมีการเปลี่ยนแปลงและ$PWD
ไม่ชี้ไปที่ไดเรกทอรีปัจจุบันจริง ๆ แล้วจะไม่เปลี่ยนเป็นa
ไดเรกทอรีในไดเรกทอรีปัจจุบัน แต่มีหนึ่งใน$PWD
(และส่งคืนข้อผิดพลาดหากไม่มีอยู่)
หากคุณต้องการเล่นกับมันคุณสามารถทำได้:
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)
ในเปลือกหอยต่างๆ
ในกรณีของคุณเนื่องจากคุณกำลังใช้bash
หลังจากที่cd a
, bash
การตรวจสอบว่า$PWD
ยังคงชี้ไปที่ไดเรกทอรีปัจจุบัน ในการทำเช่นนั้นมันจะเรียกstat()
ค่าของ$PWD
การตรวจสอบหมายเลขไอโหนดและเปรียบเทียบกับของ.
การตรวจสอบหมายเลขไอโหนดและเปรียบเทียบกับที่ของ
แต่เมื่อค้นหา$PWD
เส้นทางที่เกี่ยวข้องกับการแก้ไข symlink มากเกินไปที่stat()
กลับมาพร้อมกับข้อผิดพลาดดังนั้นเชลล์ไม่สามารถตรวจสอบว่า$PWD
ยังสอดคล้องกับไดเรกทอรีปัจจุบันดังนั้นจึงคำนวณมันอีกครั้งด้วยgetcwd()
และการปรับปรุง$PWD
ตาม
ตอนนี้เพื่อชี้แจงคำตอบของ Patrice การตรวจสอบจำนวน symlink ที่พบในขณะที่ค้นหาเส้นทางคือการป้องกันลูป symlink การวนซ้ำที่ง่ายที่สุดสามารถทำได้ด้วย
rm -f a b
ln -s a b
ln -s b a
หากไม่มีพนักงานรักษาความปลอดภัยนั้นcd a/x
ระบบจะต้องค้นหาตำแหน่งที่a
เชื่อมโยงไปหาb
และเป็น symlink ที่เชื่อมโยงไปยังa
และมันจะดำเนินต่อไปเรื่อย ๆ วิธีที่ง่ายที่สุดในการป้องกันคือการยอมแพ้หลังจากการแก้ไขมากกว่าจำนวน symlink ที่กำหนดเอง
ตอนนี้กลับไปยังไดเรกทอรีการทำงานแบบลอจิคัลปัจจุบันและทำไมคุณลักษณะไม่ดีนัก สิ่งสำคัญคือต้องตระหนักว่ามันมีไว้สำหรับcd
เชลล์เท่านั้นและไม่ใช่คำสั่งอื่น ๆ
ตัวอย่างเช่น
cd -- "$dir" && vi -- "$file"
ไม่เหมือนกับ:
vi -- "$dir/$file"
นั่นเป็นเหตุผลที่บางครั้งคุณจะพบว่าผู้คนแนะนำให้ใช้cd -P
ในสคริปต์เสมอเพื่อหลีกเลี่ยงความสับสน (คุณไม่ต้องการให้ซอฟต์แวร์ของคุณจัดการกับข้อโต้แย้งที่../x
แตกต่างจากคำสั่งอื่น ๆ เพียงเพราะเขียนในเชลล์แทนภาษาอื่น)
-P
ตัวเลือกคือการปิดการใช้งานไดเรกทอรีตรรกะจัดการเพื่อให้cd -P -- "$var"
จริงไม่เรียกchdir()
กับเนื้อหาของ$var
(ยกเว้นเมื่อ$var
เป็น-
แต่ที่เรื่องอื่น) และหลังจากcd -P
นั้น$PWD
จะมีเส้นทางแบบบัญญัติ