Kiểm tra sự tồn tại của đối số đầu vào trong tập lệnh shell Bash


1339

Tôi cần kiểm tra sự tồn tại của một đối số đầu vào. Tôi có đoạn script sau

if [ "$1" -gt "-1" ]
  then echo hi
fi

tôi có

[: : integer expression expected

Làm cách nào để kiểm tra đối số đầu vào1 trước để xem nó có tồn tại không?

Câu trả lời:


2330

Nó là:

if [ $# -eq 0 ]
  then
    echo "No arguments supplied"
fi

Các $#biến sẽ cho bạn biết số lượng các đối số đầu vào kịch bản đã được thông qua.

Hoặc bạn có thể kiểm tra xem một đối số có phải là một chuỗi rỗng hay không như sau:

if [ -z "$1" ]
  then
    echo "No argument supplied"
fi

Công -ztắc sẽ kiểm tra xem việc mở rộng "$ 1" có phải là chuỗi null hay không. Nếu nó là một chuỗi null thì phần thân được thực thi.


62
Tôi thích làm theo cách này, theo cú pháp ngắn gọn và vẫn POSIX chấp nhận được. [ -z "$1" ] && echo "No argument supplied" Tôi thích một lớp lót, vì chúng dễ dàng hơn cho tôi; và cũng nhanh hơn để kiểm tra giá trị thoát, so với việc sử dụngif
JM Becker

168
Bạn có thể muốn thêm một exit 1phần cuối của tiếng vang bên trong khối if khi đối số được yêu cầu để tập lệnh hoạt động. Rõ ràng, nhưng đáng chú ý cho sự đầy đủ.
msanford

16
Có thể, mặc dù hiếm khi hữu ích, cho đối số đầu tiên được khởi tạo nhưng trống rỗng; programname "" secondarg third. Các $#kiểm tra một cách rõ ràng để kiểm tra số lượng các đối số.
tripleee

39
Đối với một người mới, đặc biệt là một người đến từ một nền tảng không có kịch bản, điều quan trọng là phải đề cập đến một số đặc thù về những điều này. Bạn cũng có thể đã đề cập rằng chúng tôi cần một không gian sau khi mở và kết thúc. Nếu không thì mọi thứ không hoạt động. Tôi là một noob kịch bản (tôi đến từ nền C) và thấy nó thật khó khăn. Chỉ đến khi tôi quyết định sao chép toàn bộ "như hiện tại" thì mọi thứ mới có hiệu quả với tôi. Đó là lúc tôi nhận ra mình phải chừa một khoảng trống sau cú đúp mở và trước khi kết thúc.
HighOnMeat

72
và cho các đối số tùy chọnif [ ! -z "$1" ]; then ...
gcb

347

Tốt hơn là chứng minh theo cách này

if [[ $# -eq 0 ]] ; then
    echo 'some message'
    exit 1
fi

Bạn thường cần phải thoát nếu bạn có quá ít đối số.


72
Không, không phải: đây là exit 1thứ bạn thường muốn và sử dụng [[ ]]thử nghiệm mà (iirc) thường hợp lý hơn. Vì vậy, đối với những người mù dán mã sao chép thì đây là câu trả lời tốt hơn.
dshepherd

42
Để biết thêm về sự khác biệt giữa [] và [[]], hãy xem stackoverflow.com/questions/3427872/
mẹo

104

Trong một số trường hợp, bạn cần kiểm tra xem người dùng có chuyển đối số cho tập lệnh hay không và nếu không, hãy quay lại giá trị mặc định. Giống như trong kịch bản dưới đây:

scale=${2:-1}
emulator @$1 -scale $scale

Ở đây nếu người dùng không vượt qua scalenhư một tham số thứ 2, tôi sẽ khởi chạy trình giả lập Android -scale 1theo mặc định. ${varname:-word}là một toán tử mở rộng. Ngoài ra còn có các toán tử mở rộng khác:

  • ${varname:=word}trong đó thiết lập không xác định varnamethay vì trả về wordgiá trị;
  • ${varname:?message}sẽ trả về varnamenếu nó được xác định và không rỗng hoặc in messagevà hủy bỏ tập lệnh (như ví dụ đầu tiên);
  • ${varname:+word}wordchỉ trả về nếu varnameđược định nghĩa và không null; trả về null nếu không.

1
Ví dụ trên dường như sử dụng ${varname?message}. Là thêm :một lỗi đánh máy, hoặc nó thay đổi hành vi?
Eki

6
Eki, ":" là một lệnh dựng sẵn và tốc ký cho / bin / true trong ví dụ này. Nó đại diện cho một lệnh không làm gì mà về cơ bản bỏ qua các đối số được cung cấp. Đây là điều cần thiết trong thử nghiệm này để giữ cho trình thông dịch không cố thực thi nội dung của "$ varname" (điều mà bạn chắc chắn KHÔNG muốn xảy ra). Cũng đáng chú ý; bạn có thể kiểm tra bao nhiêu biến với phương thức này nếu muốn. Và tất cả với các thông báo lỗi cụ thể. tức là: ${1?"First argument is null"} ${2?"Please provide more than 1 argument"}
user.friendly 16/8/17

48

Thử:

 #!/bin/bash
 if [ "$#" -eq  "0" ]
   then
     echo "No arguments supplied"
 else
     echo "Hello world"
 fi

4
Tại sao bạn cần báo giá kép cho $#0?
dùng13107

1
Không có vấn đề gì nếu chúng tôi sử dụng mà không có dấu ngoặc kép như $ # và 0
Ranjithkumar T

trên cửa sổ, mingw đây là cách duy nhất để đi.
Lajos Meszaros

2
Câu trả lời này cung cấp điểm khởi đầu tuyệt vời cho một kịch bản tôi vừa thực hiện. Cảm ơn đã cho thấy else, quá.
Chris K

2
@ user13107 hai biến được trích dẫn trong bash ngăn chặn toàn cầu hóa (nghĩa là mở rộng tên tệp như foo*) và tách từ (nghĩa là chia nội dung nếu giá trị chứa khoảng trắng). Trong trường hợp này, không cần thiết phải trích dẫn $#vì cả hai trường hợp này đều không áp dụng. Trích dẫn 0cũng không cần thiết, nhưng một số người thích trích dẫn các giá trị vì chúng thực sự là chuỗi và điều đó làm cho nó rõ ràng hơn.
Dennis

39

Một cách khác để phát hiện nếu các đối số được truyền vào tập lệnh:

((!$#)) && echo No arguments supplied!

Lưu ý rằng (( expr ))làm cho biểu thức được đánh giá theo quy tắc của Số học Shell .

Để thoát trong trường hợp không có bất kỳ đối số, người ta có thể nói:

((!$#)) && echo No arguments supplied! && exit 1

Một cách khác (tương tự) để nói ở trên sẽ là:

let $# || echo No arguments supplied

let $# || { echo No arguments supplied; exit 1; }  # Exit if no arguments!

help let nói:

let: let arg [arg ...]

  Evaluate arithmetic expressions.

  ...

  Exit Status:
  If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.

2
-1 đây có thể là phương pháp tồi tệ nhất nếu xác thực sự tồn tại của một đối số .. cộng với nó có thể kích hoạt thay thế lịch sử và có khả năng làm những điều xấu.
user.friendly 16/8/17

2
thay vì exitgiết chết quá trình zsh của tôi, tôi sử dụng returnnó không giết nó
Timo

Tại sao sẽ ((!$#))kích hoạt lịch sử thay thế?
Zhro

25

Tôi thường sử dụng đoạn mã này cho các tập lệnh đơn giản:

#!/bin/bash

if [ -z "$1" ]; then
    echo -e "\nPlease call '$0 <argument>' to run this command!\n"
    exit 1
fi

1
Vì vậy, điều này được sử dụng trong bạn chỉ cần một đối số?
Danijel

21

Chỉ vì có thêm một điểm cơ bản để chỉ ra tôi sẽ thêm rằng bạn chỉ có thể kiểm tra chuỗi của mình là null:

if [ "$1" ]; then
  echo yes
else
  echo no
fi

Tương tự như vậy nếu bạn đang mong đợi số lượng arg chỉ cần kiểm tra lần cuối của bạn:

if [ "$3" ]; then
  echo has args correct or not
else
  echo fixme
fi

và như vậy với bất kỳ arg hoặc var


4

Nếu bạn muốn kiểm tra xem đối số có tồn tại hay không, bạn có thể kiểm tra xem # của đối số lớn hơn hoặc bằng số đối số mục tiêu của bạn.

Kịch bản sau đây cho thấy cách thức hoạt động của nó

kiểm tra

#!/usr/bin/env bash

if [ $# -ge 3 ]
then
  echo script has at least 3 arguments
fi

tạo ra đầu ra sau

$ ./test.sh
~
$ ./test.sh 1
~
$ ./test.sh 1 2
~
$ ./test.sh 1 2 3
script has at least 3 arguments
$ ./test.sh 1 2 3 4
script has at least 3 arguments

3

Như một lời nhắc nhở nhỏ, các nhà khai thác thử nghiệm số trong Bash chỉ làm việc trên các số nguyên ( -eq, -lt, -ge, vv)

Tôi muốn đảm bảo $ vars của tôi là ints

var=$(( var + 0 ))

trước khi tôi kiểm tra chúng, chỉ cần bảo vệ chống lại lỗi "[: số nguyên arg yêu cầu".


1
Thủ thuật gọn gàng, nhưng xin lưu ý: do bash không có khả năng xử lý số float trong số học, phương pháp này có thể gây ra lỗi cú pháp và trả về giá trị khác không, điều này sẽ gây trở ngại khi errexit được bật. var=$(printf "%.0f" "$var")có thể xử lý số float nhưng bị thoát khỏi số 0 khi được cung cấp một chuỗi. Nếu bạn không phiền một awk, phương pháp này tôi sử dụng dường như là mạnh mẽ nhất để thực thi một số nguyên : var=$(<<<"$var" awk '{printf "%.0f", $0}'). Nếu var không được đặt, nó mặc định là "0". Nếu var là float, nó được làm tròn đến số nguyên gần nhất. Giá trị tiêu cực cũng tốt để sử dụng.
user.friendly

0

xác nhận chức năng bash liner

myFunction() {

    : ${1?"forgot to supply an argument"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

thêm tên hàm và cách sử dụng

myFunction() {

    : ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

thêm xác nhận để kiểm tra nếu số nguyên

để thêm xác thực bổ sung, ví dụ để kiểm tra xem đối số được truyền có phải là số nguyên hay không, hãy sửa đổi một lớp xác thực để gọi hàm xác thực:

: ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"} && validateIntegers $1 || die "Must supply an integer!"

sau đó, xây dựng hàm xác thực để xác thực đối số, trả về 0 khi thành công, 1 khi thất bại và hàm chết hủy bỏ tập lệnh khi thất bại

validateIntegers() {

    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
        return 1 # failure
    fi
    return 0 #success

}

die() { echo "$*" 1>&2 ; exit 1; }

Thậm chí đơn giản hơn - chỉ cần sử dụng set -u

set -u đảm bảo rằng mọi biến được tham chiếu được đặt khi nó được sử dụng, vì vậy chỉ cần đặt nó và quên nó

myFunction() {
    set -u
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}
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.