ทำไมไม่ใช้“ อันไหน”? จะใช้อะไรดี?


328

เมื่อมองหาเส้นทางไปปฏิบัติการหรือตรวจสอบสิ่งที่จะเกิดขึ้นถ้าคุณใส่ชื่อคำสั่งในเปลือก Unix มีมากมายเหลือเฟือของสาธารณูปโภคที่แตกต่างกัน ( which, type, command, whence, where, whereis, whatis, hashฯลฯ )

เรามักได้ยินว่าwhichควรหลีกเลี่ยง ทำไม? เราควรใช้อะไรแทน


3
ฉันคิดว่าข้อโต้แย้งส่วนใหญ่ที่มีต่อการใช้whichนั้นถือว่าเป็นบริบทเชลล์แบบโต้ตอบ คำถามนี้ติดแท็ก / พกพา ดังนั้นฉันจึงตีความคำถามในบริบทนี้ว่า "สิ่งที่จะใช้แทนwhichการค้นหาสิ่งที่เรียกใช้งานได้ครั้งแรกของชื่อที่กำหนดใน$PATH" คำตอบและเหตุผลส่วนใหญ่เกี่ยวกับการwhichจัดการกับนามแฝงบิวด์อินและฟังก์ชั่นซึ่งในเชลล์สคริปแบบพกพาส่วนใหญ่ในโลกแห่งความจริงเป็นเพียงความสนใจด้านวิชาการ นามแฝงที่กำหนดไว้ในเครื่องจะไม่สืบทอดเมื่อเรียกใช้เชลล์สคริปต์ (เว้นแต่คุณจะส่งมาด้วย.)
MattBianco

5
@MattBianco ใช่csh(และwhichยังคงเป็นcshสคริปต์สำหรับ Unices เชิงพาณิชย์ส่วนใหญ่) จะอ่าน~/.cshrcเมื่อไม่มีการโต้ตอบ นั่นเป็นเหตุผลที่คุณจะสังเกตเห็นสคริปต์ csh #! /bin/csh -fมักจะเริ่มต้นด้วย whichไม่ได้เพราะมันมีจุดมุ่งหมายเพื่อให้คุณนามแฝงเพราะมันหมายถึงเป็นเครื่องมือสำหรับผู้ใช้ (interactive) cshของ POSIX command -vผู้ใช้เปลือกหอยที่มี
Stéphane Chazelas

@rudimeier แล้วคำตอบจะเป็นเสมอเว้นแต่เปลือกของคุณ(t)csh(หรือคุณไม่คิดว่ามันไม่ได้ให้ผลที่ถูกต้อง) ให้ใช้typeหรือcommand -vแทน ดูคำตอบให้ได้ว่าทำไม
Stéphane Chazelas

1
@rudimeier, ( stat $(which ls)ไม่ถูกต้องด้วยเหตุผลหลายประการ (ขาดหายไป--, ไม่มีเครื่องหมายคำพูด), ไม่เพียง แต่การใช้งานของwhich) stat -- "$(command -v ls)"คุณต้องการใช้ ที่ถือว่าlsเป็นคำสั่งที่พบในระบบไฟล์ (ไม่ใช่ builtin ของเชลล์หรือฟังก์ชันของ alias) whichอาจให้คุณเส้นทางที่ผิด (ไม่ใช่เส้นทางที่เชลล์ของคุณจะดำเนินการถ้าคุณป้อนls) หรือให้นามแฝงตามที่กำหนดไว้ในการกำหนดค่าของเปลือกหอยอื่น ๆ ...
Stéphane Chazelas

1
@rudimeier อีกครั้งมีเงื่อนไขหลายประการที่whichการใช้งานหลายอย่างไม่ให้คุณแม้กระทั่งสิ่งlsที่จะถูกค้นพบโดยการค้นหา$PATH(โดยไม่คำนึงถึงสิ่งที่lsอาจเกิดขึ้นในเชลล์ของคุณ) sh -c 'command -v ls'หรือzsh -c 'rpm -q --whatprovides =ls'มีแนวโน้มที่จะให้คำตอบที่ถูกต้องแก่คุณมากขึ้น จุดที่นี่เป็นที่ที่เป็นมรดกทางวัฒนธรรมของเสียจากwhich csh
Stéphane Chazelas

คำตอบ:


366

นี่คือทั้งหมดที่คุณไม่เคยคิดว่าคุณจะไม่อยากรู้เกี่ยวกับมัน:

สรุป

ในการรับชื่อพา ธ ของไฟล์เรียกทำงานในสคริปต์เชลล์แบบบอร์น (มีคำเตือนอยู่บ้างเล็กน้อยดูด้านล่าง):

ls=$(command -v ls)

หากต้องการค้นหาว่ามีคำสั่งที่กำหนดอยู่หรือไม่:

if command -v given-command > /dev/null 2>&1; then
  echo given-command is available
else
  echo given-command is not available
fi

ที่พร้อมต์ของเชลล์แบบบอร์นเชิงโต้ตอบ:

type ls

whichคำสั่งเป็นมรดกทางวัฒนธรรมที่แตกสลายจาก C-เชลล์และจะดีกว่าปล่อยให้อยู่ตามลำพังในเปลือกหอยบอร์นเหมือน

ใช้เคส

มีความแตกต่างระหว่างการค้นหาข้อมูลที่เป็นส่วนหนึ่งของสคริปต์หรือโต้ตอบที่ shell prompt

ที่เชลล์พรอมต์กรณีการใช้งานทั่วไปคือ: คำสั่งนี้ทำงานผิดปกติฉันใช้อันที่ถูกต้องหรือไม่ ว่าสิ่งที่เกิดขึ้นเมื่อฉันพิมพ์mycmd? ฉันสามารถดูเพิ่มเติมว่ามันคืออะไร?

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

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

คุณอาจต้องการทราบเกี่ยวกับคำสั่งทั้งหมดที่my-cmdมีอยู่ในระบบในสคริปต์แบบโต้ตอบ

เครื่องมือส่วนใหญ่ที่มีอยู่ (ตามปกติ) ได้รับการออกแบบให้ใช้งานแบบโต้ตอบ

ประวัติศาสตร์

ประวัตินิดหน่อยก่อน

กระสุน Unix ในช่วงต้นจนถึงปลายยุค 70 ไม่มีหน้าที่หรือนามแฝง เฉพาะแบบดั้งเดิมมองขึ้นไปของ executables $PATHใน cshนามแฝงแนะนำรอบ 1978 (แม้ว่าจะcshเป็นครั้งแรกที่ปล่อยออกมาใน2BSDเดือนพฤษภาคม 1979) และยังประมวลผลของการที่.cshrcให้ผู้ใช้สามารถปรับแต่งเปลือก (ทุกเปลือกเช่นcshอ่าน.cshrcแม้ไม่ได้โต้ตอบเช่นในสคริปต์)

ในขณะที่เปลือกบอร์นได้รับการปล่อยตัวครั้งแรกใน Unix V7 ก่อนหน้านี้ในปี 1979 การสนับสนุนฟังก์ชั่นเป็นเพียงการเพิ่มมากภายหลัง (1984 ใน SVR2) และแล้วก็ไม่เคยมีบางrcไฟล์ (คน.profileคือการกำหนดค่าสภาพแวดล้อมของคุณไม่ได้เป็นเปลือกต่อ se )

