ทำไม“ ls” จึงต้องการกระบวนการแยกต่างหากสำหรับการดำเนินการ


14

เหตุใดจึงlsต้องมีกระบวนการแยกต่างหากสำหรับการดำเนินการ ฉันรู้ว่าสาเหตุที่คำสั่งเช่นcdไม่สามารถดำเนินการได้โดยกลไกการฟอร์กกิ้ง แต่มีอันตรายใด ๆ หากlsถูกสั่งการโดยไม่ฟอร์กกิ้ง?


1
แม้ว่าlsจะเป็นโปรแกรมภายนอกecho *หรือecho * .*(ขึ้นอยู่กับตัวเลือกของเชลล์) ทำงานได้ค่อนข้างดีในการแสดงรายการไฟล์โดยไม่ต้องฟอร์ก
gerrit

สิ่งนี้ดียิ่งขึ้น: printf "% s \ n" *
Costa

เชลล์หลากหลายหมายเหตุ: tcsh มี builtin ซึ่งการกระทำเช่นls-F ls -Fมันมีประสิทธิภาพ คุณมักจะได้รับ-Fซึ่งมักจะเป็นความคิดที่ดี หากคุณระบุตัวเลือกอื่น ๆ มันจะทำหน้าที่เป็นคำสั่งภายนอก

คำตอบ:


18

คำตอบนั้นมากขึ้นหรือน้อยลงที่lsสามารถเรียกใช้งานได้จากภายนอก type -p lsคุณสามารถดูสถานที่ของตนโดยการเรียกใช้

ทำไมไม่lsสร้างไว้ในเชลล์ล่ะ? ทำไมมันถึงเป็นอย่างนั้น? งานของเชลล์ไม่ได้รวมทุกคำสั่งที่มีอยู่ แต่เพื่อจัดเตรียมสภาพแวดล้อมที่สามารถเรียกใช้งานได้ กระสุนสมัยใหม่บางรุ่นมีecho, printfและ, ตระกูลของพวกเขาเป็น builtins, ซึ่งโดยทางเทคนิคไม่จำเป็นต้องเป็น buildins, แต่ถูกสร้างขึ้นมาเพื่อเหตุผลด้านประสิทธิภาพเมื่อพวกมันถูกเรียกใช้ซ้ำ ๆ เชลล์จะต้องแยกและดำเนินกระบวนการใหม่สำหรับการเรียกแต่ละครั้งซึ่งอาจช้ามาก

อย่างน้อยที่สุดการรันไฟล์เรียกทำงานlsภายนอกต้องรันหนึ่งในตระกูล exec ของการเรียกระบบ คุณสามารถทำได้โดยไม่ต้องฟอร์ก แต่จะแทนที่เชลล์หลักที่คุณใช้อยู่ คุณสามารถดูว่าเกิดอะไรขึ้นในอินสแตนซ์นั้นโดยทำสิ่งต่อไปนี้:

exec ls; echo "this never gets printed"

เนื่องจากอิมเมจกระบวนการเชลล์ของคุณถูกแทนที่เชลล์ปัจจุบันจะไม่สามารถเข้าถึงได้หลังจากทำสิ่งนี้แล้ว เพื่อให้เชลล์สามารถรันต่อไปหลังจากรัน ls คำสั่งจะต้องสร้างขึ้นในเชลล์

Forking อนุญาตให้เปลี่ยนกระบวนการที่ไม่ใช่เชลล์หลักของคุณซึ่งหมายความว่าคุณสามารถรันเชลล์ต่อได้ในภายหลัง


1
ฉันคิดว่าเขาถามว่าทำไม ls (1) ไม่ใช่คุณสมบัติ builtin ของ shells ซึ่งบางคนจะต้องอธิบายว่าผู้จำหน่ายต่าง ๆ มีตัวเลือกต่าง ๆ สำหรับ ls (1) และสามารถค้นหาสิ่งต่าง ๆ จากระบบไฟล์ ฯลฯ และยัง อัพและส่วนใหญ่ของการมี 'เปลือก' ในเปลือก
llua

@llua ผมเพิ่มข้อมูลบางอย่างเกี่ยวกับที่และกรณียกเว้นecho, printfฯลฯ
คริสลง

มันไม่ชัดเจนเสมอไปว่าทำไมบางสิ่งบางอย่างในตัวและสิ่งอื่น ๆ ไม่ได้ ตัวอย่างเช่นทำไมcdไม่สามารถเรียกใช้งานภายนอกได้
Faheem Mitha

