Làm cách nào để kiểm tra xem chương trình có tồn tại từ tập lệnh Bash không?


2210

Làm thế nào để tôi xác nhận rằng một chương trình tồn tại, theo cách sẽ trả lại lỗi và thoát, hoặc tiếp tục với tập lệnh?

Có vẻ như nó dễ dàng, nhưng nó đã làm tôi thất vọng.


"Chương trình" là gì? Có bao gồm các chức năng và bí danh? whichtrả về đúng cho những điều này. typekhông có đối số cũng sẽ trả về true cho các từ dành riêng và nội dung shell. Nếu "chương trình" có nghĩa là "có thể giải thích được $PATH", thì hãy xem câu trả lời này .
Tom Hale

Câu trả lời:


3055

Câu trả lời

Tương thích POSIX:

command -v <the_command>

Đối với môi trường cụ thể của Bash:

hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords

Giải trình

Tránh which. Đây không chỉ là một quá trình bên ngoài mà bạn khởi chạy để thực hiện rất ít (có nghĩa là các nội dung như hash, typehoặc commandrẻ hơn), bạn cũng có thể dựa vào các nội dung để thực sự làm những gì bạn muốn, trong khi hiệu ứng của các lệnh bên ngoài có thể dễ dàng thay đổi từ hệ thống đến hệ thống.

Tại sao phải quan tâm?

  • Nhiều hệ điều hành có một whichthậm chí không thiết lập một trạng thái thoát , có nghĩa là if which foothậm chí sẽ không làm việc ở đó và sẽ luôn luôn báo cáo rằng footồn tại, ngay cả khi nó không (lưu ý rằng một số vỏ POSIX xuất hiện để làm việc này cho hashquá).
  • Nhiều hệ điều hành thực whichhiện các công cụ tùy chỉnh và xấu xa như thay đổi đầu ra hoặc thậm chí móc vào trình quản lý gói.

Vì vậy, không sử dụng which. Thay vào đó hãy sử dụng một trong những cách sau:

$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

(Tiểu phụ lưu ý: một số sẽ đề nghị 2>&-là như nhau 2>/dev/nullnhưng ngắn hơn - đây là không đúng sự thật . 2>&-Đóng vai trò quant FD 2 gây ra một lỗi trong chương trình khi nó cố gắng để viết thư cho stderr, mà là rất khác nhau từ văn bản thành công với nó và vứt bỏ đầu ra (và nguy hiểm!))

Nếu băm của bạn là /bin/shthì bạn nên quan tâm đến những gì POSIX nói. typehashmã thoát không được xác định rõ bằng POSIX và hashđược xem là thoát thành công khi lệnh không tồn tại (chưa thấy điều này type). commandTrạng thái thoát của POSIX được xác định rõ, do đó có thể là trạng thái an toàn nhất để sử dụng.

Nếu tập lệnh của bạn sử dụng bashmặc dù, các quy tắc POSIX không thực sự quan trọng nữa và cả hai typehashtrở nên hoàn toàn an toàn để sử dụng. typebây giờ -Pchỉ cần tìm kiếm PATHhashcó tác dụng phụ là vị trí của lệnh sẽ được băm (để tra cứu nhanh hơn vào lần tới khi bạn sử dụng nó), đây thường là một điều tốt vì bạn có thể kiểm tra sự tồn tại của nó để thực sự sử dụng nó .

Ví dụ đơn giản, đây là một hàm chạy gdatenếu nó tồn tại, nếu không date:

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}

35
@Geert: Phần &> / dev / null ẩn thông báo 'loại' phát ra khi 'foo' không tồn tại. > & 2 trên echo đảm bảo gửi thông báo lỗi đến lỗi tiêu chuẩn thay vì đầu ra tiêu chuẩn; bởi vì đó là quy ước. Cả hai đều xuất hiện trên thiết bị đầu cuối của bạn, nhưng lỗi tiêu chuẩn chắc chắn là đầu ra ưa thích cho các thông báo lỗi và cảnh báo không mong muốn.
lhunath

5
cờ -P không hoạt động trong 'sh', ví dụ stackoverflow.com/questions/2608688/iêu
momeara