csh ได้รับความนิยมมากกว่าบอร์นเชลล์มาก (แม้ว่ามันจะมีซินแทกซแย่กว่าบอร์นเชลล์มาก) มันเพิ่มฟีเจอร์ที่สะดวกและดีกว่าสำหรับการใช้แบบอินเตอร์แอคทีฟ

ใน3BSD(1980) whichสคริปต์ cshถูกเพิ่มสำหรับcshผู้ใช้เพื่อช่วยระบุการปฏิบัติการและมันเป็นสคริปต์ที่แตกต่างกันแทบจะไม่คุณสามารถหาได้whichใน Unices เชิงพาณิชย์จำนวนมากในปัจจุบัน (เช่น Solaris, HP / UX, AIX หรือ Tru64)

สคริปต์นั้นจะอ่านผู้ใช้~/.cshrc(เช่นเดียวกับcshสคริปต์ทั้งหมดยกเว้นที่เรียกใช้ด้วยcsh -f) และค้นหาชื่อคำสั่งที่ระบุในรายการชื่อแทนและใน$path(อาร์เรย์ที่cshดูแลตาม$PATH)

ที่นี่คุณไปwhichมาก่อนสำหรับเปลือกที่นิยมมากที่สุดในเวลา (และcshยังคงเป็นที่นิยมจนถึงช่วงกลาง 90s) ซึ่งเป็นเหตุผลหลักว่าทำไมมันได้รับการบันทึกไว้ในหนังสือและยังคงใช้กันอย่างแพร่หลาย

โปรดทราบว่าแม้สำหรับcshผู้ใช้whichสคริปต์ csh นั้นไม่จำเป็นต้องให้ข้อมูลที่ถูกต้องแก่คุณ จะได้รับนามแฝงที่กำหนดไว้ใน~/.cshrcไม่คนที่คุณอาจได้กำหนดไว้ต่อมาที่พรอมต์หรือตัวอย่างเช่นโดยsourceไอเอ็นจีอีกcshไฟล์และ ( แต่ไม่คิดว่าจะเป็นความคิดที่ดี) อาจจะมีการนิยามใหม่ในPATH~/.cshrc

การรันwhichคำสั่งนั้นจากเชลล์เป้าหมายจะยังคงใช้ชื่อแทนการค้นหาที่กำหนดไว้ในของคุณ~/.cshrcแต่ถ้าไม่มีเพราะคุณไม่ได้ใช้cshนั่นอาจจะเป็นคำตอบที่ถูกต้อง

ไม่ได้เพิ่มฟังก์ชันการทำงานที่คล้ายกันลงในเชลล์เป้าหมายจนกระทั่ง 1984 ใน SVR2 ด้วยtypeคำสั่ง builtin ความจริงที่ว่ามันถูกสร้างขึ้นภายใน (ตรงข้ามกับสคริปต์ภายนอก) หมายความว่าสามารถให้ข้อมูลที่ถูกต้อง (ในระดับหนึ่ง) เนื่องจากมีการเข้าถึง internals ของเชลล์

typeคำสั่งเริ่มต้นได้รับความเดือดร้อนจากปัญหาที่คล้ายกันเนื่องจากwhichสคริปต์ไม่ได้ส่งคืนสถานะการออกจากความล้มเหลวหากไม่พบคำสั่ง นอกจากนี้สำหรับเอ็กซีคิวต์ซึ่งตรงกันข้ามกับwhichมันจะส่งออกบางอย่างเช่นls is /bin/lsแทนที่จะ/bin/lsทำให้มันใช้งานง่ายในสคริปต์

ยูนิกซ์เวอร์ชัน 8 (ไม่ปล่อยในป่า) บอร์นเปลือกมีก็เปลี่ยนไปในตัวtype whatisและแผน 9 (ครั้งเพื่อจะสืบต่อจาก Unix) เชลล์rc(และอนุพันธ์ของมันเช่นakangaและes) มีwhatisเช่นกัน

Korn เชลล์ (ชุดย่อยที่คำนิยาม sh POSIX ตั้งอยู่บนพื้นฐาน) พัฒนาขึ้นในช่วงกลางยุค 80 แต่ไม่สามารถใช้ได้อย่างแพร่หลายก่อนปี 1988 เพิ่มคุณสมบัติหลายcshอย่าง (ตัวแก้ไขบรรทัดนามแฝง ... ) ที่ด้านบนของบอร์นเชลล์ . มันเพิ่มwhencebuiltin ของตัวเอง(นอกเหนือจากtype) ซึ่งมีหลายตัวเลือก ( -vเพื่อให้มีtypeเอาต์พุต verbose เหมือนและ-pเพื่อค้นหาเฉพาะไฟล์ที่เรียกทำงานได้ (ไม่ใช่ aliases / function ... ))

บังเอิญกับความวุ่นวายเกี่ยวกับปัญหาลิขสิทธิ์ระหว่าง AT&T และ Berkeley มีการใช้งานเชลล์ซอฟต์แวร์ฟรีเพียงไม่กี่รายการในช่วงปลายยุค 80 ต้น 90 ต้น ๆ Almquist shell ทั้งหมด (ash, เพื่อแทนที่เชลล์เป้าหมายใน BSDs), การใช้งานโดเมนสาธารณะของ ksh (pdksh), bash(สนับสนุนโดย FSF) zshออกมาในระหว่างปี 1989 และ 1991

แอช แต่หมายถึงการเป็นแทนเปลือกบอร์นไม่ได้มีtypeในตัวจนมากภายหลัง (ใน NetBSD 1.3 และ FreeBSD 2.3) hash -vแม้ว่ามันจะมี OSF / 1 /bin/shมีtypebuiltin ซึ่งส่งคืน 0 ถึง OSF / 1 v3.x เสมอ bashไม่ได้เพิ่มwhenceแต่เพิ่ม-pตัวเลือกในการtypeพิมพ์เส้นทาง ( type -pจะเหมือนwhence -p) และ-aจะรายงานทุกคำสั่งที่ตรงกัน tcshทำwhichbuiltin และเพิ่มwhereคำสั่งทำตัวเหมือน'sbash มีพวกเขาทั้งหมดtype -azsh

fishเปลือก (2005) มีtypeคำสั่งดำเนินการตามฟังก์ชั่น

whichสคริปต์ csh ขณะที่ถูกลบออกจาก NetBSD (ขณะที่มันกำลัง builtin ใน tcsh และการใช้งานไม่มากในเปลือกหอยอื่น ๆ ) และฟังก์ชั่นที่เพิ่มให้กับwhereis(เมื่อเรียกว่าเป็นwhich, whereisพฤติกรรมเช่นwhichยกเว้นว่ามันเพียงเงยหน้าขึ้นมอง executables ใน$PATH) ใน OpenBSD และ FreeBSD whichก็เปลี่ยนเป็นหนึ่งเขียนใน C ที่ค้นหาคำสั่งใน$PATHเท่านั้น

การใช้งาน

การใช้งานwhichคำสั่งบน Unices ต่าง ๆ ที่มีรูปแบบและพฤติกรรมที่แตกต่างกัน

บน Linux (ข้างๆ builtin tcshและzsh) เราพบการใช้งานหลายอย่าง ในระบบ Debian ล่าสุดเช่นมันเป็นสคริปต์เชลล์ POSIX $PATHง่ายที่จะมองหาคำสั่งใน

