การค้นหา PATH รวมถึงสัญลักษณ์


9

มาตรฐานเชลล์ POSIX ระบุไว้ในเว็บไซต์นี้

http://pubs.opengroup.org/onlinepubs/9699919799/

เกี่ยวกับวิธีที่ shells ใช้PATHเพื่อค้นหา executables:

"รายการจะถูกค้นหาตั้งแต่ต้นจนจบนำชื่อไฟล์ไปใช้กับแต่ละคำนำหน้าจนกว่าจะพบไฟล์ปฏิบัติการที่มีชื่อที่ระบุและพบการอนุญาตการใช้งานที่เหมาะสม"

นี่ไม่ใช่ลักษณะที่ปรากฏในการใช้งาน POSIX จริง:

man which พูดว่า:

"ส่งคืนชื่อพา ธ ของไฟล์ (หรือลิงก์) ซึ่งจะถูกดำเนินการในสภาพแวดล้อมปัจจุบันได้รับข้อโต้แย้งเป็นคำสั่งในเชลล์ที่สอดคล้องกับ POSIX อย่างเคร่งครัดมันทำสิ่งนี้โดยการค้นหา PATH สำหรับไฟล์ปฏิบัติการที่ตรงกับชื่อของ อาร์กิวเมนต์มันไม่ได้ติดตามลิงก์สัญลักษณ์ "

ตกลงเรามาดูสถานการณ์นี้กัน:

$ pwd /home/mark

$ echo $PATH /home/mark/bin:...

$ ls -l bin/foobar
lrwxrwxrwx 1 mark mark 18 Dec 12 22:51 bin/foobar -> /home/mark/foobar1
$ touch foobar1
$ which foobar
$ chmod a+x foobar1
$ which foobar
/home/mark/bin/foobar

ตกลงนี่คือลิงก์สัญลักษณ์ที่PATHมีชื่อที่ถูกต้องและมีการรายงานโดยlsให้เรียกใช้งานได้

which ไม่ได้ดูเลย แต่สนใจเฉพาะสิ่งที่ชี้ไปเท่านั้น

แม้ว่าจะมีข้อเท็จจริงที่ว่าทั้งคู่man whichบอกอย่างชัดเจนว่ามันไม่ได้เป็นไปตามลิงก์สัญลักษณ์ (และแน่นอนว่าเราไม่เห็นเพราะwhich foobarไม่พิมพ์foobar1) และเอกสาร POSIX เชลล์ที่ยกมาด้านบนไม่เคยกล่าวถึง symlink ในPATHอัลกอริทึม

ดังนั้นwhichและเชลล์ที่มีอยู่ผิดหรือฉันไม่เข้าใจเอกสารหรือไม่

เพื่อ CLARIFY:

ฉันรู้และสามารถอธิบายพฤติกรรมที่มีอยู่ คำถามของฉันไม่ใช่ "สิ่งนี้ทำงานอย่างไร" ที่ฉันรู้.

คำถามของฉันเกี่ยวกับเอกสาร: ข้อผิดพลาดของฉันในการติดตามเอกสารที่ฉันยกมา หรือเอกสารผิดหรือเปล่า?

แรงจูงใจ: ทำไมฉันถึงแคร์

ฉันเป็นผู้ดำเนินการ ผู้ใช้งานที่แตกต่างกันมีข้อกำหนดที่แตกต่างกัน สำหรับฉันความต้องการก็คือคำของมาตรฐาน POSIX ปัจจุบันจะต้องปฏิบัติตามอย่างแน่นอน (หรือแม่นยำยิ่งขึ้นดีที่สุดเท่าที่จะทำได้เพราะมาตรฐานตัวเองค่อนข้างจะบั๊กกี้) เหมือนที่เป็นพระวจนะของพระเจ้า

ตอนนี้ถ้อยคำมาตรฐานค่อนข้างชัดเจน - ไม่มีการพูดถึงสัญลักษณ์เชื่อมโยงซึ่งในที่อื่น ๆ อีกหลายแห่งจะมีการกล่าวถึงตำแหน่งที่ต้องทำ ดังนั้นในกรณีนี้อย่า

แต่ผมคู่เสมอตรวจสอบวิธีการdashและbashประพฤติเพียงเพื่อให้แน่ใจว่า ตอนนี้แน่นอนว่ามีปัญหาเล็กน้อยเช่นกันที่นี่dashแม้ว่าจะถูกเรียกเก็บเงินเป็น POSIX แต่ก็มีข้อบกพร่องเล็ก ๆ มากมายที่สอดคล้องกับ POSIX bashฉันยังไม่พบข้อบกพร่องใด ๆ ของ POSIX แต่ ... ทุบตีไม่ได้เป็น POSIX จริง ๆ มันมากกว่านั้น

