Kiểm tra số lượng đối số được truyền cho tập lệnh Bash


727

Tôi muốn tập lệnh Bash của tôi in một thông báo lỗi nếu số lượng đối số được yêu cầu không được đáp ứng.

Tôi đã thử đoạn mã sau:

#!/bin/bash
echo Script name: $0
echo $# arguments 
if [$# -ne 1]; 
    then echo "illegal number of parameters"
fi

Vì một số lý do không rõ, tôi đã gặp phải lỗi sau:

test: line 4: [2: command not found

Tôi đang làm gì sai?


54
Bạn không nên đặt tên cho kịch bản của bạn test. Đó là tên của một lệnh Unix tiêu chuẩn, bạn sẽ không muốn làm mờ nó.
Barmar

20
Luôn sử dụng khoảng trắng xung quanh '[' ('[[') hoặc '(' ('((') trong câu lệnh trong bash.
zoska

5
Để thêm vào nhận xét @zoska, bạn cần có khoảng trắng trước [vì nó được triển khai dưới dạng lệnh, hãy thử 'which ['.
Daniel Da Cunha

1
ví dụ tốt hơn được đưa ra trên liên kết dưới đây: stackoverflow.com/questions/4341630/iêu
ramkrishna

3
@Barmar chắc chắn đặt tên cho nó testlà tốt miễn là nó không có trên PATH?
dùng253751

Câu trả lời:


1099

Cũng giống như bất kỳ lệnh đơn giản nào khác, [ ... ]hoặc testyêu cầu khoảng trắng giữa các đối số của nó.

if [ "$#" -ne 1 ]; then
    echo "Illegal number of parameters"
fi

Hoặc là

if test "$#" -ne 1; then
    echo "Illegal number of parameters"
fi

Gợi ý

Khi ở Bash, thích sử dụng [[ ]]thay vì nó không thực hiện phân tách từ và mở rộng tên đường dẫn đến các biến của nó mà trích dẫn có thể không cần thiết trừ khi đó là một phần của biểu thức.

[[ $# -ne 1 ]]

Nó cũng có một số tính năng khác như nhóm điều kiện không trích dẫn, khớp mẫu (khớp mẫu mở rộng với extglob) và khớp regex.

Ví dụ sau kiểm tra xem các đối số có hợp lệ không. Nó cho phép một hoặc hai đối số.

[[ ($# -eq 1 || ($# -eq 2 && $2 == <glob pattern>)) && $1 =~ <regex pattern> ]]

Cho các biểu thức số học thuần túy, sử dụng (( ))đối với một số vẫn có thể tốt hơn, nhưng họ vẫn có thể ở [[ ]]với toán tử số học của nó thích -eq, -ne, -lt, -le, -gt, hoặc -gebằng cách đặt các biểu hiện như một đối số chuỗi duy nhất:

A=1
[[ 'A + 1' -eq 2 ]] && echo true  ## Prints true.

Điều đó sẽ hữu ích nếu bạn cũng cần kết hợp nó với các tính năng khác [[ ]].

Thoát khỏi kịch bản

Nó cũng hợp lý để làm cho đoạn mã thoát khi các tham số không hợp lệ được truyền cho nó. Điều này đã được đề xuất trong các bình luận của e Khangas nhưng ai đó đã chỉnh sửa câu trả lời này để có nó với -1giá trị trả về, vì vậy tôi cũng có thể làm đúng.

-1mặc dù được Bash chấp nhận như là một đối số exitkhông được ghi lại rõ ràng và không được sử dụng như một gợi ý chung. 64cũng là giá trị trang trọng nhất kể từ khi nó được định nghĩa trong sysexits.hvới #define EX_USAGE 64 /* command line usage error */. Hầu hết các công cụ như lscũng trả 2về các đối số không hợp lệ. Tôi cũng đã từng quay trở lại 2trong các kịch bản của mình nhưng gần đây tôi không còn quan tâm nữa, và chỉ đơn giản là sử dụng 1trong tất cả các lỗi. Nhưng chúng ta hãy đặt 2ở đây vì nó phổ biến nhất và có thể không dành riêng cho hệ điều hành.

if [[ $# -ne 1 ]]; then
    echo "Illegal number of parameters"
    exit 2
fi

Người giới thiệu


2
OP: Hãy nhớ rằng đó [chỉ là một lệnh khác, tức là thử which [.
Leo

5
Các lệnh @Leo có thể được xây dựng, và không thể. Trong bash, [là một nội trang, trong khi [[là một từ khóa. Trong một số shell cũ, [thậm chí không được dựng sẵn. Các lệnh như [tự nhiên cùng tồn tại như một lệnh bên ngoài trong hầu hết các hệ thống, nhưng các lệnh bên trong được ưu tiên bởi shell trừ khi bạn bỏ qua với commandhoặc exec. Kiểm tra tài liệu của shell về cách họ đánh giá. Hãy lưu ý về sự khác biệt của chúng và cách chúng có thể cư xử khác nhau trong mọi vỏ.
konsolebox

78

Có thể là một ý tưởng tốt để sử dụng các biểu thức số học nếu bạn đang xử lý các số.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi

Tại sao đó có thể là một ý tưởng tốt, trong trường hợp hiện tại? Xem xét hiệu quả, tính di động và các vấn đề khác, không phải là tốt nhất để sử dụng cú pháp đơn giản và phổ biến nhất, nghĩa là [ ... ], khi công việc này hoạt động tốt và không cần thao tác ưa thích?
Tối đa

Mở rộng số học @Max $(( ))không ưa thích và nên được thực hiện bởi tất cả các vỏ POSIX. Tuy nhiên, (( ))cú pháp (không có $) không phải là một phần của nó. Nếu bạn vì một số lý do hạn chế, thì chắc chắn bạn có thể sử dụng [ ]thay thế, nhưng hãy nhớ rằng sau đó bạn cũng không nên sử dụng [[ ]]. Tôi hy vọng bạn hiểu những cạm bẫy [ ]và lý do tại sao các tính năng này tồn tại. Nhưng đây là câu hỏi của Bash nên chúng tôi sẽ đưa ra câu trả lời cho Bash ( Theo quy tắc ngón tay cái, [[được sử dụng cho chuỗi và tệp. Nếu bạn muốn so sánh các số, hãy sử dụng ArithaturesExpression tựa ).
Aleks-Daniel Jakimenko-A.

40

Trên [] :! =, =, == ... là các toán tử so sánh chuỗi và -eq, -gt ... là các toán tử nhị phân số học .

Tôi sẽ dùng:

if [ "$#" != "1" ]; then

Hoặc là:

if [ $# -eq 1 ]; then

9
==thực sự là một tính năng không có giấy tờ, trong đó xảy ra để làm việc với GNU test. Nó cũng xảy ra để làm việc với FreeBSD test, nhưng có thể không hoạt động trên foo test . Các chỉ so sánh giữa các ý kiến =(chỉ FYI).
Martin Tournoij

1
Nó được ghi lại trên mục bash man: Khi các toán tử == và! = Được sử dụng, chuỗi bên phải của toán tử được coi là một mẫu và khớp với các quy tắc được mô tả bên dưới trong Kết hợp mẫu. Nếu tùy chọn shell nocasematch được bật, trận đấu được thực hiện mà không liên quan đến trường hợp các ký tự chữ cái. Giá trị trả về là 0 nếu chuỗi khớp với (==) hoặc không khớp với (! =) Mẫu và 1 nếu không. Bất kỳ phần nào của mẫu có thể được trích dẫn để buộc nó phải khớp với dạng chuỗi.
jhvaras

2
@jhvaras: Đó chính xác là những gì Carpetsmoker đã nói: nó có thể hoạt động trong một số triển khai (và thực tế, nó hoạt động ở Bash), nhưng nó không tuân thủ POSIX . Ví dụ, nó sẽ thất bại với dash: dash -c '[ 1 == 1 ]'. POSIX chỉ xác định =, và không ==.
gniourf_gniourf

34

Nếu bạn chỉ quan tâm đến việc bảo lãnh nếu thiếu một đối số cụ thể, Thay thế tham số là tuyệt vời:

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Script exits here if command-line parameter absent,
#+ with following error message.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT

không phải là được tải với bashism?
Dwight Spencer

@DwightSpencer Có vấn đề gì không?
konsolebox

@Temak Tôi có thể nếu bạn có câu hỏi cụ thể, nhưng bài viết được liên kết giải thích nó tốt hơn tôi có thể.
Pat

13

Một lớp lót đơn giản có thể được thực hiện bằng cách sử dụng:

[ "$#" -ne 1 ] && ( usage && exit 1 ) || main

Điều này được chia thành:

  1. kiểm tra biến bash cho kích thước của tham số $ # không bằng 1 (số lệnh phụ của chúng tôi)
  2. nếu đúng thì gọi hàm used () và thoát với trạng thái 1
  3. khác gọi hàm main ()

Suy nghĩ cần lưu ý:

  • cách sử dụng () có thể chỉ là tiếng vang đơn giản "$ 0: params"
  • chính có thể là một kịch bản dài

1
Nếu bạn có một tập hợp các dòng khác sau dòng đó, điều đó sẽ sai vì exit 1sẽ chỉ áp dụng cho bối cảnh của lớp con làm cho nó chỉ đồng nghĩa với ( usage; false ). Tôi không phải là người thích cách đơn giản hóa khi nói đến phân tích tùy chọn, nhưng bạn có thể sử dụng { usage && exit 1; }thay thế. Hoặc có lẽ chỉ { usage; exit 1; }.
konsolebox

1
@konsolebox (cách sử dụng && exit 1) hoạt động cho ksh, zsh và bash quay trở lại bash 2.0. Cú pháp {...} chỉ gần đây với 4.0+ bash. Đừng hiểu sai ý tôi nếu một cách có hiệu quả với bạn sau đó sử dụng nó nhưng hãy nhớ rằng không phải ai cũng sử dụng cùng một cách thực hiện bash mà bạn làm và chúng ta nên mã hóa theo tiêu chuẩn posix chứ không phải bashism.
Dwight Spencer

Tôi không chắc bạn đang nói gì. {...}là một cú pháp phổ biến và có sẵn cho hầu hết nếu không phải tất cả các shell dựa trên sh, ngay cả những shell cũ hơn không tuân theo các tiêu chuẩn POSIX.
konsolebox

7

Kiểm tra này bash cheatsheet, nó có thể giúp rất nhiều.

Để kiểm tra độ dài của các đối số được truyền vào, bạn sử dụng "$#"

Để sử dụng mảng đối số được truyền vào, bạn sử dụng "$@"

Một ví dụ về kiểm tra độ dài và lặp lại sẽ là:

myFunc() {
  if [[ "$#" -gt 0 ]]; then
    for arg in "$@"; do
      echo $arg
    done
  fi
}

myFunc "$@"

Phát biểu này đã giúp tôi, nhưng đã thiếu một vài điều cho tôi và hoàn cảnh của tôi. Hy vọng điều này sẽ giúp được ai đó.


0

Trong trường hợp bạn muốn ở bên an toàn, tôi khuyên bạn nên sử dụng getopts.

Đây là một ví dụ nhỏ:

    while getopts "x:c" opt; do
      case $opt in
        c)
          echo "-$opt was triggered, deploy to ci account" >&2
          DEPLOY_CI_ACCT="true"
          ;;
            x)
              echo "-$opt was triggered, Parameter: $OPTARG" >&2 
              CMD_TO_EXEC=${OPTARG}
              ;;
            \?)
              echo "Invalid option: -$OPTARG" >&2 
              Usage
              exit 1
              ;;
            :)
              echo "Option -$OPTARG requires an argument." >&2 
              Usage
              exit 1
              ;;
          esac
        done

xem thêm chi tiết tại đây, ví dụ http://wiki.bash-hackers.org/howto/getopts_tutorial


0

Ở đây một lớp lót đơn giản để kiểm tra nếu chỉ có một tham số được đưa ra nếu không thoát khỏi tập lệnh:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit

-1

Bạn nên thêm khoảng trắng giữa điều kiện kiểm tra:

if [ $# -ne 1 ]; 
    then echo "illegal number of parameters"
fi

Tôi hi vọng cái này giúp được.

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.