busyboxยังมีwhichคำสั่ง

มีสิ่งGNU whichที่น่าจะเป็นฟุ่มเฟือยที่สุด มันพยายามที่จะขยายสิ่งwhichสคริปต์ csh ทำกับเปลือกหอยอื่น ๆ : คุณสามารถบอกว่าสิ่งที่นามแฝงและฟังก์ชั่นของคุณเพื่อที่จะสามารถให้คำตอบที่ดีกว่า (และผมเชื่อว่าบางลินุกซ์ตั้งชื่อแทนทั่วโลกบางรอบที่bashจะทำอย่างนั้น) .

zshมีโอเปอเรเตอร์สองตัวที่จะขยายไปยังพา ธ ของไฟล์ประมวลผล: โอเปอเรเตอร์การ= ขยายชื่อไฟล์และโมดิ:cฟายเออร์การขยายประวัติ (ใช้กับการขยายพารามิเตอร์ ):

$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls

zshในzsh/parametersโมดูลยังทำให้ตารางแฮชคำสั่งเป็นcommandsอาร์เรย์เชื่อมโยง:

$ print -r -- $commands[ls]
/bin/ls

whatisยูทิลิตี้ (ยกเว้นหนึ่งในระบบปฏิบัติการยูนิกซ์ V8 บอร์นเปลือกหรือแผน 9 rc/ es) ไม่ได้เกี่ยวข้องจริงๆมันเป็นเอกสารเท่านั้น (greps ฐานข้อมูล whatis ที่เป็นบทสรุปหน้าคน)

whereisถูกเพิ่มเข้ามาใน3BSDเวลาเดียวกันราวกับwhichว่ามันถูกเขียนเข้าไปCไม่ใช่cshและถูกใช้เพื่อค้นหาในเวลาเดียวกันไฟล์ที่เรียกใช้งานได้ man page และ source แต่ไม่ได้ขึ้นอยู่กับสภาพแวดล้อมปัจจุบัน ดังนั้นอีกครั้งที่ตอบสนองความต้องการที่แตกต่าง

ตอนนี้ที่ด้านหน้ามาตรฐาน POSIX จะระบุcommand -vและ-Vคำสั่ง (ซึ่งเคยเป็นทางเลือกจนถึง POSIX.2008) UNIX ระบุtypeคำสั่ง (ไม่มีตัวเลือก) นั่นคือทั้งหมดที่ ( where, which, whenceไม่ได้ระบุไว้ในมาตรฐานใด ๆ )

ถึงบางรุ่นtypeและcommand -vเป็นทางเลือกในข้อกำหนดมาตรฐานฐาน Linux ซึ่งอธิบายว่าทำไมเช่นบางรุ่นเก่าของposh(แต่ขึ้นอยู่กับpdkshว่ามีทั้งสอง) ไม่ได้มีทั้ง command -vถูกเพิ่มไปยังการใช้งานเชลล์เป้าหมายบางส่วน (เช่นใน Solaris)

สถานะวันนี้

สถานะปัจจุบันคือtypeและcommand -vเป็นที่แพร่หลายในทุกเปลือกหอยบอร์นเหมือน ( แต่เท่าที่สังเกต @jarno ทราบข้อแม้ / ข้อผิดพลาดในbashเมื่อไม่ได้อยู่ในโหมด POSIX หรือลูกหลานของ Almquist บางเปลือกด้านล่างในความเห็น) tcshเป็นเชลล์เดียวที่คุณต้องการใช้which(เนื่องจากไม่มีtypeและมีwhichอยู่ภายใน)

ในเปลือกหอยอื่น ๆ กว่าtcshและzsh, whichอาจบอกคุณเส้นทางของการปฏิบัติการที่กำหนดตราบใดที่ไม่มีชื่อแทนหรือฟังก์ชั่นโดยใช้ชื่อเดียวกันว่าในใด ๆ ของเรา~/.cshrc, ~/.bashrcหรือแฟ้มเริ่มต้นเปลือกใด ๆ และคุณไม่ได้กำหนดไว้ใน$PATH ~/.cshrcหากคุณมีนามแฝงหรือฟังก์ชั่นที่กำหนดไว้สำหรับมันมันอาจจะหรืออาจไม่บอกคุณเกี่ยวกับมันหรือบอกคุณสิ่งที่ผิด

หากคุณต้องการทราบเกี่ยวกับคำสั่งทั้งหมดด้วยชื่อที่กำหนดไม่มีอะไรที่สามารถพกพาได้ คุณต้องการใช้whereในtcshหรือzsh, type -aในbashหรือzsh, whence -aใน ksh93 และหอยอื่น ๆ ที่คุณสามารถใช้typeร่วมกับwhich -aซึ่งอาจทำงาน

ข้อเสนอแนะ

รับชื่อพา ธ ไปที่ปฏิบัติการ

ตอนนี้เพื่อให้ได้ชื่อพา ธ ของไฟล์ที่เรียกใช้งานได้ในสคริปต์มีคำเตือนอยู่เล็กน้อย:

ls=$(command -v ls)

จะเป็นวิธีมาตรฐานในการทำ

มีปัญหาเล็กน้อยว่า:

  • มันเป็นไปไม่ได้ที่จะรู้เส้นทางของการปฏิบัติการโดยไม่ต้องดำเนินการ ทั้งหมดtype, which, command -v... การวิเคราะห์พฤติกรรมการใช้งานทั้งหมดเพื่อหาเส้นทาง พวกมันวนไปตาม$PATHส่วนประกอบต่างๆและค้นหาไฟล์ที่ไม่ใช่ไดเรกทอรีไฟล์แรกที่คุณได้รับอนุญาตให้ใช้งาน อย่างไรก็ตามขึ้นอยู่กับเชลล์เมื่อพูดถึงการเรียกใช้งานคำสั่งหลายคำสั่ง (Bourne, AT&T ksh, zsh, ash ... ) จะดำเนินการตามลำดับ$PATHจนกว่าการexecveเรียกของระบบจะไม่กลับมาพร้อมกับข้อผิดพลาด . ยกตัวอย่างเช่นถ้า$PATHมี/foo:/barและคุณต้องการที่จะดำเนินการlsพวกเขาครั้งแรกที่จะพยายามที่จะดำเนินการหรือถ้าที่ล้มเหลว/foo/ls /bar/lsตอนนี้การดำเนินการของ/foo/lsอาจล้มเหลวเนื่องจากคุณไม่ได้รับอนุญาตให้ดำเนินการ แต่ด้วยเหตุผลอื่น ๆ อีกมากมายเช่นมันไม่สามารถใช้งานได้ command -v lsจะรายงาน/foo/lsหากคุณมีสิทธิ์ดำเนินการ/foo/lsแต่การเรียกใช้lsอาจทำงานได้จริง/bar/lsหาก/foo/lsไม่ใช่ไฟล์ปฏิบัติการที่ถูกต้อง
  • ถ้าfooเป็น builtin หรือฟังก์ชั่นหรือนามแฝงผลตอบแทนcommand -v foo fooด้วยเปลือกหอยบางชอบash, pdkshหรือzshมันยังอาจจะกลับมาfooถ้า$PATHมีสตริงที่ว่างเปล่าและมีปฏิบัติการfooไฟล์ในไดเรกทอรีปัจจุบัน มีบางสถานการณ์ที่คุณอาจต้องคำนึงถึงเรื่องนี้ โปรดทราบว่ารายการของบิลด์อินจะแตกต่างกันไปตามการใช้งานเชลล์ (ตัวอย่างเช่นmountบางครั้งมีการติดตั้งไว้สำหรับ busybox sh) และอินสแตนซ์bashสามารถรับฟังก์ชั่นจากสภาพแวดล้อมได้
  • หาก$PATHมีส่วนประกอบของเส้นทางสัมพันธ์ (โดยทั่วไป.หรือสตริงว่างซึ่งทั้งสองอ้างถึงไดเรกทอรีปัจจุบัน แต่อาจเป็นอะไรก็ได้) ขึ้นอยู่กับเปลือกcommand -v cmdอาจไม่ส่งออกเส้นทางที่แน่นอน เส้นทางที่คุณได้รับในเวลาที่คุณวิ่งcommand -vจะไม่สามารถใช้ได้อีกต่อไปหลังจากที่คุณcdอยู่ที่อื่น
  • เกร็ดเล็กเกร็ดน้อย: เปลือกหอย ksh93 ถ้า/opt/ast/bin(ว่าที่เส้นทางที่แน่นอนอาจแตกต่างกันในระบบที่แตกต่างกันผมเชื่อว่า) อยู่ในตัวคุณ$PATH, ksh93 จะทำให้ใช้ได้ builtins พิเศษไม่กี่ ( chmod, cmp, cat... ) แต่command -v chmodจะกลับมา/opt/ast/bin/chmodแม้ว่าเส้นทางที่ doesn' ไม่มีตัวตน