@FaheemMitha มีเป็นภายนอกcdที่ปฏิบัติการในระบบปฏิบัติการที่สอดคล้องกับ POSIX ( ดูที่นี่ ) หากคุณต้องการ chdir () จริง ๆ ในกระบวนการปัจจุบันคุณต้องสร้างมันไว้ในเชลล์
Chris Down

มันกลายเป็นนิสัยทำไมlsภายนอก แต่สามารถนำไปใช้ในเชลล์ได้เช่นกัน ดู busybox

15

คู่มือทุบตีอ้างอิงฯ :

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

นั่นคือเปลือกหอยถูกออกแบบมาเพื่อเท่านั้นรวมคำสั่งในตัวเท่านั้นหาก:

  1. จำเป็นต้องใช้ตามมาตรฐาน POSIX
  2. คำสั่งที่ต้องการเข้าถึงเชลล์เองเช่นบิวด์อินควบคุมงาน
  3. คำสั่งที่ง่ายมากไม่ขึ้นกับระบบปฏิบัติการและเพิ่มประสิทธิภาพการดำเนินการเมื่อนำมาใช้ในตัวเช่น printf

lsคำสั่งไม่เหมาะสมใด ๆ ของข้อกำหนดดังกล่าวข้างต้น

อย่างไรก็ตามนี่คือข้อ จำกัด การเขียนโปรแกรมที่จะป้องกันไม่ให้ lsถูกฝังอยู่ในตัวที่กำลังดำเนินการในกระบวนการเดียวกับล่ามทุบตี เหตุผลการออกแบบสำหรับคำสั่งที่ไม่ได้รับการฝังอยู่ในตัวเชลล์คือ:

  1. เชลล์ควรแยกจากระบบไฟล์ - ไม่มีคำสั่งในตัวควรขึ้นอยู่กับการทำงานที่ถูกต้องของระบบไฟล์หรืออุปกรณ์ต่อพ่วง
  2. คำสั่งที่อาจเป็นประเภทระบบไฟล์หรือขึ้นอยู่กับระบบปฏิบัติการที่ควรจะเป็นปฏิบัติการแยกต่างหาก
  3. คำสั่งที่คุณอาจต้องการไพพ์ไปหรือจากนั้นควรเป็นกระบวนการแยกต่างหาก
  4. คำสั่งที่คุณอาจต้องการรันในพื้นหลังควรเป็นไฟล์ปฏิบัติการแยกต่างหาก
  5. คำสั่งที่มีพารามิเตอร์ที่เป็นไปได้จำนวนมากจะถูกนำไปใช้ในการปฏิบัติการแยกต่างหาก
  6. คำสั่งที่ควรมีเอาต์พุตเดียวกันโดยไม่คำนึงถึงชนิดของเชลล์ (bash, csh, tsh, ... ) ที่เรียกใช้พวกมันควรจะเป็นไฟล์แบบสแตนด์อะโลน

เกี่ยวกับเหตุผลแรก - คุณต้องการให้เชลล์มีความเป็นอิสระและมีความยืดหยุ่นมากที่สุด คุณไม่ต้องการให้เชลล์ติดกับlsNFS mount นั่นคือ "ไม่ตอบสนองยังคงพยายาม"

เกี่ยวกับเหตุผลข้อที่สอง - ในหลาย ๆ กรณีคุณอาจต้องการใช้เชลล์สำหรับระบบที่ใช้ Busybox หรือระบบไฟล์อื่น ๆ ที่มีการlsใช้งานที่แตกต่างกัน หรือแม้แต่ใช้เชลล์ซอร์สเดียวกันใน OS ของที่มีความแตกต่างกันlsการใช้งานที่

เกี่ยวกับเหตุผลที่สาม - สำหรับการแสดงออกเช่นfind . -type d | xargs ls -ladมันจะเป็นเรื่องยากหรือเป็นไปไม่ได้ที่จะใช้lsในกระบวนการเดียวกับล่ามเปลือก

เกี่ยวกับเหตุผลข้อที่สี่ - lsคำสั่งบางคำสั่งอาจใช้เวลานานกว่าจะสำเร็จ คุณอาจต้องการให้เชลล์ดำเนินการอย่างอื่นต่อไปในระหว่างนี้