130
Đối với những người không quen thuộc với chuyển hướng i / o 'nâng cao' trong bash: 1) 2>&- ("mô tả tệp đầu ra đóng 2", đó là stderr) có kết quả tương tự như 2> /dev/null; 2) >&2là một lối tắt cho 1>&2, mà bạn có thể nhận ra là "chuyển hướng stdout sang stderr". Xem trang Chuyển hướng Bash Bashing nâng cao để biết thêm thông tin.
mikewaters

9
@mikewaters ABS trông khá tiên tiến và mô tả một loạt các chức năng CLI bash và không bash, nhưng nó rất sơ suất trong nhiều khía cạnh và không tuân theo thông lệ tốt. Tôi không có đủ không gian trong bình luận này để viết bài; nhưng tôi có thể dán một vài ví dụ ngẫu nhiên mã BAD: while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{cho một trong seq $BEGIN $END}}, ... Nhiều người đã cố gắng liên hệ với tác giả và đề xuất cải tiến nhưng nó không wiki và yêu cầu đã hạ cánh vào tai điếc.
lhunath

56
@mikewaters 2>&-không giống như 2>/dev/null. Cái trước đóng bộ mô tả tập tin, trong khi cái sau chỉ đơn giản là chuyển hướng nó đến /dev/null. Bạn có thể không thấy lỗi vì chương trình cố gắng thông báo cho bạn trên stderr rằng stderr đã bị đóng.
nyuszika7h

577

Sau đây là một cách di động để kiểm tra xem một lệnh có tồn tại $PATH hay không và có thể thực thi được không:

[ -x "$(command -v foo)" ]

Thí dụ:

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi

Kiểm tra thực thi là cần thiết bởi vì bash trả về một tệp không thể thực thi nếu không tìm thấy tệp thực thi có tên đó $PATH.

Cũng lưu ý rằng nếu một tệp không thể thực thi có cùng tên với tệp thực thi tồn tại trước đó $PATH, dấu gạch ngang trả về tệp trước , mặc dù tệp sau sẽ được thực thi. Đây là một lỗi và vi phạm tiêu chuẩn POSIX. [ Báo cáo lỗi ] [ Tiêu chuẩn ]

Ngoài ra, điều này sẽ thất bại nếu lệnh bạn đang tìm kiếm được xác định là bí danh.


4
Sẽ command -vtạo ra một đường dẫn ngay cả đối với một tệp không thể thực thi? Đó là, -x thực sự cần thiết?
einpoklum

5
@einpoklum -xkiểm tra xem tập tin có thể thực thi được không, đó là câu hỏi gì.
Ken Sharp

3
@KenSharp: Nhưng điều đó dường như là dư thừa, vì commandchính nó sẽ kiểm tra khả năng thực thi của nó - phải không?
einpoklum

13
@einpoklum Vâng, nó là cần thiết. Trong thực tế, thậm chí giải pháp này có thể phá vỡ trong một trường hợp cạnh. Cảm ơn vì đã mang đến sự chú ý của tôi. dash, bash và zsh đều bỏ qua các tệp không thể thực thi $PATHkhi thực hiện lệnh. Tuy nhiên, hành vi của command -vrất không nhất quán. Trong dấu gạch ngang, nó trả về tệp phù hợp đầu tiên trong $PATH, bất kể nó có thực thi được hay không. Trong bash, nó trả về kết quả khớp thực thi đầu tiên trong $PATH, nhưng nếu không có, nó có thể trả về một tệp không thể thực thi. Và trong zsh, nó sẽ không bao giờ trả lại một tệp không thể thực thi.
nyuszika7h

5
Theo như tôi có thể nói, dashlà người duy nhất trong số ba người không tuân thủ POSIX; [ -x "$(command -v COMMANDNAME)"]sẽ làm việc trong hai người kia. Trông giống như lỗi này đã được báo cáo nhưng chưa có bất kỳ phản hồi nào: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
nyuszika7h

210

Tôi đồng ý với lhunath để không khuyến khích sử dụng whichvà giải pháp của anh ấy hoàn toàn hợp lệ đối với người dùng Bash . Tuy nhiên, để dễ di chuyển hơn, command -vsẽ được sử dụng thay thế:

