Kiểm tra nếu nhiều biến được đặt


19

Tôi muốn đảm bảo rằng tại một điểm nhất định của tập lệnh, sau khi nhập sourcetệp cấu hình, một số biến được đặt và nếu không, sẽ dừng thực thi, thông báo cho người dùng về biến bị thiếu. Tôi đã thử

for var in $one $two $three ; do
    ...

nhưng nếu ví dụ $twokhông được đặt, vòng lặp sẽ không bao giờ được thực thi $two. Điều tiếp theo tôi đã thử là

for var in one two three ; do
    if [ -n ${!var} ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Nhưng nếu hai không được đặt, tôi vẫn nhận được "hai được đặt thành" thay vì "hai không được đặt".

Làm thế nào tôi có thể chắc chắn rằng tất cả các biến cần thiết được đặt?

Cập nhật / Giải pháp: Tôi biết rằng có một sự khác biệt giữa "thiết lập" và "thiết lập, nhưng trống rỗng". Bây giờ tôi sử dụng (nhờ /programming//a/16753536/3456281 và câu trả lời cho câu hỏi này) như sau:

if [ -n "${!var:-}" ] ; then

vì vậy, nếu varđược đặt nhưng trống, nó vẫn được coi là không hợp lệ.


1
Bạn cũng có thể thêm set -uvào đầu tập lệnh của mình để chấm dứt tập lệnh ngay lập tức khi sử dụng biến không đặt.
n.st

Câu trả lời:


8

Trích dẫn lỗi.

if [ -n "${!var}" ] ; then

Vì tương lai: Cài đặt

set -x

trước khi chạy mã sẽ chỉ cho bạn vấn đề. Thay vì thêm mã đó vào mã, bạn có thể gọi tập lệnh của mình bằng

bash -vx ./my/script.sh

Điều này hoạt động, nhưng những gì đang xảy ra với / trích dẫn không có? Là cách tiếp cận chung của tôi đúng nơi đầu tiên?
Jasper

Vâng, đó là nhận thức: Tôi đã trả lời câu hỏi trước khi được hỏi ... 8-)
Hauke ​​Laging

4
@Jasper Bạn nên luôn luôn trích dẫn các biến. Điều đó không tốn nhiều thời gian hơn là suy nghĩ về việc liệu điều này có cần thiết mọi lúc không. Nhưng nó tránh được các lỗi.
Hauke ​​Laging

Ngược lại, trích dẫn chỉ bảo vệ chống lại sự mở rộng. Nếu việc mở rộng là những gì bạn quan tâm, trích dẫn chắc chắn không phải là cách để đi. Ví dụ: cs = 673,290,765 ,; đặt - $ (IFS = ,; echo $ cs); tiếng vang $ #; đầu ra: 3
mikeerv

2
@mikeerv Chỉ các trích dẫn duy nhất bảo vệ chống lại việc mở rộng những gì tôi rõ ràng không đề xuất. Dấu ngoặc kép bảo vệ chống chia tách từ. Tôi đã không phủ nhận rằng có thể có trường hợp trích dẫn phải được bỏ qua. Nhưng đó không phải là một lập luận chống lại quy tắc chung của tôi. Những loại người sẽ sử dụng một cái gì đó như thế set -- $(IFS=, ; echo $cs)nào? Loại người phải hỏi ở đây tại sao if [ -n ${!var} ] ; thenkhông làm việc? Chắc là không.
Hauke ​​Laging

5

Điều duy nhất bạn cần là trích dẫn trong bài kiểm tra của bạn:

for var in one two three ; do
    if [ -n "${!var}" ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Làm việc cho tôi.


4

Nếu bạn muốn chương trình dừng lại:

N= 
${one?var 1 is unset} 
${two:?var 2 is unset or null}
${three:+${N:?var 3 is set and not null}}

Đó sẽ là một mẹo nhỏ. Mỗi thông báo theo dấu chấm hỏi được in stderrvà shell cha chết. Chà, vậy, không phải mỗi tin nhắn - chỉ một tin nhắn - chỉ một tin nhắn đầu tiên không in được một tin nhắn khiến shell bị chết. Tôi thích sử dụng các thử nghiệm như thế này:

( for v in "$one" "$two" "$three" ; do
    i=$((i+1)) ; : ${v:?var $i is unset or null...} 
done ) || _handle_it

Tôi đã có rất nhiều điều để nói về điều này ở đây .


2

Bạn có thể thêm

set -u

vào đầu tập lệnh của bạn để làm cho nó kết thúc khi nó cố gắng sử dụng một biến không đặt.

Một kịch bản như

#!/bin/sh
set -u
echo $foo

sẽ cho kết quả

script.sh: 3: script.sh: foo: tham số không được đặt

Nếu bạn đang sử dụng bashthay thế, lỗi sẽ như thế này:

script.sh: dòng 3: foo: biến không liên kết


Và theo ý kiến ​​của bạn điều này có ý nghĩa gì đối với việc kiểm tra thời gian chạy?
Hauke ​​Laging

2
@HaukeLaging Tôi không hoàn toàn theo dõi bạn - set -ungăn chặn chính xác loại lỗi mà OP đang cố gắng tránh và (ngược lại với tất cả các giải pháp khác) không giới hạn ở một bộ biến cụ thể. Trên thực tế, đây là một biện pháp phòng ngừa hữu ích cho hầu hết tất cả các tập lệnh shell để chúng thất bại một cách an toàn thay vì làm những điều không mong muốn khi một biến không được đặt. Bài viết này là một tài liệu tham khảo hữu ích về chủ đề này.
n.st

@ n.st Tôi không đồng ý - null có thể hữu ích như một giá trị không phải là null nếu bạn lập kế hoạch cho nó.
mikeerv

1
@ n.st Điều này không có ý nghĩa gì đối với OP vì anh ta không muốn bảo vệ chống lại việc truy cập các biến không đặt (BTW: một biến nhưng bộ trống sẽ gây ra lỗi tương tự nhưng cũng không phản ứng với "bảo vệ" của bạn). Anh ta muốn kiểm tra thời gian chạy xem một biến không được đặt / trống. Đề xuất của bạn có thể hữu ích như một trợ giúp phát triển chung nhưng không giải quyết được vấn đề của OP.
Hauke ​​Laging

set -utôi có thể sử dụng tốt như tôi nghĩ, nhưng nó không linh hoạt như mở rộng tham số thích hợp (xem câu hỏi cập nhật của tôi). Và với việc set -utôi phải tạo một quyền truy cập giả cho tất cả các biến mục tiêu nếu tôi muốn kiểm tra sự hiện diện của chúng ở một nơi.
Jasper

2

Một giải pháp thân thiện tối đa kiểm tra tất cả các yêu cầu và báo cáo chúng cùng nhau, thay vì thất bại ở lần đầu tiên và yêu cầu qua lại để thực hiện đúng:

#!/bin/bash

required_vars=(one two three)

missing_vars=()
for i in "${required_vars[@]}"
do
    test -n "${!i:+y}" || missing_vars+=("$i")
done
if [ ${#missing_vars[@]} -ne 0 ]
then
    echo "The following variables are not set, but should be:" >&2
    printf ' %q\n' "${missing_vars[@]}" >&2
    exit 1
fi

Tôi sử dụng một biến mảng để theo dõi những biến nào chưa được đặt và sử dụng kết quả để soạn tin nhắn hướng tới người dùng.

Ghi chú:

  • Tôi đã trích dẫn ${required_vars[@]}trong forvòng lặp chủ yếu theo thói quen - Tôi sẽ không khuyên bao gồm bất kỳ siêu ký tự shell nào trong tên biến của bạn!
  • Tôi đã không trích dẫn ${#missing_vars[@]}, bởi vì đó luôn là một số nguyên, ngay cả khi bạn đủ sai lầm để bỏ qua lời khuyên trước đó.
  • Tôi đã sử dụng %qkhi in; %sthông thường sẽ là đủ.
  • Đầu ra lỗi luôn đi đến luồng lỗi >&2, do đó, nó không được chuyển sang các lệnh hạ lưu
  • Im lặng là vàng - không in thông tin tiến trình hoặc thông tin gỡ lỗi trừ khi được hỏi cụ thể. Điều đó làm cho lỗi rõ ràng hơn.

1

bash4.2 cho phép bạn kiểm tra nếu một biến được đặt với -vtoán tử; một biến unset và một biến được đặt thành chuỗi rỗng là hai điều kiện khác nhau:

$ unset foo
$ [[ -v foo ]] && echo foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
$ foo=
$ [[ -v foo ]] && echo foo is set
foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty

Tôi đã hỏi về "nhiều" biến số, vì vậy tôi đang tìm kiếm một cái gì đó giống như sự gián tiếp ...
Jasper

Bạn không cần gián tiếp, vì -vlấy tên của một biến, vì vậy for var in one two three; [[ -v $var ]] && ...; donesẽ kiểm tra nếu mỗi người trong số one, twothreeđược thiết lập theo thứ tự.
chepner

1

Tôi nghĩ nếu bạn có ý nghĩa not set, vì vậy biến không bao giờ được khởi tạo. Nếu bạn sử dụng [ -n "${!var}" ], do đó, biến trống như thế two=""sẽ thất bại, trong khi nó được đặt . Bạn có thể thử điều này:

one=1
three=3

for var in one two three; do
  declare -p $var > /dev/null 2>&1 \
  && printf '%s is set to %s\n' "$var" "${!var}" \
  || printf '%s is not set\n' "$var"
done

0

Một giải pháp ngắn gọn (mặc dù hơi khó hiểu), để xử lý trường hợp sourcetập lệnh d có thể đặt các biến thành giá trị null, là khởi tạo các biến thành một giá trị đặc biệt trước khi tìm nguồn của tập lệnh và sau đó kiểm tra giá trị đó , ví dụ

one=#UNSET#
two=#UNSET#
three=#UNSET#

. set_vars_script

if [[ $one$two$three == *#UNSET#* ]] ; then
  echo "One or more essential variables are unset" >&2
  exit 1
fi

1
một giải pháp hay khác, nhưng tôi muốn đưa ra một thông báo lỗi cụ thể hơn sau đó "bỏ một hoặc nhiều biến" ...
Jasper

0

Tôi đã mở rộng câu trả lời của @ mikeerv .

Phiên bản này lấy danh sách các biến để kiểm tra sự hiện diện của, in tên của biến bị thiếu và kích hoạt lệnh gọi sử dụng () do lỗi.

REQD_VALUES=("VARIABLE" "FOO" "BAR" "OTHER_VARIABLE")
( i=0; for var_name in ${REQD_VALUES[@]}; do
    VALUE=${!var_name} ;
    i=$((i+1)) ; : ${VALUE:?$var_name is missing}
done ) || usage

Ví dụ đầu ra khi thiếu một giá trị:

./myscript.sh: line 42: VALUE: OTHER_VARIABLE is missing

Lưu ý rằng bạn có thể thay đổi biến có tên là GIÁ TRỊ thành một tên thay thế phù hợp với đầu ra mong muốn của bạn tốt hơ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.