หมายเหตุ: ดูโพสต์ที่เป็นประโยชน์นี้โดยWarren Youngเพื่อตอบคำถามที่คล้ายกัน


คุณพลาดความง่ายในการไพพ์เอาท์พุทถ้ามันเป็นคำสั่งที่แยกต่างหากและการเขียนโปรแกรมทั้งหมดที่ใช้ในการไพพ์เชลล์ดั้งเดิมในการปฏิบัติการแยกต่างหาก
Bruce Ediger

@BruceEdiger: ยินดีที่ได้รับความคิดเห็นจากผู้ที่นับถือ BE ขอบคุณ! ฉันเชื่อว่าเหตุผลที่ 3 ครอบคลุมความคิดเห็นของคุณใช่ไหม
Jonathan Ben-Avraham

1
ฉันคิดเพิ่มเติมตามบรรทัดของความซับซ้อนของซอร์สโค้ดของเชลล์เองว่ามันจะต้องจัดการกับไพพ์สำหรับกระบวนการภายนอกและไพพ์เอาต์พุตของคำสั่งภายในเช่นสมมุติlsลงในกระบวนการภายนอก มันสามารถทำได้ แต่มันจะซับซ้อน
Bruce Ediger

1
ฉันกลัวที่สุดถ้าไม่ใช่ 5 คะแนนของคุณทั้งหมดจะเป็นที่สงสัย 1: ls คือ (หวังว่า) จะเป็นอิสระจากการใช้งานระบบไฟล์ ขึ้นอยู่กับเคอร์เนลที่จะจัดหาอินเตอร์เฟสที่สอดคล้องกับไลบรารี่มาตรฐานและแอพพลิเคชั่น 2: ls มีแนวโน้มน้อยที่ต้องพึ่งพาระบบปฏิบัติการมากกว่าเชลล์ 3: เปลือกหอยอนุญาตให้ buildins เป็นท่อแน่นอน 4: เชลล์อนุญาตให้บิวด์อินรันในเบื้องหลังได้อย่างแน่นอน 5: มันค่อนข้างเป็นอัตนัย
jlliagre

1
@ JonathanBen-Avraham @BruceEdiger เชลล์ไม่ได้จัดการกรณีท่อสำหรับ builtins กับ subshells หรือไม่? เช่นการส่งออกbash alias | grep lsอินพุตcat /etc/passwd | while read a; do echo "$a"; done
Matt

2

lsไม่ต้องใช้กระบวนการแยกต่างหาก จริง ๆ แล้วคำสั่งน้อยมากต้องการกระบวนการแยกต่างหาก: เฉพาะคำสั่งที่ต้องการเปลี่ยนสิทธิ์

ตามกฎแล้วเชลล์ใช้คำสั่งเป็นบิลด์เมื่อจำเป็นต้องใช้คำสั่งเหล่านั้นเป็นบิลด์ คำสั่งเช่นalias, cd, exit, export, jobs, ... ต้องอ่านหรือแก้ไขบางรัฐภายในของเปลือกและดังนั้นจึงไม่สามารถเป็นโปรแกรมแยก คำสั่งที่ไม่มีข้อกำหนดดังกล่าวสามารถแยกคำสั่งได้ ด้วยวิธีนี้พวกเขาสามารถเรียกจากเปลือกหรือโปรแกรมอื่น ๆ

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

  • command- แต่มันจะเสียประโยชน์ในสถานการณ์ที่PATHอาจไม่สามารถตั้งค่าได้อย่างถูกต้องและสคริปต์ใช้commandเป็นส่วนหนึ่งของการตั้งค่า
  • echo - มันเป็น builtin สำหรับประสิทธิภาพ
  • help - มันสามารถใช้ฐานข้อมูลแยกจากกันได้ แต่การฝังข้อความช่วยเหลือลงในเชลล์ที่สามารถเรียกใช้งานได้นั้นมีข้อดีของการทำให้เชลล์สามารถเรียกทำงานได้ในตัว
  • kill - มีข้อดีสองประการในการมี builtin: มันสามารถรับรู้ตำแหน่งงานนอกเหนือจาก ID กระบวนการและสามารถใช้งานได้แม้ว่าจะมีทรัพยากรไม่เพียงพอที่จะเริ่มกระบวนการแยกต่างหาก
  • printf- ด้วยเหตุผลเดียวกันกับechoและเพื่อสนับสนุน-vตัวเลือกในการใส่ผลลัพธ์ในตัวแปร
  • pwd - builtin มีความสามารถเพิ่มเติมของการติดตามไดเรกทอรีโลจิคัลปัจจุบัน (ปล่อยให้ลิงก์สัญลักษณ์ไม่เสียหายแทนที่จะขยายออก)
  • test- มันมีประสิทธิภาพในตัว (และทุบตีด้วยเวทมนตร์ด้วยไฟล์ที่เรียกว่า/dev/fd/…ระบบปฏิบัติการบางระบบ)

