Tập lệnh shell Linux: Chỉ chạy một chương trình nếu nó tồn tại, bỏ qua nó nếu nó không tồn tại


15

Tôi đang lập trình một tập lệnh shell Linux sẽ in các biểu ngữ trạng thái trong khi thực thi chỉ khi công cụ thích hợp figletđược cài đặt (đây là: có thể truy cập trên đường dẫn hệ thống ).

Thí dụ:

#!/usr/bin/env bash
echo "foo"
figlet "Starting"
echo "moo"
figlet "Working"
echo "foo moo"
figlet "Finished"

Tôi muốn cho kịch bản của tôi để làm việc mà không có lỗi ngay cả khi figletđang không được cài đặt .

Điều gì có thể là một phương pháp thực tế ?



@sudodus: chỉ cần bỏ qua lệnh 'figlet' (và các tham số của nó) sẽ ổn. Tiếp tục thực hiện, tất nhiên.
Sopalajo de Arrierez

2
Tiêu đề cho câu hỏi này đã đưa tôi vào tất cả các loại vấn đề siêu hình
g_uint

2
Bạn có muốn bỏ qua tất cả các lỗi? Chỉ cần sử dụng figlet ... || true.
Giacomo Alzetta

Nếu bạn không quan tâm đến mã thoát, một phím tắt sẽ được sử dụng figlet || true, nhưng trong trường hợp của bạn có lẽ là một hàm shell mà Echos plaintext Nếu không có Banner nào có thể được in thì nhiều khả năng bạn muốn.
eckes

Câu trả lời:


30

Giải thích của tôi sẽ sử dụng một hàm bao bọc có tên giống như công cụ; trong chức năng đó, thực thi công cụ thực nếu nó tồn tại:

figlet() {
  command -v figlet >/dev/null && command figlet "$@"
}

Sau đó, bạn có thể figlet arg1 arg2...không thay đổi trong kịch bản của bạn.

@Olorin đã đưa ra một phương pháp đơn giản hơn: chỉ xác định hàm bao bọc nếu chúng ta cần (nếu công cụ không tồn tại):

if ! command -v figlet > /dev/null; then figlet() { :; }; fi

Nếu bạn muốn in các đối số sẽ figletđược in ngay cả khi figlet chưa được cài đặt, hãy điều chỉnh đề xuất của Olorin như sau:

if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi

1
Nơi nào commandđến từ đâu? Tôi không có nó trong bản cài đặt của mình (Red Hat 6.8 Enterprise và Cygwin64).
eewanco

1
@eewanco, xem unix.stackexchange.com/a/85250/117549 ; Câu chuyện dài, nó được xây dựng để bash, có lẽ là vỏ của bạn.
Jeff Schaller

4
commandlà một vỏ POSIX Bourne. Nó thường thực thi lệnh được cung cấp, nhưng -vcờ làm cho nó hoạt động giống như typemột vỏ khác.
wyrm

@eewanco type -a commandsẽ chỉ cho bạn. whichsẽ chỉ hiển thị các tệp thực thi trên $PATH, không được tích hợp sẵn, từ khóa, hàm hoặc bí danh.
l0b0

@ l0b0: trên RedHat gia đình với bash và cấu hình mặc định (s) which không tìm thấy một bí danh được áp dụng, bởi vì nó là bí danh whichriêng của mình để chạy /usr/bin/which(!) và ống nó một danh sách các bí danh của vỏ để nhìn trong (Tất nhiên \whichngăn chặn các bí danh và chỉ sử dụng chương trình không hiển thị bí danh.)
dave_thndry_085

14

Bạn có thể kiểm tra xem có figlettồn tại không

if type figlet >/dev/null 2>&1
then
    echo Figlet is installed
fi

6