การพิจารณาว่าคำสั่งมีอยู่จริงหรือไม่

ในการตรวจสอบว่าคำสั่งที่กำหนดมีอยู่เป็นมาตรฐานหรือไม่คุณสามารถทำได้:

if command -v given-command > /dev/null 2>&1; then
  echo given-command is available
else
  echo given-command is not available
fi

ที่หนึ่งอาจต้องการใช้ which

(t)csh

ในcshและtcshคุณไม่มีทางเลือกมากนัก ในtcshนั้นเป็นสิ่งที่ดีเป็นwhichbuiltin ในcshนั้นจะเป็นwhichคำสั่งระบบซึ่งอาจไม่ทำสิ่งที่คุณต้องการในบางกรณี

ค้นหาคำสั่งในเชลล์บางตัวเท่านั้น

กรณีที่มันอาจจะทำให้ความรู้สึกที่จะใช้whichคือถ้าคุณต้องการที่จะรู้เส้นทางของคำสั่งโดยไม่สนใจ builtins เปลือกที่มีศักยภาพหรือฟังก์ชั่นในbash, csh(ไม่tcsh) dashหรือBourneเปลือกสคริปต์ที่เป็นเปลือกหอยที่ไม่ได้whence -p(เหมือนkshหรือzsh) , command -ev(ชอบyash), whatis -p( rc, akanga) หรือ builtin which(ชอบtcshหรือzsh) บนระบบที่whichมีให้ใช้งานและไม่ใช่cshสคริปต์

หากตรงตามเงื่อนไขเหล่านั้นแล้ว:

echo=$(which echo)

จะให้เส้นทางของคุณเป็นครั้งแรกechoใน$PATH(ยกเว้นในกรณีมุม) โดยไม่คำนึงถึงว่าจะechoเกิดขึ้นเป็นเชลล์ builtin / alias / function หรือไม่

ในเชลล์อื่นคุณต้องการ:

  • zsh : echo==echoหรือecho=$commands[echo]หรือecho=${${:-echo}:c}
  • ksh , zsh :echo=$(whence -p echo)
  • yash :echo=$(command -ev echo)
  • rc , akanga : echo=`whatis -p echo`(ระวังเส้นทางที่มีช่องว่าง)
  • ปลา :set echo (type -fp echo)

โปรดทราบว่าถ้าสิ่งที่คุณต้องการจะทำคือเรียกว่าechoคำสั่งคุณไม่จำเป็นต้องได้รับเส้นทางของมันคุณก็สามารถทำได้:

env echo this is not echoed by the builtin echo

ตัวอย่างเช่น, with tcsh, เพื่อป้องกัน builtin ไม่ให้whichถูกใช้:

set Echo = "`env which echo`"

เมื่อคุณต้องการคำสั่งภายนอก

อีกกรณีที่คุณอาจต้องการใช้whichคือเมื่อคุณต้องการคำสั่งจากภายนอก POSIX ต้องการให้เชลล์บิวด์อินทั้งหมด (เช่นcommand) พร้อมใช้งานเป็นคำสั่งภายนอก แต่น่าเสียดายที่ไม่ใช่ในกรณีของcommandหลายระบบ ตัวอย่างเช่นมันหายากที่จะหาcommandคำสั่งบนระบบปฏิบัติการที่ใช้ Linux ในขณะที่พวกเขาส่วนใหญ่มีwhichคำสั่ง

กรณีที่คุณอาจต้องการคำสั่งภายนอกจะเป็นทุกที่ที่คุณจะเรียกใช้คำสั่งโดยไม่ต้องเรียกใช้ POSIX เชลล์

system("some command line"), popen()... ฟังก์ชั่นของ C หรือภาษาต่างๆไม่เรียกเปลือกจะแยกบรรทัดคำสั่งที่เพื่อsystem("command -v my-cmd")ทำผลงานในพวกเขา ข้อยกเว้นสำหรับสิ่งที่จะperlเพิ่มประสิทธิภาพเชลล์ถ้าไม่เห็นอักขระพิเศษของเชลล์ (นอกเหนือจากช่องว่าง) ซึ่งยังใช้กับผู้ให้บริการ backtick ด้วย:

$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0

$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs

นอกเหนือจากที่กล่าวมา:;ข้างต้นบังคับperlให้เรียกกระสุนที่นั่น โดยการใช้whichคุณจะไม่ต้องใช้เคล็ดลับนั้น


24
@Joe whichเป็นcshสคริปต์เกี่ยวกับ Unices เชิงพาณิชย์จำนวนมาก เหตุผลคือประวัติศาสตร์นั่นคือสาเหตุที่ฉันให้ประวัติดังนั้นผู้คนจึงเข้าใจว่ามันมาจากไหนทำไมผู้คนถึงคุ้นเคยกับการใช้มันและทำไมจริง ๆ แล้วไม่มีเหตุผลที่คุณควรจะใช้มัน และใช่บางคนใช้ (t) csh ไม่ใช่ทุกคนที่ใช้ Linux
Stéphane Chazelas

12
หลังจากอ่านบทความนี้ฉันได้พบบริบทมากมายสำหรับคำตอบ แต่ไม่ใช่คำตอบ ที่จริงแล้วในโพสต์นี้จะพูดว่าทำไมไม่ใช้whichซึ่งแตกต่างจากสิ่งที่คุณอาจพยายามที่จะใช้whichประวัติของwhichการใช้งานของwhichคำสั่งอื่น ๆ ที่จะทำงานที่เกี่ยวข้องหรือเหตุผลที่จะใช้จริงwhich? ทำไมจึงมีคำสั่งอื่น ๆที่ดีกว่า ? พวกเขาทำอะไรที่แตกต่างจากwhich? พวกเขาจะหลีกเลี่ยงหลุมพรางได้อย่างไร whichคำตอบนี้จริงใช้เวลาคำพูดเพิ่มเติมเกี่ยวกับปัญหาที่มีทางเลือกมากกว่าปัญหาที่เกิดขึ้นกับ
user62251