ดังนั้นคุณมีมัน นั่นคือเหตุผลที่ฉันสนใจ


คุณไม่เข้าใจซึ่งไม่เป็นไปตาม symlinks ในไฟล์ $PATHสามารถมี symlink ลองwhich shดู
Ipor Sircer

ตกลง แต่ในกรณีของฉัน$PATHไม่มีการเชื่อมโยงใด ๆ
user322908

ในเกือบทุกสถานการณ์ symlink จะถูกติดตามอย่างโปร่งใส กรณีที่ไม่ได้กล่าวถึงอย่างชัดเจน (เช่นการเรียกระบบเช่นlstat(2)) การติดตามกรณีเหล่านี้มักไม่ได้ระบุไว้ ยกตัวอย่างเช่นคำอธิบายของopen(2)เพียงกล่าวถึง symlinks O_CREAT | O_EXCLเมื่อพูดคุยเกี่ยวกับพฤติกรรมของ ไม่จำเป็นต้องระบุว่าจะเปิดไฟล์เป้าหมาย
Barmar

คำตอบ:


10

สิทธิ์ของ symlink นั้นไม่เกี่ยวข้อง คุณไม่สามารถเปลี่ยนแปลงได้หากคุณพยายาม

สิ่งสำคัญคือการอนุญาตของไฟล์อ้างอิง

มันเป็นการดีที่จะมีไดเรกทอรีใน PATH ของคุณรวมถึง symlinks เพื่อ executables ในความเป็นจริงมันเป็นไปได้ว่าไฟล์ปฏิบัติการหลายรายการใน PATH ของคุณเป็น symlink ตัวอย่างเช่นบนระบบเดเบียน / เหมือนอูบุนตู:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 23  2017 /bin/sh -> dash

เอกสาร

จากman chmod:

chmod ไม่เคยเปลี่ยนการอนุญาตของลิงก์สัญลักษณ์; การเรียกระบบ chmod ไม่สามารถเปลี่ยนการอนุญาตได้ นี่ไม่ใช่ปัญหาเนื่องจากไม่มีการใช้สิทธิ์ของลิงก์สัญลักษณ์ อย่างไรก็ตามสำหรับแต่ละลิงก์สัญลักษณ์ที่แสดงรายการบนบรรทัดคำสั่ง chmod จะเปลี่ยนการอนุญาตของไฟล์แบบชี้ไปที่ ในทางตรงกันข้าม chmod จะไม่สนใจลิงก์สัญลักษณ์ที่พบระหว่างการสำรวจเส้นทางแบบเรียกซ้ำ [เน้นเพิ่มแล้ว]

ตัวอย่าง

เชลล์มีการทดสอบ-xเพื่อตรวจสอบว่าไฟล์สามารถเรียกใช้งานได้หรือไม่ ลองดูที่:

$ ls -l
total 0
lrwxrwxrwx 1 john1024 john1024 7 Dec 12 23:36 foo -> foobar1
-rw-rw---- 1 john1024 john1024 0 Dec 12 23:36 foobar1
$ [ -x foo ] && echo foo is executable
$ chmod +x foobar1
$ [ -x foo ] && echo foo is executable
foo is executable

ดังนั้นเช่นเดียวกับที่คุณพบwhichเชลล์จะไม่พิจารณา softlink ที่สามารถเรียกทำงานได้ยกเว้นว่าไฟล์ต้นแบบนั้นสามารถเรียกทำงานได้

วิธีการทำงาน