$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed.  Aborting." >&2; exit 1; }

Lệnh commandtuân thủ POSIX. Xem ở đây để biết đặc điểm kỹ thuật của nó: lệnh - thực hiện một lệnh đơn giản

Lưu ý: typelà tuân thủ POSIX, nhưng type -Pkhông.


3
Tương tự như trên - exit 1;giết một xterm, nếu được gọi từ đó.
người dùng không xác định

1
Điều này sẽ không hoạt động trên một sh ​​tiêu chuẩn: bạn &> không phải là một hướng dẫn chuyển hướng hợp lệ.
jyavenard

7
@jyavenard: Câu hỏi được gắn thẻ bash , do đó ký hiệu chuyển hướng cụ thể của bash ngắn gọn hơn &>/dev/null. Tuy nhiên, tôi đồng ý với bạn, điều thực sự quan trọng là tính di động, tôi đã chỉnh sửa câu trả lời của mình cho phù hợp, hiện đang sử dụng chuyển hướng sh tiêu chuẩn >/dev/null 2>&1.
GregV

để cải thiện nhiều hơn câu trả lời này, tôi sẽ làm hai việc: 1: sử dụng "&>" để đơn giản hóa nó, như câu trả lời của Josh. 2: chia {} thành một dòng bổ sung, đặt một tab trước tiếng vang, để dễ đọc
knocte

Tôi chỉ đặt một lớp lót này vào một hàm bash nếu bất cứ ai muốn nó ... github.com/equant/my_bash_tools/blob/master/tarp.bash
tương đương

94

Tôi có một hàm được định nghĩa trong .bashrc giúp việc này dễ dàng hơn.

command_exists () {
    type "$1" &> /dev/null ;
}

Đây là một ví dụ về cách nó được sử dụng (từ của tôi .bash_profile.)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi

Làm gì &>?
Saad Malik


&>có thể không có sẵn trong phiên bản Bash của bạn. Mã của Marcello sẽ hoạt động tốt; Nó làm điều tương tự.
Josh Strater

3
Thất bại trên nội dung và các từ dành riêng: thenVí dụ : Hãy thử điều này với từ này . Xem câu trả lời này nếu bạn yêu cầu thực thi tồn tại $PATH.
Tom Hale

84

Nó phụ thuộc vào việc bạn muốn biết liệu nó có tồn tại trong một trong các thư mục trong $PATHbiến hay bạn có biết vị trí tuyệt đối của nó hay không. Nếu bạn muốn biết nó có trong $PATHbiến không, hãy sử dụng

if which programname >/dev/null; then
    echo exists
else
    echo does not exist
fi

nếu không thì sử dụng

if [ -x /path/to/programname ]; then
    echo exists
else
    echo does not exist
fi

Việc chuyển hướng đến /dev/null/trong ví dụ đầu tiên triệt tiêu đầu ra của whichchương trình.


22
Bạn thực sự không nên sử dụng "cái đó" vì những lý do được nêu trong nhận xét của tôi.
lhunath

39

Mở rộng câu trả lời của @ lhunath và @ GregV, đây là mã dành cho những người muốn dễ dàng đưa kiểm tra đó vào trong một iftuyên bố:

exists()
{
  command -v "$1" >/dev/null 2>&1
}

Đây là cách sử dụng nó:

if exists bash; then
  echo 'Bash exists!'
else
  echo 'Your system does not have Bash'
fi

13
Sẵn sàng học hỏi và cải thiện phải được khen thưởng. +1 Điều này sạch sẽ và đơn giản. Điều duy nhất tôi có thể thêm là commandthành công ngay cả đối với các bí danh, có thể hơi phản trực giác. Kiểm tra sự tồn tại trong một vỏ tương tác sẽ cho kết quả khác nhau từ khi bạn chuyển nó sang một tập lệnh.
Palec