1
ตรงกันข้ามกับสิ่งที่คำตอบอ้างว่าcommand -vไม่ตรวจสอบการอนุญาตการดำเนินการอย่างน้อยถ้าคุณเรียกมันโดยอาร์กิวเมนต์ชื่อไฟล์บริสุทธิ์ที่ไม่มีเส้นทาง ฉันทดสอบโดยขีดกลาง 0.5.8 และ GNU ทุบตี 4.3.48
jarno

2
@ StéphaneChazelasหากฉันสร้างไฟล์ใหม่ด้วยtouch /usr/bin/mytestfileและหลังจากนั้นเรียกใช้command -v mytestfileมันจะให้เส้นทาง (ในขณะที่which mytestfileไม่ได้)
jarno

2
@jarno โอ้ใช่คุณพูดถูก bashจะชำระในไฟล์ไม่สามารถทำงานได้ถ้ามันไม่สามารถหาหนึ่งที่ปฏิบัติการได้ดังนั้นจึงเป็นเรื่อง "OK" (แม้ว่าในทางปฏิบัติอย่างใดอย่างหนึ่งจะค่อนข้างcommand -v/ typeกลับข้อผิดพลาด) เป็นที่คำสั่งก็จะพยายามที่จะดำเนินการเมื่อคุณเรียกใช้mytestfileแต่dashพฤติกรรมเป็นบั๊กราวกับว่ามีสิ่งที่ไม่สามารถcmdดำเนินการได้ก่อนหน้าของสิ่งที่ปฏิบัติการได้command -vส่งคืนสิ่งที่ไม่สามารถเรียกใช้งานได้ในขณะที่การดำเนินการcmdจะทำให้เกิดความสามารถในการเรียกใช้งานได้ FreeBSD sh(ขึ้นอยู่กับash) มีข้อผิดพลาดเหมือนกัน zsh, yash, ksh, mksh, bash ตามที่ sh เป็นปกติ
Stéphane Chazelas

47

เหตุผลที่คนอาจไม่ต้องการใช้whichนั้นได้รับการอธิบายแล้ว แต่นี่เป็นตัวอย่างเล็ก ๆ น้อย ๆ ในบางระบบที่whichล้มเหลวจริง

บนเชลล์เหมือน Bourne เรากำลังเปรียบเทียบเอาต์พุตของwhichกับเอาต์พุตของtype( typeเป็นเชลล์ที่สร้างขึ้นภายในมันหมายถึงความจริงพื้นฐานเพราะเชลล์เป็นเชลล์ที่บอกเราว่ามันจะเรียกใช้คำสั่งอย่างไร)

หลายกรณีมีมุมกรณี แต่จำไว้ว่าwhich/ typeมักจะใช้ในกรณีที่มุม (ที่จะหาคำตอบให้กับพฤติกรรมที่ไม่คาดคิดเช่น: ทำไมในโลกคือคำสั่งที่ทำตัวเหมือนว่าเป็นที่หนึ่งที่ฉันโทรมา? )

ระบบส่วนใหญ่เชลล์แบบบอร์นส่วนใหญ่: ฟังก์ชั่น

กรณีที่ชัดเจนที่สุดสำหรับฟังก์ชั่น:

$ type ls
ls is a function
ls ()
{
[ -t 1 ] && set -- -F "$@";
command ls "$@"
}
$ which ls
/bin/ls

เหตุผลที่whichรายงานเฉพาะเกี่ยวกับไฟล์เรียกทำงานและบางครั้งเกี่ยวกับนามแฝง (แม้ว่าไม่ใช่ทุกเชลล์ของคุณ ) ไม่ใช่ฟังก์ชัน

GNU ที่ man page มีตัวอย่างที่แตก (ขณะที่พวกเขาลืมที่จะพูด$@) เกี่ยวกับวิธีใช้ในการรายงานฟังก์ชั่นเช่นกัน แต่ก็เหมือนกับนามแฝงเพราะมันไม่ได้ใช้ตัวแยกวิเคราะห์ไวยากรณ์ของเชลล์มันหลอกง่าย:

$ which() { (alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@";}
$ f() { echo $'\n}\ng ()\n{ echo bar;\n}\n' >> ~/foo; }
$ type f
f is a function
f ()
{
echo '
}
g ()
{ echo bar;
}
' >> ~/foo
}
$ type g
bash: type: g: not found
$ which f
f ()
{
echo '
}
$ which g
g ()
{ echo bar;
}

ระบบส่วนใหญ่เชลล์แบบบอร์นส่วนใหญ่: builtins

อีกกรณีที่เห็นได้ชัดคือ builtins หรือคำหลักเช่นwhichเป็นคำสั่งภายนอกมีไม่มีทางที่จะทราบว่า builtins เปลือกของคุณมี (และเปลือกหอยบางอย่างเช่นzsh, bashหรือkshสามารถโหลดแบบไดนามิก builtins):

$ type echo . time
echo is a shell builtin
. is a shell builtin
time is a shell keyword
$ which echo . time
/bin/echo
which: no . in (/bin:/usr/bin)
/usr/bin/time

(ที่ไม่ได้ใช้กับzshที่ที่whichมีการสร้าง)

Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5.1 และอื่น ๆ อีกมากมาย:

$ csh
% which ls
ls:   aliased to ls -F
% unalias ls
% which ls
ls:   aliased to ls -F
% ksh
$ which ls
ls:   aliased to ls -F
$ type ls
ls is a tracked alias for /usr/bin/ls

นั่นเป็นเพราะใน Unices เชิงพาณิชย์มากที่สุดwhich(เช่นเดียวกับในการดำเนินงานที่เป็นต้นฉบับใน 3BSD) เป็นสคริปต์ที่อ่านcsh ~/.cshrcนามแฝงที่จะรายงานนั้นเป็นชื่อที่กำหนดไว้โดยไม่คำนึงถึงนามแฝงที่คุณได้กำหนดไว้ในปัจจุบันและไม่ว่าเชลล์ที่คุณใช้งานจริงจะเป็นอย่างไร

ใน HP / UX หรือ Tru64:

% echo 'setenv PATH /bin:/usr/bin' >> ~/.cshrc
% setenv PATH ~/bin:/bin:/usr/bin
% ln -s /bin/ls ~/bin/
% which ls
/bin/ls

(เวอร์ชัน Solaris และ AIX ได้แก้ไขปัญหานั้นโดยการบันทึก$pathก่อนอ่าน~/.cshrcและเรียกคืนก่อนที่จะค้นหาคำสั่ง)

$ type 'a b'
a b is /home/stephane/bin/a b
$ which 'a b'
no a in /usr/sbin /usr/bin
no b in /usr/sbin /usr/bin

หรือ:

$ d="$HOME/my bin"
$ mkdir "$d"; PATH=$PATH:$d
$ ln -s /bin/ls "$d/myls"
$ type myls
myls is /home/stephane/my bin/myls
$ which myls
no myls in /usr/sbin /usr/bin /home/stephane/my bin

(แน่นอนว่าการเป็นcshสคริปต์คุณไม่สามารถคาดหวังได้ว่ามันจะทำงานกับอาร์กิวเมนต์ที่มีช่องว่าง ... )

CentOS 6.4, ทุบตี

$ type which
which is aliased to `alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
$ alias foo=': "|test|"'
$ which foo
alias foo=': "|test|"'
        /usr/bin/test
$ alias $'foo=\nalias bar='
$ unalias bar
-bash: unalias: bar: not found
$ which bar
alias bar='

ในระบบนั้นมีนามแฝงทั้งระบบที่ล้อมรอบwhichคำสั่งGNU

เอาท์พุทปลอมเป็นเพราะwhichอ่านการส่งออกของbash's aliasแต่ไม่ทราบวิธีที่จะแยกได้อย่างถูกต้องและใช้การวิเคราะห์พฤติกรรม (หนึ่งนามแฝงต่อบรรทัดมองหาที่พบคำสั่งแรกหลังจาก|, ;, &... )

สิ่งที่แย่ที่สุดใน CentOS คือว่าzshมีความสมบูรณ์ดีwhichคำสั่ง builtin แต่ CentOS การจัดการที่จะทำลายมันโดยการแทนที่มันด้วยนามแฝงไม่ทำงานเพื่อ whichGNU

เดเบียน 7.0, ksh93:

(แม้ว่าจะใช้กับระบบส่วนใหญ่ที่มีเชลล์จำนวนมาก)

$ unset PATH
$ which which
/usr/local/bin/which
$ type which
which is a tracked alias for /bin/which

บน Debian /bin/whichเป็น/bin/shสคริปต์ ในกรณีของฉันshเป็นแต่ก็เหมือนกันเมื่อมันdashbash

ล้างPATHไม่ได้ที่จะปิดการใช้งานPATHการค้นหา แต่หมายถึงการใช้ระบบเส้นทางเริ่มต้นที่โชคร้ายใน Debian ไม่มีใครตกลงบน ( dashและbashมี/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zshมี/bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93มี/bin:/usr/bin, mkshมี/usr/bin:/bin( $(getconf PATH)) execvp()(เหมือนในenv) มี:/bin:/usr/bin(ใช่ดูในไดเรกทอรีปัจจุบันแรก! ))

นี่คือสาเหตุที่whichทำให้เกิดความผิดพลาดด้านบนเนื่องจากใช้dashค่าเริ่มต้นPATHซึ่งแตกต่างจากksh93ของ

มันไม่ได้ดีกว่ากับ GNU whichที่รายงาน:

which: no which in ((null))

(ที่น่าสนใจมีแน่นอน/usr/local/bin/whichในระบบของฉันซึ่งเป็นจริงakangaสคริปต์ที่มาพร้อมกับakanga(เป็นrcอนุพันธ์เปลือกที่เริ่มต้นPATHเป็น/usr/ucb:/usr/bin:/bin:.))

ทุบตีระบบใด ๆ :

คริสคนหนึ่งพูดถึงในคำตอบของเขา :

$ PATH=$HOME/bin:/bin
$ ls /dev/null
/dev/null
$ cp /bin/ls bin
$ type ls
ls is hashed (/bin/ls)
$ command -v ls
/bin/ls
$ which ls
/home/chazelas/bin/ls

และหลังจากโทรhashด้วยตนเอง:

$ type -a which
which is /usr/local/bin/which
which is /usr/bin/which
which is /bin/which
$ hash -p /bin/which which
$ which which
/usr/local/bin/which
$ type which
which is hashed (/bin/which)

ตอนนี้เป็นกรณีที่whichและบางครั้งtypeล้มเหลว:

$ mkdir a b
$ echo '#!/bin/echo' > a/foo
$ echo '#!/' > b/foo
$ chmod +x a/foo b/foo
$ PATH=b:a:$PATH
$ which foo
b/foo
$ type foo
foo is b/foo

ตอนนี้ด้วยกระสุนบางส่วน:

$ foo
bash: ./b/foo: /: bad interpreter: Permission denied

กับคนอื่น ๆ :

$ foo
a/foo

ทั้งwhichมิได้typeสามารถรู้ล่วงหน้าว่าb/fooไม่สามารถดำเนินการ เปลือกหอยบางคนชอบbash, kshหรือyashเมื่อกล่าวอ้างfooก็จะพยายามที่จะเรียกb/fooและรายงานข้อผิดพลาดขณะที่คนอื่น (เช่นzsh, ash, csh, Bourne, tcsh) จะทำงานa/fooบนความล้มเหลวของระบบโทรบนexecve()b/foo


mkshใช้จริงบางสิ่งบางอย่างที่แตกต่างกันสำหรับการเริ่มต้น$PATH: แรกคงที่เวลารวบรวมระบบปฏิบัติการ_PATH_DEFPATHจะใช้ (ปกติมากที่สุดใน BSDs) แล้วconfstr(_CS_PATH, …)ถูกนำมาใช้ (POSIX) และถ้าทั้งสองไม่ได้มีอยู่หรือล้มเหลว/bin:/usr/bin:/sbin:/usr/sbinถูกนำมาใช้
mirabilos

1
ในตัวอย่างที่ 1 ของคุณแม้ว่าlsจะเป็นฟังก์ชันที่ใช้lsจาก PATH และwhichเป็นเรื่องปกติที่จะบอกคุณที่หนึ่งที่ถูกนำมาใช้หรือ/usr/bin/ls /usr/local/bin/lsฉันไม่เห็น "ทำไมไม่ใช้อัน
ไหน

@rudimeier ว่าwhich lsจะให้ฉัน/bin/lsโดยไม่คำนึงถึงว่าlsฟังก์ชั่นการโทร/bin/lsหรือ/opt/gnu/bin/lsหรือdirหรืออะไรเลย IOW which(สิ่งที่ทำให้เกิดการใช้งาน IMMV) กำลังให้สิ่งที่ไม่เกี่ยวข้อง
Stéphane Chazelas

1
@ StéphaneChazelas ไม่ไม่ไม่. ฉันรู้แล้วว่าฉันlsเป็นฟังก์ชั่น ฉันรู้ว่าฉันlsฟังก์ชั่นที่โทรจากls PATHตอนนี้whichบอกฉันว่าไฟล์อยู่ที่ไหน คุณเห็นกรณีใช้เพียงครั้งเดียว: "เปลือกของฉันจะทำอะไรกับคำสั่งนี้" สำหรับกรณีการใช้งานนี้whichผิดให้ถูกต้อง แต่มีกรณีการใช้งานอื่น ๆ ที่ (GNU) whichเป็นสิ่งที่ถูกต้อง
rudimeier

@Rudimeter ขึ้นอยู่กับwhichการนำไปใช้ บางคนจะบอกคุณว่ามันเป็นนามแฝง (ถ้าคุณมีนามแฝงที่กำหนดค่าหรือถ้ามี~/.cshrcในบ้านของคุณที่มีนามแฝงดังกล่าว) บางคนจะให้เส้นทาง แต่ผิดภายใต้เงื่อนไขบางอย่าง sh -c 'command -v ls'แม้ว่าจะไม่สมบูรณ์แบบก็ยังมีแนวโน้มที่จะให้คำตอบที่ถูกต้องกับความต้องการที่แตกต่างกัน (และเป็นมาตรฐาน)
Stéphane Chazelas

21

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


6

ในจิตวิญญาณของ UNIX: ทำให้แต่ละโปรแกรมทำสิ่งหนึ่งได้ดี

หากเป้าหมายคือตอบ: ไฟล์ใดที่มีชื่อนี้ ?

โปรแกรมปฏิบัติการที่ให้มาพร้อมกับระบบ Debian นั้นเป็นคำตอบที่ดี ซึ่งมาพร้อมกับ csh รวมนามแฝงซึ่งเป็นแหล่งที่มาของปัญหา สิ่งที่เปลือกบางให้เป็นภายในภายในมีเป้าหมายที่แตกต่างกัน คุณสามารถใช้ไฟล์ที่ปฏิบัติการได้หรือใช้สคริปต์ที่ให้ไว้ในตอนท้ายของคำตอบนี้

หากใช้สคริปต์นี้คำตอบที่ได้คือสะอาดง่ายและมีประโยชน์

เป้าหมายนี้จะตรงกับประโยคแรกของคำถามของคุณ:

เมื่อมองหาเส้นทางไปยังไฟล์ที่ปฏิบัติการได้ ... ...

หากคุณมีระบบที่ไม่มีปฏิบัติการที่เรียกว่า (ระบบ linux ส่วนใหญ่มีหนึ่งระบบ) คุณสามารถสร้างระบบขึ้น~/bin/whichมาก่อนหน้า/bin/ใน PATH เพื่อให้ระบบแทนที่ส่วนบุคคลที่ปฏิบัติการได้เช่นเดียวกับที่ด้านล่างของโพสต์นี้:

ไฟล์ปฏิบัติการนั้นจะแสดงรายการ (ตามค่าเริ่มต้น) ไฟล์ปฏิบัติการทั้งหมดที่พบใน PATH หากจำเป็นต้องใช้ครั้งแรกเท่านั้นตัวเลือก-fนี้จะพร้อมใช้งาน


เมื่อมาถึงจุดนี้เราตกอยู่ในเป้าหมายที่แตกต่าง:

สิ่งที่เชลล์จะดำเนินการ (หลังจากการแยกวิเคราะห์)

นั่นมาจากประโยคที่สองของคุณ:

การตรวจสอบสิ่งที่จะเกิดขึ้นหากคุณป้อนชื่อคำสั่งใน Unix shell

หัวเรื่องที่สองนี้พยายามค้นหาคำตอบที่ดีสำหรับคำถามที่ค่อนข้างตอบยาก เชลล์มีมุมมองที่แตกต่างกันมุมเคสและการตีความที่แตกต่างกัน (อย่างน้อย) เพิ่มไปที่:

มีสิ่งอำนวยความสะดวกต่าง ๆ มากมาย (ซึ่ง, ประเภท, คำสั่ง, ที่ไหน, ที่ไหน, ที่ไหน, อะไร, อะไร, แฮช ฯลฯ )

และแน่นอนว่าความพยายามทั้งหมดนั้นตรงกับเป้าหมายนั้น


หลีกเลี่ยงไหน

เรามักจะได้ยินสิ่งที่ควรหลีกเลี่ยง

ฉันสงสัยว่า: ทำไมจึงควรได้รับการกล่าวว่าหากwhichทำงานได้ดี (อย่างน้อยในเดเบียน)?

ด้วยจิตวิญญาณของ UNIX: ทำให้แต่ละโปรแกรมทำได้ดี

ภายนอกโปรแกรมwhichจะทำสิ่งหนึ่งที่: ค้นหาปฏิบัติการครั้งแรกบนเส้นทางที่มีชื่อเดียวกับชื่อคำสั่ง และทำมันได้ดีพอสมควร

ฉันไม่รู้จักโปรแกรมหรือยูทิลิตี้อื่น ๆ ที่ตอบคำถามนี้ในแบบพื้นฐานมากขึ้น เช่นนี้มีประโยชน์และสามารถใช้เมื่อจำเป็น

ทางเลือกที่ใกล้เคียงที่สุดดูเหมือนจะ: command -pv commandNameแต่จะรายงานเกี่ยวกับบิลด์และนามแฝงด้วย ไม่ใช่คำตอบเดียวกัน

แน่นอนwhichมีข้อ จำกัด ไม่ตอบคำถามทุกข้อไม่มีเครื่องมือใดที่สามารถทำได้ ( ยังไม่ได้ ... ) แต่จะมีประโยชน์เมื่อใช้ตอบคำถามที่ออกแบบมาเพื่อตอบ (ข้างบน) ชอบมากedถูก จำกัด และจากนั้นsedปรากฏ (หรือvi/ vim) หรือชอบawkถูก จำกัด และทำให้ Perl ปรากฏขึ้นและขยาย แต่ed, sedและ / หรือawkมีกรณีการใช้งานเฉพาะที่vimหรือperlมีไม่ได้เครื่องมือที่ดีที่สุด

ทำไม?

อาจเป็นเพราะwhichคำตอบเพียงส่วนหนึ่งของคำถามที่ผู้ใช้เชลล์อาจถาม:

สิ่งที่จะถูกดำเนินการเมื่อฉันพิมพ์ commandName?


ภายนอกซึ่ง

ซึ่งควรจะมีให้ (ในหลาย ๆ ระบบ) ว่าเป็นปฏิบัติการภายนอก
วิธีที่แน่นอนในการเรียกเครื่องมือภายนอกนั้นคือการใช้ env เพื่อออกจากเชลล์และจากนั้นเรียกwhich(ซึ่งใช้ได้กับเชลล์ทั้งหมด):

 $ env which which
 /usr/bin/which

หรือใช้เส้นทางแบบเต็มเพื่อwhich(ซึ่งอาจแตกต่างกันไปในระบบที่แตกต่างกัน):

 /usr/bin/which which 

ทำไมถึงhackจำเป็น? เพราะเปลือกบางส่วน (พิเศษ zsh) ซ่อนwhich:

 $ zsh -c 'which which'
 which: shell built-in command

การเป็นเครื่องมือภายนอก (เช่นenv) อธิบายได้อย่างสมบูรณ์ว่าทำไมมันจะไม่รายงานข้อมูลภายในของเชลล์ เช่นเดียวกับนามแฝงฟังก์ชันบิวอินบิวด์อินพิเศษตัวแปรเชลล์ (ไม่เอ็กซ์พอร์ต) ฯลฯ :

 $ env which ls
 /usr/bin/ls
 $ env which ll       # empty output

เอาต์พุตว่างของll(นามแฝงทั่วไปสำหรับll='ls -l') บ่งชี้ว่าllไม่เกี่ยวข้องกับโปรแกรมปฏิบัติการหรืออย่างน้อยที่สุดว่าไม่มีไฟล์ที่ปฏิบัติการได้ที่ตั้งชื่อllใน PATH การใช้llควรเรียกอย่างอื่นในกรณีนี้นามแฝง:

 $ type ll
 ll is aliased to `ls -l'

type และ command

คำสั่งtypeและcommand -vถูกร้องขอโดย POSIX พวกมันควรถูกคาดหวังให้ทำงานในเชลล์ส่วนใหญ่และพวกมันทำได้ยกเว้นใน csh, tcsh, fish และ rc

ทั้งสองคำสั่งสามารถนำมาใช้เพื่อให้มุมมองอื่นที่จะดำเนินการคำสั่ง

whence, where, whereis, whatis,hash

แล้วมีwhence, where, whereis, whatis, hash, และคนอื่น ๆ คำตอบที่แตกต่างกันสำหรับคำถามที่คล้ายกัน ทั้งหมดทำงานในรูปแบบที่แตกต่างกันในเปลือกที่แตกต่างกัน อาจจะเป็นหลังจากที่พบมากที่สุดwhence typeส่วนอื่น ๆ เป็นคำตอบพิเศษที่ตอบคำถามเดียวกันด้วยวิธีที่ต่างกัน

เราควรใช้อะไรแทน

อาจจะเป็นwhichคนแรกที่รู้ถ้ามีปฏิบัติการโดยใช้ชื่อของCommandNameแล้วtypeและcommandแล้วถ้าCommandNameยังไม่ได้พบเลย: whence, where, whereis, whatis, hashในลำดับที่


เชลล์สคริปต์เพื่อจัดทำwhichไฟล์ปฏิบัติการ

#! /bin/sh
set -ef; oldIFS=$IFS; IFS=:

say()( IFS=" "; printf "%s\n" "$*"; )
say "Simplified version of which."
usage(){ say Usage: "$0" [-f] args; }
if [ "$#" -eq 0 ]; then say Missing argument(s); usage; exit 2; fi

firstmatch=0
while getopts f whichopts; do
    case "$whichopts" in
        f) firstmatch=1 ;;
        ?) usage; exit 3 ;;
    esac
