Đâ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 which
lệ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-cmd
lệ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
. csh
bí danh giới thiệu khoảng năm 1978 (mặc dù csh
lầ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 .cshrc
cho người dùng tùy biến vỏ (mỗi vỏ, như csh
đọc .cshrc
ngay 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ố rc
tệp (đó .profile
là 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 3BSD
năm 1980, một which
tập lệnh csh đã được thêm vào cho csh
ngườ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ư which
trê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 csh
tậ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 csh
duy trì dựa trên $PATH
).
Ở đây bạn đi, which
lần đầu tiên cho vỏ phổ biến nhất vào thời điểm đó (và csh
vẫ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 csh
người dùng, which
tậ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 source
một csh
tệp khác , và (mặc dù đó không phải là một ý tưởng tốt), PATH
có thể được xác định lại ~/.cshrc
.
Chạy which
lệ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 type
lệ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 type
gặp phải một vấn đề tương tự như which
tậ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/ls
thay vì chỉ /bin/ls
là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ư akanga
và es
) whatis
cũ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 csh
tính năng (trình chỉnh sửa dòng, bí danh ...) trên đầu vỏ Bourne . Nó đã thêm phần whence
dự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à -p
chỉ 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), zsh
xuấ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 type
sẵn cho đến sau này (trong NetBSD 1.3 và FreeBSD 2.3), mặc dù nó đã có hash -v
. OSF / 1 /bin/sh
có type
nội dung tích hợp luôn trả về 0 cho đến OSF / 1 v3.x. bash
không thêm whence
nhưng thêm -p
tùy chọn để type
in đường dẫn ( type -p
sẽ như thế whence -p
) và -a
báo cáo tất cả các lệnh khớp. tcsh
làm which
BUILTIN và thêm một where
lệnh hành động như bash
's type -a
. zsh
có tất cả
Các fish
vỏ (2005) có một type
lệnh thực hiện như một hàm.
Các which
kị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
, whereis
hoạt động như which
ngoại trừ việc nó chỉ nhìn lên thực thi trong $PATH
). Trong OpenBSD và FreeBSD, which
cũ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 which
lệ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 tcsh
và 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
.
busybox
cũng có một which
lệnh.
Có một GNU
which
cái có lẽ là xa hoa nhất. Nó cố gắng mở rộng which
tậ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 đó) .
zsh
có 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ộ :c
sử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/parameters
mô-đun cũng làm cho bảng băm lệnh dưới dạng commands
mảng kết hợp:
$ print -r -- $commands[ls]
/bin/ls
Các whatis
tiệ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).
whereis
cũng được thêm vào 3BSD
cùng lúc với which
việc nó được viết C
, không csh
và đượ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 -v
và -V
(được sử dụng là tùy chọn cho đến POSIX.2008). UNIX chỉ định type
lệnh (không có tùy chọn). Đó là tất cả ( where
, which
, whence
không được chỉ định trong tiêu chuẩn có)
Lên đến một số phiên bản type
và command -v
là 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 pdksh
cả hai) đều không có. command -v
cũ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à type
và command -v
có 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 bash
khi 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). tcsh
là vỏ duy nhất mà bạn muốn sử dụng which
(vì không type
có ở đó và which
được dựng sẵn).
Trong vỏ ngoài tcsh
và zsh
, which
có 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
, ~/.bashrc
hoặc bất kỳ tập tin khởi động vỏ và bạn không xác định $PATH
trong 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 where
trong tcsh
hoặc zsh
, type -a
trong bash
hoặc zsh
, whence -a
trong ksh93 và trong các shell khác, bạn có thể sử dụng type
kết hợp với which -a
nhữ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 $PATH
thà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ự $PATH
cho đến khi execve
cuộc gọi hệ thống không trả về lỗi . Chẳng hạn, nếu $PATH
chứa /foo:/bar
và bạn muốn thực thi ls
, trước tiên họ sẽ cố gắng thực thi /foo/ls
hoặc nếu điều đó không thành công /bar/ls
. Bây giờ thực hiện/foo/ls
có 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 ls
sẽ báo cáo /foo/ls
nếu bạn có quyền thực thi /foo/ls
, nhưng chạy ls
thực sự có thể chạy /bar/ls
nếu /foo/ls
không phải là thực thi hợp lệ.
- nếu
foo
là nội dung hoặc hàm hoặc bí danh, command -v foo
trả về foo
. Với một số shell như ash
, pdksh
hoặc zsh
, nó cũng có thể trả về foo
nếu $PATH
bao gồm chuỗi rỗng và có tệp thực thi foo
trong 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ụ bash
có thể nhận các hàm từ môi trường.
- nếu
$PATH
chứ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 cmd
có 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 -v
sẽ 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 chmod
sẽ trở lại /opt/ast/bin/chmod
ngay 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 csh
và 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à which
lệ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 which
là 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
), dash
hoặc Bourne
vỏ kịch bản, đó là vỏ mà không có whence -p
(như ksh
hay zsh
) , command -ev
(như yash
), whatis -p
( rc
, akanga
) hoặc nội dung which
(như tcsh
hoặc zsh
) trên các hệ thống which
có sẵn và không phải là csh
tậ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 echo
trong $PATH
(trừ những trường hợp góc), bất kể echo
cũ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==echo
hoặ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 which
là 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 command
trên nhiều hệ thống. Chẳng hạn, rất hiếm khi tìm thấy một command
lệ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ó which
lệ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à perl
tố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ìwhich
tì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ạiwhich
việ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.
).