1
Tôi chỉ kiểm tra và sử dụng shopt -u expand_aliasesbỏ qua / ẩn các bí danh (như alias ls='ls -F'được đề cập trong câu trả lời khác) và shopt -s expand_aliasesgiải quyết chúng thông qua command -v. Vì vậy, có lẽ nó nên được đặt trước kiểm tra và bỏ đặt sau, mặc dù nó có thể ảnh hưởng đến giá trị trả về của hàm nếu bạn không nắm bắt và trả lại đầu ra của lệnh gọi một cách rõ ràng.
dragon788

24

Hãy thử sử dụng:

test -x filename

hoặc là

[ -x filename ]

Từ trang quản lý Bash dưới Biểu thức có điều kiện :

 -x file
          True if file exists and is executable.

26
Điều đó có nghĩa là bạn cần phải biết đường dẫn đầy đủ đến ứng dụng.
lhunath

12
OP không chỉ định nếu anh ta muốn kiểm tra một trường hợp cụ thể hoặc cho bất kỳ trường hợp thực thi nào ... Tôi đã trả lời nó theo cách tôi đọc nó.
dmckee --- ex-moderator mèo con

16

Để sử dụng hash, như @lhunath gợi ý , trong tập lệnh Bash:

hash foo &> /dev/null
if [ $? -eq 1 ]; then
    echo >&2 "foo not found."
fi

Kịch bản lệnh này chạy hashvà sau đó kiểm tra xem mã thoát của lệnh gần đây nhất, giá trị được lưu trữ trong $?, có bằng không 1. Nếu hashkhông tìm thấy foo, mã thoát sẽ là 1. Nếu foocó, mã thoát sẽ là 0.

&> /dev/nullchuyển hướng lỗi tiêu chuẩnđầu ra tiêu chuẩn từ hashđó để nó không xuất hiện trên màn hình và echo >&2ghi thông báo thành lỗi tiêu chuẩn.


8
Tại sao không chỉ if hash foo &> /dev/null; then ...?
Beni Cherniavsky-Paskin

9

Tôi chưa bao giờ có được câu trả lời trước để làm việc trên hộp tôi có quyền truy cập. Đối với một, typeđã được cài đặt (làm những gì morelàm). Vì vậy, chỉ thị dựng sẵn là cần thiết. Lệnh này hoạt động với tôi:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi

3
Dấu ngoặc không phải là một phần của ifcú pháp, chỉ cần sử dụng if builtin type -p vim; then .... Và backticks là cú pháp thực sự cổ xưa và không dùng nữa, $()được hỗ trợ ngay cả shtrên tất cả các hệ thống hiện đại.
nyuszika7h

9

Kiểm tra nhiều phụ thuộc và thông báo trạng thái cho người dùng cuối

for cmd in latex pandoc; do
  printf '%-10s' "$cmd"
  if hash "$cmd" 2>/dev/null; then
    echo OK
  else
    echo missing
  fi
done

Đầu ra mẫu:

latex     OK
pandoc    missing

Điều chỉnh 10độ dài lệnh tối đa. Nó không tự động, vì tôi không thấy cách POSIX không dài dòng để làm điều đó: Làm cách nào tôi có thể căn chỉnh các cột của một bảng được phân tách không gian trong Bash?

Kiểm tra nếu một số aptgói được cài đặt dpkg -svà cài đặt chúng khác .

Xem: Kiểm tra xem gói apt-get đã được cài đặt chưa và sau đó cài đặt gói đó nếu không có trên Linux

Nó đã được đề cập trước đây tại: Làm cách nào tôi có thể kiểm tra xem chương trình có tồn tại từ tập lệnh Bash không?


1
Cách không dài dòng để làm điều đó: 1) thoát khỏi bộ xác định chiều rộng; 2) thêm một khoảng trắng sau printf tên lệnh của bạn; 3) chuyển vòng lặp for của bạn sang column -t(một phần của linux-linux).
Patrice Levesque

8

Nếu bạn kiểm tra sự tồn tại của chương trình, có lẽ bạn sẽ chạy nó sau. Tại sao không thử chạy nó ở nơi đầu tiên?

if foo --version >/dev/null 2>&1; then
    echo Found
else
    echo Not found
fi

Đó là một kiểm tra đáng tin cậy hơn rằng chương trình chạy hơn là chỉ xem các thư mục PATH và quyền truy cập tệp.