done
[ "$OPTIND" -gt 1 ] && shift `expr "$OPTIND" - 1`

allret=0; [ "$#" -eq 0 ] && allret=1
for program in "$@"; do
    ret=1
    for element in $PATH''; do
        case "$program" in
            */*) element="$program"; loop=0;;
            *)   element="${element:-.}/$program"; loop=1;;
        esac
        if [ -f "$element" ] && [ -x "$element" ]; then
            say "$element"
            ret=0
            if [ "$firstmatch" -eq 1 ] || [ "$loop" -eq 0 ]; then break; fi
        fi
    done
    [ "$ret" -eq 1 ] && allret=1
done

IFS="$oldIFS"
exit "$allret"

0

เรามักจะได้ยินสิ่งที่ควรหลีกเลี่ยง ทำไม? เราควรใช้อะไรแทน

ฉันไม่เคยได้ยินเรื่องนั้น โปรดระบุตัวอย่างที่เฉพาะเจาะจง ฉันจะกังวลเกี่ยวกับการแจกจ่าย linux และแพ็คเกจซอฟต์แวร์ที่ติดตั้งของคุณซึ่งwhichมาจากที่ใด!

SLES 11.4 x86-64

ใน tcsh เวอร์ชั่น 6.18.01:

> which which

which: shell built-in command.

ใน bash เวอร์ชั่น 3.2-147:

> which which

/usr/bin/which

> which -v

GNU which v2.19, Copyright (C) 1999 - 2008 Carlo Wood.
GNU which comes with ABSOLUTELY NO WARRANTY;
This program is free software; your freedom to use, change
and distribute this program is protected by the GPL.

whichเป็นส่วนหนึ่งของ util-linux แพคเกจมาตรฐานที่จัดจำหน่ายโดย Linux Kernel Organization สำหรับใช้เป็นส่วนหนึ่งของระบบปฏิบัติการ Linux นอกจากนี้ยังมีไฟล์อื่น ๆ เหล่านี้

/bin/dmesg
/bin/findmnt
/bin/logger
/bin/lsblk
/bin/more
/bin/mount
/bin/umount
/sbin/adjtimex
/sbin/agetty
/sbin/blkid
/sbin/blockdev
/sbin/cfdisk
/sbin/chcpu
/sbin/ctrlaltdel
/sbin/elvtune
/sbin/fdisk
/sbin/findfs
/sbin/fsck
/sbin/fsck.cramfs
/sbin/fsck.minix
/sbin/fsfreeze
/sbin/fstrim
/sbin/hwclock
/sbin/losetup
/sbin/mkfs
/sbin/mkfs.bfs
/sbin/mkfs.cramfs
/sbin/mkfs.minix
/sbin/mkswap
/sbin/nologin
/sbin/pivot_root
/sbin/raw
/sbin/sfdisk
/sbin/swaplabel
/sbin/swapoff
/sbin/swapon
/sbin/switch_root
/sbin/wipefs
/usr/bin/cal
/usr/bin/chrp-addnote
/usr/bin/chrt
/usr/bin/col
/usr/bin/colcrt
/usr/bin/colrm
/usr/bin/column
/usr/bin/cytune
/usr/bin/ddate
/usr/bin/fallocate
/usr/bin/flock
/usr/bin/getopt
/usr/bin/hexdump
/usr/bin/i386
/usr/bin/ionice
/usr/bin/ipcmk
/usr/bin/ipcrm
/usr/bin/ipcs
/usr/bin/isosize
/usr/bin/line
/usr/bin/linux32
/usr/bin/linux64
/usr/bin/look
/usr/bin/lscpu
/usr/bin/mcookie
/usr/bin/mesg
/usr/bin/mkzimage_cmdline
/usr/bin/namei
/usr/bin/rename
/usr/bin/renice
/usr/bin/rev
/usr/bin/script
/usr/bin/scriptreplay
/usr/bin/setarch
/usr/bin/setsid
/usr/bin/setterm
/usr/bin/tailf
/usr/bin/taskset
/usr/bin/time
/usr/bin/ul
/usr/bin/uname26
/usr/bin/unshare
/usr/bin/uuidgen
/usr/bin/wall
/usr/bin/whereis
/usr/bin/which
/usr/bin/write
/usr/bin/x86_64
/usr/sbin/addpart
/usr/sbin/delpart
/usr/sbin/fdformat
/usr/sbin/flushb
/usr/sbin/freeramdisk
/usr/sbin/klogconsole
/usr/sbin/ldattach
/usr/sbin/partx
/usr/sbin/rcraw
/usr/sbin/readprofile
/usr/sbin/rtcwake
/usr/sbin/setctsid
/usr/sbin/tunelp

ฉันutil-linuxคือรุ่น 2.19 บันทึกประจำรุ่นสามารถพบได้ง่ายย้อนหลังไปถึง v2.13 ลงวันที่ (28 ส.ค. - 2550) ไม่แน่ใจว่าประเด็นหรือเป้าหมายของเรื่องนี้คืออะไรแน่นอนว่ามันไม่ได้รับคำตอบในเรื่องที่ยืดเยื้อ 331 ครั้ง


2
สังเกตว่าคำถามไม่ได้กล่าวถึงสิ่งที่ Unix อ้างถึง Linux เป็นเพียงหนึ่งในไม่กี่คน
Kusalananda

2
ในขณะที่which -vรายการของคุณนั่นคือ GNU ซึ่ง (หนึ่งในฟุ่มเฟือยที่กล่าวถึงในคำตอบอื่น ๆ และไม่ได้เจาะจงเฉพาะกับ Linux) ไม่ใช่ util-linux ซึ่ง AFAIK ไม่ได้รวมwhichยูทิลิตี้ util-linux 2.19 มาจาก 2011, GNU ที่ 2.19 มาจากปี 2008
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.