Một cách phổ biến để làm điều này là với test -xaka [ -x. Đây là một ví dụ được lấy từ /etc/init.d/ntptrên một hệ thống Linux:

if [ -x /usr/bin/lockfile-create ]; then
    lockfile-create $LOCKFILE
    lockfile-touch $LOCKFILE &
    LOCKTOUCHPID="$!"
fi

Biến thể này dựa trên việc biết đường dẫn đầy đủ của tệp thực thi. Trong /bin/lesspipetôi tìm thấy một ví dụ hoạt động xung quanh đó bằng cách kết hợp -xwhichlệnh:

if [ -x "`which bunzip`" ]; then bunzip -c "$1"
else echo "No bunzip available"; fi ;;

Bằng cách đó sẽ làm việc này mà không biết trước nơi trong PATHcác bunzipthực thi được.


4
Đừng sử dụngwhich . Và ngay cả khi whichlàm việc, sử dụng test -xtrên đầu ra của nó là ngớ ngẩn: nếu bạn nhận được một đường dẫn từ which, nó tồn tại.
Gilles 'SO- ngừng trở nên xấu xa'

1
@Gilles: Về mặt kỹ thuật, test -xkhông chỉ kiểm tra xem một tập tin có tồn tại hay không (đó là những gì test -edành cho). Nó cũng kiểm tra nếu tập tin có quyền thực thi được thiết lập.
comfreak


5

Khi bắt đầu tập lệnh của bạn, hãy kiểm tra xem có figlettồn tại không và nếu không, hãy xác định hàm shell không có gì:

type figlet >/dev/null 2>&1 || figlet() { :; }

typekiểm tra nếu figlettồn tại dưới dạng shell, hàm, bí danh hoặc từ khóa tích hợp, >/dev/null 2>&1loại bỏ stdin và stdout để bạn không nhận được bất kỳ đầu ra nào và nếu nó không tồn tại, hãy figlet() { :; }định nghĩa figletlà một hàm không có gì.

Bằng cách này, bạn không phải chỉnh sửa mọi dòng tập lệnh sử dụng figlethoặc kiểm tra xem nó có tồn tại mỗi lần figletđược gọi không.

Bạn có thể thêm một thông báo chẩn đoán, nếu bạn muốn:

type figlet >/dev/null 2>&1 || { echo 'figlet not installed.' ; figlet() { :; } ; }

Như một phần thưởng, vì bạn đã không đề cập đến loại vỏ nào bạn đang sử dụng, tôi tin rằng đây là tuân thủ POSIX, vì vậy nó sẽ hoạt động trên hầu hết mọi vỏ.


Tôi thích sự đơn giản của câu trả lời này. Bạn cũng có thể thay thế type figlet >/dev/null 2>&1bằng hash figlet 2>/dev/nullnếu bạn đang sử dụng bash. (OP đã nói "nếu nó ở ĐƯỜNG của tôi".)
Joe

5

Một cách khác - một mẫu tôi đã thấy trong các kịch bản tự động cấu hình dự án:

if [ -x /usr/bin/figlet ]
then
    FIGLET=/usr/bin/figlet
else
    FIGLET=:
fi

$FIGLET "Hello, world!"

Trong trường hợp cụ thể của bạn, bạn thậm chí có thể làm,

if [ -x /usr/bin/figlet ]
then
   SAY=/usr/bin/figlet
elif [ -x /usr/local/bin/figlet ]
then
   SAY=/usr/local/bin/figlet
elif [ -x /usr/bin/banner ]
then
   SAY=/usr/bin/banner
else
   SAY=/usr/bin/echo
fi

$SAY "Hello, world!"

Nếu bạn không biết đường dẫn cụ thể, bạn có thể thử nhiều elif(xem bên trên) để thử các vị trí đã biết hoặc chỉ sử dụng PATHđể luôn giải quyết lệnh:

if command -v figlet >/dev/null
then
    SAY=figlet
elif command -v banner >/dev/null
then
    SAY=banner
else
    SAY=echo
fi

Nói chung, khi viết tập lệnh, tôi thích chỉ gọi các lệnh ở các vị trí cụ thể do tôi chỉ định. Tôi không thích sự không chắc chắn / rủi ro về những gì người dùng cuối có thể đặt vào họ PATH, có lẽ là của chính họ ~/bin.

Ví dụ, nếu tôi đang viết một tập lệnh phức tạp cho những người khác có thể xóa các tệp dựa trên đầu ra của một lệnh cụ thể mà tôi đang gọi, tôi sẽ không muốn vô tình nhặt thứ gì đó trong ~/binđó có thể là hoặc không phải là lệnh Tôi mong đợi.


3
Điều gì nếu figletlà trong /usr/local/binhoặc /home/bob/stuff/programs/executable/figlet?
Gilles 'SO- ngừng trở nên xấu xa'

3
type -p figlet > /dev/null && figlet "foo"

Lệnh bash typetìm thấy một lệnh, hàm, bí danh, từ khóa hoặc nội dung (xem help type) và in ra vị trí hoặc định nghĩa. Nó cũng trả về một mã trả về đại diện cho kết quả tìm kiếm; đúng (0) nếu tìm thấy. Vì vậy, những gì chúng tôi đang làm ở đây là cố gắng tìm figlettrong đường dẫn ( -pcó nghĩa là chỉ tìm tệp, không được tích hợp sẵn hoặc chức năng, đồng thời loại bỏ thông báo lỗi), loại bỏ đầu ra (đó là những gì > /dev/null) và nếu nó trả về đúng ( &&) , nó sẽ thực thi figlet.

Điều này đơn giản hơn nếu figletở một vị trí cố định:

[ -x /usr/bin/figlet ] && /usr/bin/figlet "foo"

Ở đây chúng tôi đang sử dụng testlệnh (aka [) để xem /usr/bin/figletcó thực thi được không ( -x) và nếu vậy ( &&) thực thi nó. Giải pháp này tôi nghĩ là dễ mang theo hơn là sử dụng typemột cách bashism mà tôi tin.

Bạn có thể tạo một chức năng thực hiện điều này cho bạn:

function x() {
    if type -p "$1" >/dev/null; then
        cmd="$1"
        shift
        "$cmd" "$@"
    fi
}

(Các trích dẫn là cần thiết do không gian tiềm năng)

Sau đó, bạn chỉ cần làm:

x figlet "foo"

0

o /, tôi sẽ nói một cái gì đó như

#!/usr/bin/env bash
# if figlet is installed :
if [ "$(which figlet 2>/dev/null)" ]; then
       # do what you wanted to do
       echo "foo"
       figlet "Starting"
       echo "moo"
       figlet "Working"
       echo "foo moo"
       figlet "Finished"
# if not
else
       # exit program with an error
       echo "please install figlet"
       exit 1
fi

0

Bạn có thể thực hiện kiểm tra, loại bỏ bất kỳ đầu ra nào và kiểm tra mã thành công / thất bại. Chọn đối số để figlet để làm cho thử nghiệm này không tốn kém. - -? hoặc --help hoặc --version là những khả năng rõ ràng.

if figlet --help >/dev/null 2>&1 ; then
    # figlet is available
    echo "foo"
    figlet "starting"
    #etc
else
    rc=$?
    echo "figlet is not installed or not working correctly (return code ${rc})"
fi

Đã thêm vào để trả lời nhận xét bên dưới: nếu bạn thực sự muốn kiểm tra rằng figlet tồn tại, không phải là nó có thể sử dụng được, thì bạn sẽ làm

figlet --help >/dev/null 2>&1 
rc=$?
if [ $rc -eq 127 ] ; then # 127 is "command not found" on linux bash 4.4.23(1)
    echo "command figlet not found"
else
    figlet "whatever" # use figlet
fi

Vấn đề ở đây là figlet có thể thất bại vì những lý do ngoài việc không được cài đặt, vì vậy chỉ cần kiểm tra mã thoát là không đủ.
Chris

Nếu bạn thực sự chỉ muốn kiểm tra cài đặt của nó, thì bạn muốn kiểm tra nếu $?bằng 127 (trên hệ thống linux của tôi). 127 là "lệnh không tìm thấy". Nhưng suy nghĩ của tôi là đối với các lệnh hợp lý, nếu command --helpthất bại, thì quá trình cài đặt được thực hiện đủ để nó có thể không có ở đó!.
nigel 222

1
126 là "lệnh được tìm thấy nhưng không thể thực thi được", vì vậy bạn cũng muốn kiểm tra chống lại điều đó. Thật không may, bạn không thể phụ thuộc vào --helpviệc có sẵn. Các tiện ích Posix không có nó, đối với một, và các hướng dẫn posix thực sự khuyên bạn nên chống lại nó . at --helpkhông thành công với at: invalid option -- '-', ví dụ, và một trạng thái thoát của 130trên hệ thống của tôi.
Chris

Ngoài ra, tất nhiên, nếu figlet có thể thoát với trạng thái 127 cũng có thể là một vấn đề.
Chris

Điểm công bằng khoảng 126. Hầu hết các tiện ích lành mạnh đều có một số tùy chọn lệnh vô hại nhẹ, chẳng hạn như -? --help --version... hoặc bạn có thể tìm ra figlet -0 --illegallối thoát nào, và coi đó là chỉ số thành công của bạn (miễn là không phải là 127, mà tôi sẽ phân loại như phá hoại).
nigel 222
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.