กระสุนจำนวนหนึ่งมีจำนวนบิวด์อินเพิ่มขึ้นจำนวนมาก มีสายสะพายซึ่งเป็นเชลล์ที่ออกแบบมาให้เป็นไบนารีแบบสแตนด์อโลนสำหรับการซ่อมแซมฉุกเฉิน (เมื่อคำสั่งภายนอกบางอย่างอาจไม่สามารถใช้งานได้) แต่ก็มีในตัวlsเรียกว่า-lsเช่นเดียวกับเครื่องมืออื่น ๆ เช่นและ-grep -tarบิวด์อินของ Sash มีความสามารถน้อยกว่าคำสั่งแบบเต็ม ข้อเสนอ zsh builtins ที่คล้ายกันบางอย่างในของzsh โมดูล มันไม่ได้มีlsแต่การขยายตัวสัญลักษณ์ ( echo *) และzstatสามารถให้บริการฟังก์ชั่นที่คล้ายกัน


2

ฉันคิดว่าสิ่งที่คนขาดหายไปในที่นี้คือความซับซ้อนของการเฉือนของlsโปรแกรมGNU บน Linux เมื่อเปรียบเทียบขนาดที่สามารถใช้งานได้ของlsไปยังbashและdashเชลล์บนระบบ Debian ของเราเราเห็นว่ามันมีขนาดค่อนข้างใหญ่:

graeme@graeme:~$ ls -lh /bin/{ls,bash,dash}
-rwxr-xr-x 1 root root 953K Mar 30  2013 /bin/bash
-rwxr-xr-x 1 root root 115K Dec 25 20:25 /bin/dash
-rwxr-xr-x 1 root root 108K Jul 20 22:52 /bin/ls

การรวมฟีเจอร์lsเต็มรูปแบบเช่นเดียวกับ GNU เวอร์ชั่นbashจะเพิ่มขนาดไฟล์เรียกทำงานได้ 10% มันเกือบจะเป็นขนาดเดียวกับdashกระสุนเต็ม!

บิวด์เชลล์ส่วนใหญ่จะถูกเลือกเพราะมันรวมเข้ากับเชลล์ในวิธีที่เอ็กซีคิวต์ภายนอกไม่สามารถทำได้ (คำถามชี้ให้เห็นcdแต่อีกตัวอย่างหนึ่งคือเวอร์ชันทุบตีของkillการรวมเข้ากับการควบคุมงานทุบตี) หรือเพราะเป็นคำสั่งที่ง่ายมาก ให้ความเร็วที่มากเทียบกับขนาดผลตอบแทน ( trueและfalseเป็นเรื่องง่ายเท่าที่จะได้รับ)

GNU lsมีวัฏจักรการพัฒนาที่ยาวนานและอาจมีตัวเลือกในการปรับแต่งผลลัพธ์ที่แสดง การใช้ builtin ls เป็นค่าเริ่มต้นอาจสูญเสียฟังก์ชันการทำงานนี้หรือเพิ่มความซับซ้อนและขนาดของเชลล์


1

cdถูกสร้างขึ้นในเปลือกเป็นโปรแกรมแยกต่างหากที่คุณจะเห็นที่ls/bin/ls


0

ทำสิ่งที่คุณกำลังมองหา:

printf "%s\n" *

นอกจากนี้คุณสามารถจัดเก็บชื่อไฟล์ในอาร์เรย์:

files=(`printf "%s\n" *`)  #items are separated by whitespace
echo ${#files[*]} files
for index in ${!a[*]}
do printf "%d: %s\n" $index ${a[$index]};
done

แต่มันไม่สนใจช่องว่างในชื่อ
สิ่งนี้ส่งผ่านไปยังตัวแปรและไม่สนใจช่องว่าง:

printf "%s\n" * | while read a; do echo $a; done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.