Đây là tất cả những gì bạn không bao giờ nghĩ rằng bạn sẽ không bao giờ muốn biết về nó:
Tóm lược
Để có được tên đường dẫn của một tệp thực thi trong tập lệnh shell giống Bourne (có một vài cảnh báo; xem bên dưới):
ls=$(command -v ls)
Để tìm hiểu nếu một lệnh nhất định tồn tại:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Tại dấu nhắc của một vỏ tương tác Bourne:
type ls
Các whichlệnh là một di sản bị hỏng từ C-Shell và còn lại tốt hơn một mình trong vỏ Bourne-như thế nào.
Trường hợp sử dụng
Có một sự khác biệt giữa tìm kiếm thông tin đó như là một phần của tập lệnh hoặc tương tác tại dấu nhắc shell.
Tại dấu nhắc shell, trường hợp sử dụng điển hình là: lệnh này hoạt động kỳ lạ, tôi có đang sử dụng đúng không? Chính xác thì chuyện gì đã xảy ra khi tôi gõ mycmd? Tôi có thể nhìn xa hơn xem nó là gì không?
Trong trường hợp đó, bạn muốn biết shell của bạn làm gì khi bạn gọi lệnh mà không thực sự gọi lệnh.
Trong các kịch bản shell, nó có xu hướng khá khác nhau. Trong tập lệnh shell không có lý do tại sao bạn muốn biết lệnh ở đâu hoặc lệnh là gì nếu tất cả những gì bạn muốn làm là chạy nó. Nói chung, những gì bạn muốn biết là đường dẫn của tệp thực thi, vì vậy bạn có thể lấy thêm thông tin từ nó (như đường dẫn đến tệp khác liên quan đến tệp đó hoặc đọc thông tin từ nội dung của tệp thực thi tại đường dẫn đó).
Tương tác, bạn có thể muốn biết về tất cả các my-cmdlệnh có sẵn trên hệ thống, trong các tập lệnh, hiếm khi như vậy.
Hầu hết các công cụ có sẵn (như thường lệ) đã được thiết kế để được sử dụng tương tác.
Lịch sử
Một chút lịch sử đầu tiên.
Các shell Unix đầu cho đến cuối những năm 70 không có chức năng hoặc bí danh. Chỉ có truyền thống tìm kiếm thực thi trong $PATH. cshbí danh giới thiệu khoảng năm 1978 (mặc dù cshlần đầu tiên được phát hành trong 2BSD, tháng 5 năm 1979), và cũng có thể chế biến một .cshrccho người dùng tùy biến vỏ (mỗi vỏ, như cshđọc .cshrcngay cả khi không tương tác như trong kịch bản).
Trong khi shell Bourne được phát hành lần đầu tiên trong Unix V7 vào đầu năm 1979, hỗ trợ chức năng chỉ được thêm vào sau đó (1984 trong SVR2), và dù sao, nó không bao giờ có một số rctệp (đó .profilelà cấu hình môi trường của bạn, không phải là shell per se ).
csh trở nên phổ biến hơn nhiều so với shell Bourne vì (mặc dù nó có cú pháp cực kỳ tệ hơn so với shell Bourne), nó đã thêm rất nhiều tính năng tiện lợi và tốt hơn để sử dụng tương tác.
Vào 3BSDnăm 1980, một whichtập lệnh csh đã được thêm vào cho cshngười dùng để giúp xác định tập tin thực thi và đó là tập lệnh khó có thể tìm thấy như whichtrên nhiều Unice thương mại hiện nay (như Solaris, HP / UX, AIX hoặc Tru64).
Tập lệnh đó đọc người dùng ~/.cshrc(giống như tất cả các cshtập lệnh thực hiện trừ khi được gọi với csh -f) và tìm (các) tên lệnh được cung cấp trong danh sách các bí danh và trong $path(mảng cshduy trì dựa trên $PATH).
Ở đây bạn đi, whichlần đầu tiên cho vỏ phổ biến nhất vào thời điểm đó (và cshvẫn còn phổ biến cho đến giữa những năm 90), đó là lý do chính tại sao nó được ghi lại trong sách và vẫn được sử dụng rộng rãi.
Lưu ý rằng, ngay cả đối với cshngười dùng, whichtập lệnh csh đó không nhất thiết phải cung cấp cho bạn thông tin chính xác. Nó lấy các bí danh được xác định trong ~/.cshrc, không phải các bí danh mà bạn có thể đã xác định sau đó tại dấu nhắc hoặc ví dụ bằng cách nhập sourcemột cshtệp khác , và (mặc dù đó không phải là một ý tưởng tốt), PATHcó thể được xác định lại ~/.cshrc.
Chạy whichlệnh đó từ trình bao Bourne, vẫn sẽ tra cứu các bí danh được xác định trong bạn ~/.cshrc, nhưng nếu không có vì bạn không sử dụng csh, điều đó vẫn có thể giúp bạn có câu trả lời đúng.
Một chức năng tương tự không được thêm vào trình bao Bourne cho đến năm 1984 trong SVR2 bằng typelệnh dựng sẵn. Việc nó được dựng sẵn (trái ngược với tập lệnh bên ngoài) có nghĩa là nó có thể cung cấp cho bạn thông tin đúng (ở một mức độ nào đó) vì nó có quyền truy cập vào phần bên trong của trình bao.
Lệnh ban đầu typegặp phải một vấn đề tương tự như whichtập lệnh ở chỗ nó không trả về trạng thái thoát thất bại nếu không tìm thấy lệnh. Ngoài ra, đối với các tệp thực thi, ngược lại which, nó tạo ra một cái gì đó giống như ls is /bin/lsthay vì chỉ /bin/lslàm cho nó ít dễ sử dụng hơn trong các tập lệnh.
Phiên bản Unix Bour 8 (không được phát hành trong vỏ) Bourne đã được typeđổi tên thành whatis. Và Plan9 (trình tự kế thừa của Unix) rc(và các dẫn xuất của nó như akangavà es) whatiscũng vậy.
Shell Korn (một tập hợp con mà định nghĩa sh POSIX dựa trên), được phát triển vào giữa những năm 80 nhưng không được phổ biến rộng rãi trước năm 1988, đã thêm nhiều cshtính năng (trình chỉnh sửa dòng, bí danh ...) trên đầu vỏ Bourne . Nó đã thêm phần whencedựng sẵn của riêng mình (ngoài ra type), có một số tùy chọn ( -vđể cung cấp typeđầu ra dài dòng giống như và -pchỉ tìm kiếm các tệp thực thi (không phải bí danh / hàm ...)).
Trùng hợp với sự hỗn loạn liên quan đến các vấn đề bản quyền giữa AT & T và Berkeley, một vài triển khai vỏ phần mềm miễn phí đã xuất hiện vào cuối những năm 80 đầu thập niên 90. Tất cả các vỏ Almquist (tro, để thay thế vỏ Bourne trong BSD), việc triển khai phạm vi công cộng của ksh (pdksh), bash(được tài trợ bởi FSF), zshxuất hiện vào giữa năm 1989 và 1991.
Ash, mặc dù có nghĩa là một sự thay thế cho vỏ Bourne đã không được tích hợp typesẵn cho đến sau này (trong NetBSD 1.3 và FreeBSD 2.3), mặc dù nó đã có hash -v. OSF / 1 /bin/shcó typenội dung tích hợp luôn trả về 0 cho đến OSF / 1 v3.x. bashkhông thêm whencenhưng thêm -ptùy chọn để typein đường dẫn ( type -psẽ như thế whence -p) và -abáo cáo tất cả các lệnh khớp. tcshlàm whichBUILTIN và thêm một wherelệnh hành động như bash's type -a. zshcó tất cả
Các fishvỏ (2005) có một typelệnh thực hiện như một hàm.
Các whichkịch bản csh khi đó đã bị xóa khỏi NetBSD (như nó đã được dựng sẵn trong tcsh và không sử dụng nhiều trong vỏ khác), và các chức năng thêm vào whereis(khi gọi như which, whereishoạt động như whichngoại trừ việc nó chỉ nhìn lên thực thi trong $PATH). Trong OpenBSD và FreeBSD, whichcũng được đổi thành một chữ viết bằng C chỉ tìm kiếm các lệnh $PATH.
Triển khai
Có hàng tá việc thực hiện một whichlệnh trên các Unice khác nhau với cú pháp và hành vi khác nhau.
Trên Linux (bên cạnh các bản dựng sẵn trong tcshvà zsh), chúng tôi tìm thấy một số triển khai. Ví dụ, trên các hệ thống Debian gần đây, đó là tập lệnh shell POSIX đơn giản tìm kiếm các lệnh trong $PATH.
busyboxcũng có một whichlệnh.
Có một GNU whichcái có lẽ là xa hoa nhất. Nó cố gắng mở rộng whichtập lệnh csh đã làm với các shell khác: bạn có thể nói cho nó biết bí danh và chức năng của bạn là gì để nó có thể cho bạn câu trả lời tốt hơn (và tôi tin rằng một số bản phân phối Linux đặt một số bí danh toàn cầu bashđể làm điều đó) .
zshcó một vài toán tử để mở rộng theo đường dẫn của các tệp thực thi: toán tử = mở rộng tên tệp và bộ :csửa đổi mở rộng lịch sử (ở đây áp dụng cho mở rộng tham số ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh, trong zsh/parametersmô-đun cũng làm cho bảng băm lệnh dưới dạng commandsmảng kết hợp:
$ print -r -- $commands[ls]
/bin/ls
Các whatistiện ích (trừ trường hợp một trong Unix V8 Bourne shell hoặc Plan 9 rc/ es) là không thực sự liên quan vì nó là chỉ tài liệu (greps cơ sở dữ liệu whatis, đó là người đàn ông trang tóm tắt).
whereiscũng được thêm vào 3BSDcùng lúc với whichviệc nó được viết C, không cshvà được sử dụng để tra cứu cùng lúc, trang man và nguồn thực thi nhưng không dựa trên môi trường hiện tại. Vì vậy, một lần nữa, câu trả lời một nhu cầu khác nhau.
Bây giờ, ở mặt trước tiêu chuẩn, POSIX chỉ định các lệnh command -vvà -V(được sử dụng là tùy chọn cho đến POSIX.2008). UNIX chỉ định typelệnh (không có tùy chọn). Đó là tất cả ( where, which, whencekhông được chỉ định trong tiêu chuẩn có)
Lên đến một số phiên bản typevà command -vlà tùy chọn trong đặc tả Cơ sở Tiêu chuẩn Linux, điều này giải thích tại sao một số phiên bản cũ của posh(mặc dù dựa trên pdkshcả hai) đều không có. command -vcũng đã được thêm vào một số triển khai shell Bourne (như trên Solaris).
Tình trạng hôm nay
Tình trạng hiện nay là typevà command -vcó mặt ở tất cả các vỏ giống như Bourne (mặc dù, như đã lưu ý bởi @jarno, lưu ý cảnh báo / lỗi bashkhi không ở chế độ POSIX hoặc một số hậu duệ của vỏ Almquist bên dưới trong các bình luận). tcshlà vỏ duy nhất mà bạn muốn sử dụng which(vì không typecó ở đó và whichđược dựng sẵn).
Trong vỏ ngoài tcshvà zsh, whichcó thể cho bạn biết con đường thực thi được miễn là không có bí danh hoặc chức năng bằng cách đó cùng tên trong bất kỳ của chúng tôi ~/.cshrc, ~/.bashrchoặc bất kỳ tập tin khởi động vỏ và bạn không xác định $PATHtrong của bạn ~/.cshrc. Nếu bạn có một bí danh hoặc chức năng được xác định cho nó, nó có thể hoặc không thể cho bạn biết về nó, hoặc cho bạn biết điều sai.
Nếu bạn muốn biết về tất cả các lệnh theo một tên cụ thể, không có gì có thể mang theo được. Bạn sẽ sử dụng wheretrong tcshhoặc zsh, type -atrong bashhoặc zsh, whence -atrong ksh93 và trong các shell khác, bạn có thể sử dụng typekết hợp với which -anhững thứ có thể hoạt động.
khuyến nghị
Lấy tên đường dẫn để thực thi
Bây giờ, để có được tên đường dẫn của một tệp thực thi trong tập lệnh, có một vài lưu ý:
ls=$(command -v ls)
sẽ là cách tiêu chuẩn để làm điều đó.
Có một vài vấn đề:
- Không thể biết đường dẫn của tệp thực thi mà không thực thi nó. Tất cả các
type, which, command -v... tất cả chẩn đoán sử dụng để tìm ra con đường. Họ lặp qua các $PATHthành phần và tìm tệp không phải thư mục đầu tiên mà bạn có quyền thực thi. Tuy nhiên, tùy thuộc vào shell, khi thực hiện lệnh, nhiều người trong số họ (Bourne, AT & T ksh, zsh, ash ...) sẽ thực hiện chúng theo thứ tự $PATHcho đến khi execvecuộc gọi hệ thống không trả về lỗi . Chẳng hạn, nếu $PATHchứa /foo:/barvà bạn muốn thực thi ls, trước tiên họ sẽ cố gắng thực thi /foo/lshoặc nếu điều đó không thành công /bar/ls. Bây giờ thực hiện/foo/lscó thể thất bại vì bạn không có quyền thực thi nhưng cũng vì nhiều lý do khác, như đó không phải là một thực thi hợp lệ. command -v lssẽ báo cáo /foo/lsnếu bạn có quyền thực thi /foo/ls, nhưng chạy lsthực sự có thể chạy /bar/lsnếu /foo/lskhông phải là thực thi hợp lệ.
- nếu
foolà nội dung hoặc hàm hoặc bí danh, command -v footrả về foo. Với một số shell như ash, pdkshhoặc zsh, nó cũng có thể trả về foonếu $PATHbao gồm chuỗi rỗng và có tệp thực thi footrong thư mục hiện tại. Có một số trường hợp mà bạn có thể cần phải tính đến điều đó. Ví dụ, hãy nhớ rằng danh sách các nội trang thay đổi theo cách triển khai shell (ví dụ, mountđôi khi được tích hợp sẵn cho busybox sh) và ví dụ bashcó thể nhận các hàm từ môi trường.
- nếu
$PATHchứa các thành phần đường dẫn tương đối (thông thường .hoặc chuỗi rỗng, cả hai đều tham chiếu đến thư mục hiện tại nhưng có thể là bất cứ thứ gì), tùy thuộc vào trình bao, command -v cmdcó thể không xuất ra một đường dẫn tuyệt đối. Vì vậy, đường dẫn bạn có được tại thời điểm bạn chạy command -vsẽ không còn hiệu lực sau khi bạn cdở một nơi khác.
- Giai thoại: với vỏ ksh93, nếu
/opt/ast/bin(mặc dù con đường chính xác có thể khác nhau trên các hệ thống khác nhau Tôi tin) là ở bạn $PATH, ksh93 sẽ cung cấp một vài builtins thêm ( chmod, cmp, cat...), nhưng command -v chmodsẽ trở lại /opt/ast/bin/chmodngay cả khi con đường đó doesn' t tồn tại
Xác định xem một lệnh có tồn tại không
Để tìm hiểu xem một lệnh đã cho có tồn tại chuẩn hay không, bạn có thể làm:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Nơi mà người ta có thể muốn sử dụng which
(t)csh
Trong cshvà tcsh, bạn không có nhiều sự lựa chọn. Trong tcshđó, đó là tốt như whichđược xây dựng. Trong cshđó, đó sẽ là whichlệnh hệ thống , có thể không làm những gì bạn muốn trong một vài trường hợp.
tìm lệnh chỉ trong một số shell
Một trường hợp nó có thể làm cho tinh thần để sử dụng whichlà nếu bạn muốn biết con đường của một lệnh, bỏ qua builtins vỏ tiềm năng hoặc chức năng trong bash, csh(không tcsh), dashhoặc Bournevỏ kịch bản, đó là vỏ mà không có whence -p(như kshhay zsh) , command -ev(như yash), whatis -p( rc, akanga) hoặc nội dung which(như tcshhoặc zsh) trên các hệ thống whichcó sẵn và không phải là cshtập lệnh.
Nếu những điều kiện đó được đáp ứng, thì:
echo=$(which echo)
sẽ cung cấp cho bạn con đường đầu tiên echotrong $PATH(trừ những trường hợp góc), bất kể echocũng sẽ xảy ra là một vỏ BUILTIN / bí danh / chức năng hay không.
Trong các shell khác, bạn thích:
- zsh :
echo==echohoặc echo=$commands[echo]hoặcecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- yash :
echo=$(command -ev echo)
- RC , akanga :
echo=`whatis -p echo`(coi chừng đường dẫn có dấu cách)
- cá :
set echo (type -fp echo)
Lưu ý rằng nếu tất cả những gì bạn muốn làm là chạyecho lệnh đó , bạn không cần phải có đường dẫn của nó, bạn chỉ có thể làm:
env echo this is not echoed by the builtin echo
Ví dụ, với tcsh, để ngăn chặn nội dung whichđược sử dụng:
set Echo = "`env which echo`"
khi bạn cần một lệnh bên ngoài
Một trường hợp khác mà bạn có thể muốn sử dụng whichlà khi bạn thực sự cần một lệnh bên ngoài. POSIX yêu cầu tất cả các nội dung shell (như command) cũng có sẵn dưới dạng các lệnh bên ngoài, nhưng thật không may, đó không phải là trường hợp commandtrên nhiều hệ thống. Chẳng hạn, rất hiếm khi tìm thấy một commandlệnh trên các hệ điều hành dựa trên Linux trong khi hầu hết chúng đều có whichlệnh (mặc dù các lệnh khác nhau với các tùy chọn và hành vi khác nhau).
Các trường hợp bạn có thể muốn một lệnh bên ngoài sẽ là bất cứ nơi nào bạn thực thi lệnh mà không cần gọi trình bao POSIX.
Các chức năng system("some command line"), popen()... của C hoặc các ngôn ngữ khác nhau gọi một trình bao để phân tích dòng lệnh đó, do đó, hãy system("command -v my-cmd")thực hiện chúng. Một ngoại lệ sẽ là perltối ưu hóa lớp vỏ nếu nó không nhìn thấy bất kỳ ký tự đặc biệt nào của vỏ (trừ không gian). Điều đó cũng áp dụng cho toán tử backtick của nó:
$ 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
Việc bổ sung các :;lực lượng trên perlđể gọi một cái vỏ ở đó. Bằng cách sử dụng which, bạn sẽ không phải sử dụng thủ thuật đó.
whichđều giả định bối cảnh vỏ tương tác. Câu hỏi này được gắn thẻ / tính di động. Vì vậy, tôi giải thích câu hỏi trong ngữ cảnh này là "sử dụng cái gì thay vìwhichtìm tệp thực thi đầu tiên của một tên đã cho trong$PATH". Hầu hết các câu trả lời và lý do chống lạiwhichviệc đối phó với các bí danh, nội dung và hàm, trong hầu hết các tập lệnh shell di động trong thế giới thực chỉ là mối quan tâm học thuật. Các bí danh được xác định cục bộ không được kế thừa khi chạy tập lệnh shell (trừ khi bạn lấy nó với.).