บนระบบ Debian whichคือเชลล์สคริปต์ ส่วนที่เกี่ยวข้องของรหัสคือ:

 case $PROGRAM in
  */*)
   if [ -f "$PROGRAM" ] && [ -x "$PROGRAM" ]; then
    puts "$PROGRAM"
    RET=0
   fi
   ;;
  *)
   for ELEMENT in $PATH; do
    if [ -z "$ELEMENT" ]; then
     ELEMENT=.
    fi
    if [ -f "$ELEMENT/$PROGRAM" ] && [ -x "$ELEMENT/$PROGRAM" ]; then
     puts "$ELEMENT/$PROGRAM"
     RET=0
     [ "$ALLMATCHES" -eq 1 ] || break
    fi
   done
   ;;
 esac

อย่างที่คุณเห็นมันใช้การ-xทดสอบเพื่อพิจารณาว่าไฟล์นั้นทำงานได้หรือไม่

POSIX ระบุการ-xทดสอบดังนี้:

-x pathname
จริงหากชื่อพา ธ แก้ไขเป็นรายการไดเรกทอรีที่มีอยู่สำหรับไฟล์ที่ได้รับอนุญาตให้ดำเนินการไฟล์ (หรือค้นหาถ้ามันเป็นไดเรกทอรี) จะได้รับอนุญาตตามที่กำหนดไว้ในไฟล์อ่านเขียนและการสร้าง เท็จถ้าชื่อพา ธ ไม่สามารถแก้ไขได้หรือหากชื่อพา ธ แก้ไขเป็นรายการไดเรกทอรีที่มีอยู่สำหรับไฟล์ที่ได้รับอนุญาตให้ดำเนินการ (หรือค้นหา) ไฟล์จะไม่ได้รับอนุญาต [เน้นเพิ่มแล้ว]

ดังนั้นการตรวจสอบ POSIX สิ่งที่ชื่อพา ธแก้ไขไป กล่าวอีกนัยหนึ่งมันยอมรับการเชื่อมโยง

ฟังก์ชัน POSIX exec

POSIX exec ฟังก์ชั่นดังต่อไปนี้ symlinks ข้อมูลจำเพาะ POSIX จะดำเนินต่อไปตามความยาวเพื่อระบุเงื่อนไขข้อผิดพลาดซึ่งอาจรายงานหาก symlink เป็นวงกลมหรือลึกเกินไปเช่น:

[ELOOP]
มีลูปในลิงก์สัญลักษณ์ที่พบในระหว่างการแก้ปัญหาพา ธ หรืออาร์กิวเมนต์ไฟล์

[ELOOP]
พบสัญลักษณ์มากกว่า {SYMLOOP_MAX} ลิงก์ระหว่างการแก้ปัญหาพา ธ หรืออาร์กิวเมนต์ไฟล์
[ENAMETOOLONG]
เป็นผลมาจากการพบลิงก์สัญลักษณ์ในการแก้ไขข้อโต้แย้งเส้นทางความยาวของสตริงชื่อพา ธ ที่ถูกแทนที่มีค่าเกิน {PATH_MAX}


ฉันรู้ทุกสิ่งที่คุณเขียนในคำตอบของคุณ ฉันรู้ว่าสิ่งที่ "ทำงาน" นั่นไม่ใช่คำถามของฉัน คำถามของฉันเกี่ยวกับเอกสาร ชี้ไปที่ฉันไม่เข้าใจเอกสาร หรือบอกฉันว่าเอกสารไม่ถูกต้อง
user322908

@ user322908 สำหรับระบบส่วนใหญ่whichจะเป็นเชลล์สคริปต์ ดังนั้นจึงน่าจะเป็นเพียงแค่ใช้การ-xทดสอบที่ฉันแสดงให้เห็น ตาม POSIX -xทดสอบว่าไฟล์ "แก้ไข" เพื่อปฏิบัติการหรือไม่ หากคุณเห็นบางสิ่งที่แตกต่างคุณกำลังมองหาที่ไหน
John1024

ขอบคุณและฉันขอโทษที่เจ็บปวดเช่นนี้ ... ฉันรู้ว่าคำถามของฉันแตกต่างจากคำถาม 99% ดังนั้นจึงยากที่จะเข้าใจว่าฉันเป็นอย่างไร อีกครั้งฉันไม่สนใจ "วิธี" whichทำงานหรือไม่ว่าจะเป็น-xหรืออย่างอื่น ฉันสนใจที่จะทราบว่าฉันไม่ได้ปฏิบัติตามเอกสารที่ฉันอ้างอย่างถูกต้องที่ไหน
user322908

4
@ user322908 ฟังก์ชันPOSIXexecติดตาม symlinks ดูเหมือนว่าจะค่อนข้างชัดเจนว่าไฟล์ symlinked สามารถปฏิบัติการได้ภายใต้ POSIX
John1024

2
ฉันยังตรวจสอบUbuntu 17.10 man whichซึ่งระบุว่า "ไม่ใช่ชื่อพา ธ ที่เป็นมาตรฐาน" ไม่ได้หมายความว่ามันไม่ได้ติดตามลิงค์ นั่นหมายความว่าอย่างที่คุณสังเกตเห็นว่ามันไม่ใช่ "บัญญัติ" ชื่อ
John1024

3

ในกรณีนี้ symlink จะถูกติดตามอย่างโปร่งใสโดยไม่ต้องกำหนดเส้นทางสุดท้ายให้เป็นมาตรฐาน กล่าวอีกนัยหนึ่งwhichไม่สนใจว่า/home/mark/binsymlink หรือไม่ สิ่งที่สำคัญคือไฟล์/home/mark/bin/foobarมีอยู่หรือไม่ มันไม่จำเป็นต้องไปด้วยตนเอง symlinks แผ่ไปตามเส้นทาง - ระบบปฏิบัติการสามารถทำเช่นนั้นได้ดีในตัวเอง

และแน่นอนเมื่อwhichถามเกี่ยวกับข้อมูลไฟล์ของ/home/mark/bin/foobarระบบปฏิบัติการ/home/mark/binจะแจ้งให้ทราบว่าเป็น symlink ติดตามและประสบความสำเร็จfoobarในไดเรกทอรีเป้าหมาย

นี่คือพฤติกรรมเริ่มต้นยกเว้นว่าโปรแกรมใช้open(…, O_NOFOLLOW)หรือfstatat(…, AT_SYMLINK_NOFOLLOW)เข้าถึงไฟล์

[ความคิดเห็นที่รวมใน]

ในขณะที่คุณพูดว่าอรรถประโยชน์ของเชลล์ดำเนินการเป็นกรณี ๆ ไปมันไม่เหมือนกันกับเคอร์เนล syscalls: การเรียกที่เกี่ยวข้องกับไฟล์ทั้งหมดจะติดตาม symlinks โดยค่าเริ่มต้นเว้นแต่จะมีการกำหนดแฟล็ก "nofollow" (แม้ lstat จะติดตาม symlink ในส่วนประกอบพา ธ ทั้งหมดยกเว้นอันสุดท้าย)

เมื่อสเปคไม่ได้กล่าวถึงอย่างชัดเจนว่าจะทำอย่างไรกับ symlink มันหมายถึงพฤติกรรมเริ่มต้นที่จะใช้ นั่นคือเชลล์ที่ติดตามอัลกอริธึมพา ธ neithers จะแก้ไข symlink ด้วยตนเองและไม่เลือกที่จะไม่ทำเช่นเดียวกัน (มันแค่เชื่อมโยงส่วนประกอบ $ PATH แต่ละรายการกับชื่อที่ปฏิบัติการได้)

เมื่อหน้าคู่มือ (1) บอกว่ามันไม่ได้เป็นไปตาม symlink มันอาจหมายถึงหลายสิ่งหลายอย่าง แต่ coreutils ของ GNU ระบุว่าเป็นแบบนี้:

ซึ่งจะพิจารณาสองไดเรกทอรีที่เทียบเท่ากันจะแตกต่างกันเมื่อหนึ่งในนั้นมีเส้นทางที่มีลิงก์สัญลักษณ์

นั่นเป็นขอบเขตที่แคบกว่ามาก - หมายความว่าwhichจะไม่พยายามกำหนดเส้นทางทั้งหมดเพื่อกำจัดวัชพืชที่ซ้ำกันด้วยตนเองแต่ไม่ได้หมายความว่าเครื่องมือจะเลือกไม่ใช้ symlink ตามด้วยระบบปฏิบัติการโดยทั่วไป ตัวอย่างเช่นถ้า/binเป็น symlink ไป/usr/binทำงานwhich -a shจะกลับมาทั้งสอง และ/bin/sh/usr/bin/sh


ใช่ขอบคุณฉันรู้ทั้งหมดนี้ คำถามของฉันไม่ใช่สิ่งที่ "ทำงาน" ฉันรู้ว่าพวกเขาทำงานอย่างไร นั่นไม่ใช่จุดของฉัน ประเด็นของฉันเกี่ยวกับเอกสาร ฉันทำตามเอกสารไม่ถูกต้อง หรือเอกสารไม่ถูกต้อง
user322908

2
คุณกำลังเข้าใจเอกสารอย่างไม่ถูกต้อง - หากไม่ได้กล่าวถึง symlink ต่อไปนี้แสดงว่าไม่ได้แก้ไข symlink ด้วยตนเองแต่ลักษณะการทำงานของระบบปฏิบัติการปกติยังคงใช้อยู่ whichหน้าคู่มือGNU ระบุว่าแตกต่าง: "ซึ่งจะพิจารณาสองไดเรกทอรีที่เทียบเท่ากันจะแตกต่างกันเมื่อหนึ่งในนั้นมีเส้นทางที่มีลิงก์สัญลักษณ์"
user1686

ตกลงนั่นดีกว่าขอบคุณ! ฉันพยายามที่จะเข้าใจ ... แต่ ... อภัยความเจ็บปวดในลำคอ: "พฤติกรรมของระบบปฏิบัติการปกติ" ไม่ใช่การติดตามสัญลักษณ์โดยปริยาย มีสาธารณูปโภคมากมายที่ไม่มี เป็นกรณี ๆ ไป
user322908

1
การเรียกเคอร์เนลทั้งหมด - chdir, เปิด, chmod, execve ... - จะติดตาม symlink ทั้งในพา ธ และส่วนท้ายยกเว้นว่าคุณระบุ AT_SYMLINK_NOFOLLOW หรือคล้ายกัน (lstat เป็นเพียงอันเดียวซึ่งไม่ได้อ้างอิงการเชื่อมโยงที่หาง แต่ก็ยังทำเช่นนั้นสำหรับเส้นทางที่เหลืออยู่) ดังนั้นพฤติกรรมเริ่มต้นคือการติดตาม symlinks ตัวอย่างเช่นเมื่อเชลล์เรียกexecve("/home/mark/bin/foobar", …)มันจะส่งผลให้ symlink ทั้งหมดถูกติดตาม
user1686

ตกลงฉันคิดว่าฉันกำลังซื้อexecveข้อโต้แย้งที่จริงในการดำเนินการของฉันมันเป็นexecl()สิ่งเดียวกัน กรุณาถ้าคุณรวมไว้ในคำตอบของคุณฉันจะยอมรับ
user322908

1

เชลล์เป็นไปตามเอกสารประกอบของมันซึ่งเป็นไปตามกฎสำหรับการจำแนกชื่อพา ธ whichสอดคล้องกับเอกสารประกอบ ทั้งสองทำสิ่งที่แตกต่างกันเล็กน้อย

เอาต์พุตของwhichคือชื่อไฟล์และพา ธ ของลิงก์ไม่ใช่พา ธ ไปยังสิ่งที่ symlink ชี้ไป นี่คือการสะกดคำในหน้าคน

เมื่อมีคำสั่งจะถูกดำเนินการเชื่อมโยงจะ "ตาม" ตามมาตรา 4.13 Pathname มติในที่เดียวกัน ประโยคที่เกี่ยวข้องสำหรับการดำเนินการไฟล์คือ:

ในกรณีอื่น ๆ ทั้งหมดระบบจะนำหน้าชื่อพา ธ ที่เหลืออยู่หากมีเนื้อหาของลิงค์สัญลักษณ์ยกเว้นว่าหากเนื้อหาของลิงค์สัญลักษณ์เป็นสตริงว่างการแก้ไขชื่อพา ธ จะล้มเหลวด้วยฟังก์ชั่นการรายงาน [ENOENT ] ข้อผิดพลาดและยูทิลิตี้ที่เขียนข้อความวินิจฉัยที่เทียบเท่าหรือชื่อพา ธ ของไดเรกทอรีที่มีลิงค์สัญลักษณ์จะต้องใช้แทนเนื้อหาของลิงค์สัญลักษณ์ หากเนื้อหาของลิงค์สัญลักษณ์ประกอบด้วยอักขระเพียงอย่างเดียวแล้วอักขระนำหน้าทั้งหมดของชื่อพา ธ ที่เหลือจะถูกตัดออกจากชื่อพา ธ แบบรวมที่เกิดขึ้นโดยทิ้งเฉพาะอักขระนำจากเนื้อหาลิงก์สัญลักษณ์ ในกรณีที่คำนำหน้าเกิดขึ้นหากความยาวรวมเกิน {PATH_MAX} และการนำไปปฏิบัติถือว่าสิ่งนี้เป็นข้อผิดพลาด การจำแนกชื่อพา ธ จะล้มเหลวด้วยฟังก์ชั่นที่รายงานข้อผิดพลาด [ENAMETOOLONG] และยูทิลิตี้ที่เขียนข้อความวินิจฉัยที่เทียบเท่ากัน มิฉะนั้นชื่อพา ธ ที่ได้รับการแก้ไขจะเป็นความละเอียดของชื่อพา ธ ที่เพิ่งสร้างขึ้น หากชื่อพา ธ ที่เป็นผลลัพธ์ไม่ได้ขึ้นต้นด้วย a จะมีการนำบรรพบุรุษของชื่อไฟล์แรกของชื่อพา ธ ไปเป็นไดเรกทอรีที่มีลิงก์สัญลักษณ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.