Ngoài ra, bạn có thể nhận được một số kết quả hữu ích từ chương trình của mình, chẳng hạn như phiên bản của nó.

Tất nhiên nhược điểm là một số chương trình có thể nặng để bắt đầu và một số không có --versiontùy chọn để thoát ngay lập tức (và thành công).


6

hash foo 2>/dev/null: hoạt động với vỏ Z (Zsh), Bash, Dashtro .

type -p foo: nó dường như hoạt động với Z shell, Bash và ash ( BusyBox ), nhưng không phải Dash (nó diễn giải -pnhư một đối số).

command -v foo: hoạt động với Z shell, Bash, Dash, nhưng không tro (BusyBox) ( -ash: command: not found).

Cũng lưu ý rằng builtinkhông có sẵn với tro và Dash.


4

Sử dụng nội dung Bash nếu bạn có thể:

which programname

...

type -P programname

15
Huh? whichkhông phải là một nội dung Bash.
tripleee

loại -P tên chương trình sẽ được ưu tiên, xem câu trả lời được chấp nhận
RobertG

@RobertG Tất cả những gì tôi thấy -Plà không phải POSIX. Tại sao được type -Pưa thích?
mikemaccana

Tôi nên nói rằng "được ưu tiên trong môi trường bash" - vì tôi cố gắng trả lời bình luận cụ thể trước bash. Nhưng dù sao, đó là những năm trước - tôi đoán tôi nên chỉ một lần nữa, chỉ cho bạn câu trả lời được đánh dấu là "được tích lũy"
RobertG

4

Lệnh -vhoạt động tốt nếu tùy chọn POSIX_BUILTINS được đặt cho<command> để kiểm tra, nhưng nó có thể thất bại nếu không. (Nó đã làm việc cho tôi trong nhiều năm, nhưng gần đây tôi đã chạy vào một nơi mà nó không hoạt động.)

Tôi thấy những điều sau đây là không thể hơn:

test -x $(which <command>)

Vì nó kiểm tra ba thứ: đường dẫn, sự tồn tại và sự cho phép thực thi.


Không hoạt động. test -x $(which ls)trả về 0, như vậy test -x $(which sudo), mặc dù đã lsđược cài đặt và có thể chạy được và sudothậm chí không được cài đặt trong bộ chứa docker mà tôi đang chạy.
tảo

@ achal Bạn cần sử dụng trích dẫn tôi nghĩ, vì vậytest -x "$(which <command>)"
JoniVR

@ achal Có lẽ lslà bí danh? Tôi không nghĩ rằng nó sẽ hoạt động nếu lệnh có tham số.
AnthonyC

3

Đối với những người quan tâm, không có phương pháp nào trong các câu trả lời trước hoạt động nếu bạn muốn phát hiện một thư viện đã cài đặt. Tôi tưởng tượng bạn còn lại với việc kiểm tra thực tế đường dẫn (có khả năng cho các tệp tiêu đề và tương tự) hoặc đại loại như thế này (nếu bạn đang ở trên bản phân phối dựa trên Debian):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

Như bạn có thể thấy ở trên, câu trả lời "0" từ truy vấn có nghĩa là gói không được cài đặt. Đây là chức năng của "grep" - "0" có nghĩa là trận đấu được tìm thấy, "1" có nghĩa là không tìm thấy kết quả khớp nào.


10
Tuy nhiên, mô hình chống lại cmd; if [ $? -eq 0 ]; thennên được tái cấu trúc thànhif cmd; then
tripleee

Điều này chỉ hoạt động cho các lib được cài đặt qua dpkghoặcapt
Weijun Zhou

3

Có rất nhiều lựa chọn ở đây, nhưng tôi đã ngạc nhiên khi không có một lớp lót nhanh nào. Đây là những gì tôi đã sử dụng ở đầu tập lệnh của mình:

[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

Điều này dựa trên câu trả lời được chọn ở đây và một nguồn khác.


2

Tôi muốn nói rằng không có cách nào di động và đáng tin cậy 100% do lơ lửng aliases. Ví dụ:

alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/

Tất nhiên, chỉ có điều cuối cùng là có vấn đề (không có ý xúc phạm đến Ringo!). Nhưng tất cả chúng đều là aliases hợp lệ theo quan điểm của command -v.

Để từ chối những cái lủng lẳng như thế ringo, chúng ta phải phân tích đầu ra của lệnh tích hợp shell aliasvà tái sử dụng chúng ( command -vkhông phải là ưu việt aliasở đây.) Không có bất kỳ giải pháp di động nào cho nó, và thậm chí là Bash- giải pháp cụ thể là khá tẻ nhạt.

Lưu ý rằng một giải pháp như thế này sẽ từ chối vô điều kiện alias ls='ls -F':

test() { command -v $1 | grep -qv alias }

Điểm tốt. Tuy nhiên, khi chạy từ bên trong tập lệnh bash, các bí danh không hiển thị.
Basil Musa

1
Cũng có một vấn đề, nó sẽ trả về false khi lệnh 'bí danh' được kiểm tra. Khi nào nó nên trở về đúng. Ví dụ: kiểm tra "bí danh"
Basil Musa

2
Tôi chỉ thử nghiệm và sử dụng shopt -u expand_aliasesbỏ qua / ẩn các bí danh này và shopt -s expand_aliaseshiển thị chúng qua command -v.
dragon788

2

Điều này sẽ cho biết theo vị trí nếu chương trình tồn tại hay không:

    if [ -x /usr/bin/yum ]; then
        echo "This is Centos"
    fi

Có, tôi đã thêm lệnh này nếu bạn cần cài đặt gói trong Sevrer, Open suse, centos, Debian
Klevin Kona

Cú pháp tô sáng bị tắt trong dòng "echo". Giải pháp là gì? Nó có gợi ý kịch bản Bash nên khác không?
Peter Mortensen

@PeterMortensen Làm nổi bật cú pháp bị tắt vì nó không nhận ra đó là một chuỗi.
Adrien

1

Các whichlệnh có thể có ích. người đàn ông mà

Nó trả về 0 nếu thực thi được tìm thấy và trả về 1 nếu không tìm thấy hoặc không thực thi:

NAME

       which - locate a command

SYNOPSIS

       which [-a] filename ...

DESCRIPTION

       which returns the pathnames of the files which would
       be executed in the current environment, had its
       arguments been given as commands in a strictly
       POSIX-conformant shell. It does this by searching
       the PATH for executable files matching the names
       of the arguments.

OPTIONS

       -a     print all matching pathnames of each argument

EXIT STATUS

       0      if all specified commands are 
              found and executable

       1      if one or more specified commands is nonexistent
              or not executable

       2      if an invalid option is specified

Điều thú vị whichlà nó chỉ ra nếu thực thi có sẵn trong môi trường whichđược chạy trong - nó sẽ lưu một vài vấn đề ...


Sử dụng nếu bạn đang tìm kiếm bất kỳ thực thi có tên foo, nhưng hãy xem câu trả lời của tôi nếu bạn muốn kiểm tra một tệp / đường dẫn / đến / a / tên / foo cụ thể. Cũng lưu ý rằng có thể không khả dụng trên một số hệ thống tối thiểu, mặc dù nó sẽ có mặt trên bất kỳ cài đặt chính thức nào ...
dmckee --- ex-moderator mèo con

9
Đừng dựa vào trạng thái thoát. Nhiều hệ điều hành có một trạng thái thậm chí không đặt trạng thái thoát khác 0.
lhunath

1

Thiết lập của tôi cho Debian máy chủ :

Tôi gặp vấn đề khi nhiều gói chứa cùng một tên.

Ví dụ apache2. Vì vậy, đây là giải pháp của tôi:

function _apt_install() {
    apt-get install -y $1 > /dev/null
}

function _apt_install_norecommends() {
    apt-get install -y --no-install-recommends $1 > /dev/null
}
function _apt_available() {
    if [ `apt-cache search $1 | grep -o "$1" | uniq | wc -l` = "1" ]; then
        echo "Package is available : $1"
        PACKAGE_INSTALL="1"
    else
        echo "Package $1 is NOT available for install"
        echo  "We can not continue without this package..."
        echo  "Exitting now.."
        exit 0
    fi
}
function _package_install {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install $1
            sleep 0.5
        fi
    fi
}

function _package_install_no_recommends {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install_norecommends $1
            sleep 0.5
        fi
    fi
}

1

Nếu các bạn / cô gái không thể có được những điều trong câu trả lời ở đây để làm việc và đang nhổ tóc ra khỏi lưng, hãy thử chạy cùng một lệnh bằng cách sử dụng bash -c . Chỉ cần nhìn vào mê sảng somnambular này. Đây là những gì thực sự xảy ra khi bạn chạy $ (lệnh phụ):

Đầu tiên. Nó có thể cung cấp cho bạn đầu ra hoàn toàn khác nhau.

$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls

Thứ hai. Nó có thể cung cấp cho bạn không có đầu ra nào cả.

$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found

Sự khác biệt được gây ra bởi sự khác biệt giữa chế độ tương tác và không tương tác của vỏ. ~ / .Bashrc của bạn chỉ được đọc khi trình bao không đăng nhập và tương tác. Cái thứ hai trông có vẻ kỳ quặc, bởi vì điều này phải được gây ra bởi sự khác biệt trong biến môi trường PATH, nhưng các lớp con thừa hưởng môi trường.
Palec

Trong trường hợp của tôi .bashrccó một khoản dự phòng [ -z "$PS1" ] && returntrước # If not running interactively, don't do anythingvì vậy tôi đoán đó là lý do tại sao ngay cả việc tìm nguồn cung ứng bashrc rõ ràng trong chế độ không tương tác cũng không giúp ích được gì. Vấn đề có thể được giải quyết bằng cách gọi một tập lệnh với toán tử dấu chấm ss64.com/bash/source.html. ./script.sh nhưng đó không phải là điều người ta muốn nhớ để nhập mỗi lần.
dùng619271

1
Tìm nguồn cung ứng các kịch bản không được cho là có nguồn gốc là một ý tưởng tồi. Tất cả những gì tôi đã cố gắng nói là câu trả lời của bạn không liên quan gì đến câu hỏi được hỏi và nhiều việc phải làm với Bash và chế độ tương tác (không) của nó.
Palec

Nếu nó giải thích những gì đang xảy ra trong những trường hợp này, nó sẽ là một phụ lục hữu ích cho một câu trả lời.
Palec

0

Biến thể băm có một cạm bẫy: Trên dòng lệnh, ví dụ bạn có thể nhập vào

one_folder/process

để có quá trình thực hiện. Đối với điều này, thư mục mẹ của one_folder phải ở trong $ PATH . Nhưng khi bạn cố gắng băm lệnh này, nó sẽ luôn thành công:

hash one_folder/process; echo $? # will always output '0'

4
"Đối với điều này, thư mục mẹ của one_folder phải nằm trong $PATH" Đây là hoàn toàn không chính xác. Thử nó. Để làm việc này, one_folder phải nằm trong thư mục hiện tại .
tự đại diện

0

Tôi thứ hai sử dụng "lệnh -v". Ví dụ như thế này:

md=$(command -v mkdirhier) ; alias md=${md:=mkdir}  # bash

emacs="$(command -v emacs) -nw" || emacs=nano
alias e=$emacs
[[ -z $(command -v jed) ]] && alias jed=$emacs

0

Tôi đã phải kiểm tra xem Git đã được cài đặt như là một phần của việc triển khai máy chủ CI của chúng tôi chưa . Kịch bản Bash cuối cùng của tôi là như sau (máy chủ Ubuntu):

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi

3
Điều kiện khá vô dụng, hãy điều chỉnh thời gian khởi động để chạy apt-get, vì apt-get sẽ được thỏa mãn và thoát nếu git-core đã được cài đặt.
tripleee

3
Thời gian khởi động của nó là không đáng kể, nhưng động lực quan trọng hơn là sudo: không có điều kiện, nó sẽ luôn dừng lại và yêu cầu mật khẩu (trừ khi bạn đã làm một sudo gần đây). BTW, nó có thể hữu ích để làm sudo -p "Type your password to install missing git-core: "như vậy nhắc nhở không đi ra khỏi màu xanh.
Beni Cherniavsky-Paskin

0

Để bắt chước Bash type -P cmd, chúng ta có thể sử dụng tuân thủ POSIX env -i type cmd 1>/dev/null 2>&1.

man env
# "The option '-i' causes env to completely ignore the environment it inherits."
# In other words, there are no aliases or functions to be looked up by the type command.

ls() { echo 'Hello, world!'; }

ls
type ls
env -i type ls

cmd=ls
cmd=lsx
env -i type $cmd 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }

7
Tại sao điều này được nâng cao? Những hệ thống nào thực sự làm việc cho bạn? typedường như là một builtinphần lớn các shell nên điều này không thể hoạt động được vì envsử dụng execvpđể chạy commandnên commandkhông thể là một builtin(và builtinsẽ luôn được chạy trong cùng một môi trường). Đây không cho tôi ở bash, ksh93, zsh, busybox [a]shdashtất cả trong số đó cung cấp typenhư một BUILTIN vỏ.
Adrian Frühwirth

0

Nếu không có bất kỳ typelệnh bên ngoài nào khả dụng (như được cấp ở đây ), chúng ta có thể sử dụng tuân thủ POSIX env -i sh -c 'type cmd 1>/dev/null 2>&1':

# Portable version of Bash's type -P cmd (without output on stdout)
typep() {
   command -p env -i PATH="$PATH" sh -c '
      export LC_ALL=C LANG=C
      cmd="$1"
      cmd="`type "$cmd" 2>/dev/null || { echo "error: command $cmd not found; exiting ..." 1>&2; exit 1; }`"
      [ $? != 0 ] && exit 1
      case "$cmd" in
        *\ /*) exit 0;;
            *) printf "%s\n" "error: $cmd" 1>&2; exit 1;;
      esac
   ' _ "$1" || exit 1
}

# Get your standard $PATH value
#PATH="$(command -p getconf PATH)"
typep ls
typep builtin
typep ls-temp

Ít nhất trên Mac OS X v10.6.8 (Snow Leopard) sử dụng Bash 4.2.24 (2) command -v lskhông khớp với di chuyển /bin/ls-temp.


0

Trong trường hợp bạn muốn kiểm tra xem một chương trình có tồn tại và thực sự là một chương trình, không phải là lệnh tích hợp Bash , sau đó command, typehash không thích hợp để thử nghiệm như tất cả họ đều trở về 0 trạng thái thoát cho built-in lệnh.

Ví dụ, có chương trình thời gian cung cấp nhiều tính năng hơn lệnh tích hợp thời gian . Để kiểm tra xem chương trình có tồn tại không, tôi sẽ đề nghị sử dụng whichnhư trong ví dụ sau:

# First check if the time program exists
timeProg=`which time`
if [ "$timeProg" = "" ]
then
  echo "The time program does not exist on this system."
  exit 1
fi

# Invoke the time program
$timeProg --quiet -o result.txt -f "%S %U + p" du -sk ~
echo "Total CPU time: `dc -f result.txt` seconds"
rm result.txt

0

Tôi muốn câu hỏi tương tự được trả lời nhưng để chạy trong Makefile.

install:
    @if [[ ! -x "$(shell command -v ghead)" ]]; then \
        echo 'ghead does not exist. Please install it.'; \
        exit -1; \
    fi

-1

Kịch bản

#!/bin/bash

# Commands found in the hash table are checked for existence before being
# executed and non-existence forces a normal PATH search.
shopt -s checkhash

function exists() {
 local mycomm=$1; shift || return 1

 hash $mycomm 2>/dev/null || \
 printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n"; return 1;
}
readonly -f exists

exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'

Kết quả

 [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
Fin.

-1

Tôi sử dụng cái này, vì nó rất dễ:

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi

hoặc là

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi

Nó sử dụng trạng thái echo của shell và chương trình cho đầu ra tiêu chuẩn và không có gì xảy ra lỗi tiêu chuẩn. Mặt khác, nếu không tìm thấy lệnh, nó chỉ báo trạng thái lỗi cho lỗi tiêu chuẩn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.