Sử dụng ngôn ngữ $ {a: -b} để gán biến trong tập lệnh


427

Tôi đã xem xét một vài tập lệnh mà người khác đã viết (cụ thể là Red Hat) và rất nhiều biến số của chúng được gán bằng cách sử dụng ký hiệu sau VARIABLE1="${VARIABLE1:-some_val}" hoặc một số biến mở rộng các biến khác VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

Điểm của việc sử dụng ký hiệu này thay vì chỉ khai báo các giá trị trực tiếp (ví dụ VARIABLE1=some_val:) là gì?

Có những lợi ích cho ký hiệu này hoặc các lỗi có thể sẽ được ngăn chặn?

Liệu :-có ý nghĩa cụ thể trong bối cảnh này?


Có một cái nhìn vào man bash; tìm kiếm khối "Mở rộng tham số" (khoảng 28%). Các bài tập này là các hàm mặc định: "Chỉ sử dụng giá trị mặc định nếu chưa có giá trị nào được đặt."
Hauke ​​Laging

Câu trả lời:


697

Kỹ thuật này cho phép một biến được gán một giá trị nếu một biến khác trống hoặc không xác định. LƯU Ý: "Biến khác" này có thể giống hoặc biến khác.

đoạn trích

${parameter:-word}
    If parameter is unset or null, the expansion of word is substituted. 
    Otherwise, the value of parameter is substituted.

LƯU Ý: Hình thức này cũng hoạt động ${parameter-word}. Nếu bạn muốn xem danh sách đầy đủ tất cả các hình thức mở rộng tham số có sẵn trong Bash thì tôi khuyên bạn nên xem chủ đề này trong wiki của Bash Hacker có tiêu đề: " Mở rộng tham số ".

Ví dụ

biến không tồn tại
$ echo "$VAR1"

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
default value
biến tồn tại
$ VAR1="has value"
$ echo "$VAR1"
has value

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
has value

Điều tương tự có thể được thực hiện bằng cách đánh giá các biến khác hoặc chạy các lệnh trong phần giá trị mặc định của ký hiệu.

$ VAR2="has another value"
$ echo "$VAR2"
has another value
$ echo "$VAR1"

$

$ VAR1="${VAR1:-$VAR2}"
$ echo "$VAR1"
has another value

Thêm ví dụ

Bạn cũng có thể sử dụng một ký hiệu hơi khác nhau VARX=${VARX-<def. value>}.

$ echo "${VAR1-0}"
has another value
$ echo "${VAR2-0}"
has another value
$ echo "${VAR3-0}"
0

Ở trên $VAR1& $VAR2đã được xác định với chuỗi "có giá trị khác" nhưng $VAR3không được xác định, do đó, giá trị mặc định được sử dụng thay thế , 0.

Một vi dụ khac

$ VARX="${VAR3-0}"
$ echo "$VARX"
0

Kiểm tra và gán sử dụng :=ký hiệu

Cuối cùng tôi sẽ đề cập đến các toán tử tiện dụng , :=. Điều này sẽ thực hiện kiểm tra và gán giá trị nếu biến được kiểm tra trống hoặc không xác định.

Thí dụ

Lưu ý rằng $VAR1bây giờ được thiết lập. Toán tử :=đã thực hiện kiểm tra và bài tập trong một thao tác duy nhất.

$ unset VAR1
$ echo "$VAR1"

$ echo "${VAR1:=default}"
default
$ echo "$VAR1"
default

Tuy nhiên, nếu giá trị được đặt trước, thì nó sẽ để yên.

$ VAR1="some value"
$ echo "${VAR1:=default}"
some value
$ echo "$VAR1"
some value

Bảng tham khảo tiện dụng Dandy

    ss của bảng

Người giới thiệu


8
Lưu ý rằng không phải tất cả những điều này là mở rộng được ghi lại trong bash. Các ${var:-word}một trong Q là, nhưng không phải là ${var-word}ở trên. Tuy nhiên, tài liệu POSIX có một bảng đẹp, có thể đáng để sao chép nó vào câu trả lời này - pubs.opengroup.org/onlinepub/9699919799/utilities/ Lỗi
Graeme

5
@Graeme, nó được ghi lại, bạn chỉ cần chú ý đến việc Bỏ kết quả dấu hai chấm trong một bài kiểm tra chỉ cho một tham số không được đặt.
Stéphane Chazelas

7
Sử dụng echo "${FOO:=default}"là tuyệt vời nếu bạn thực sự muốn echo. Nhưng nếu bạn không, thì hãy thử tích :hợp ... : ${FOO:=default} Bạn $FOOđược đặt thành defaultnhư trên (nghĩa là, nếu chưa được đặt). Nhưng không có tiếng vang $FOOtrong quá trình.
fbicknel

2
Ok, tìm thấy một câu trả lời: stackoverflow.com/q/24405606/1172302 . Sử dụng ${4:-$VAR}sẽ làm việc.
Nikos Alexandris

1
Tại đây, có +1 để đưa bạn tới 500. Xin chúc mừng! :)
XtraSimplility

17

@slm đã bao gồm các tài liệu POSIX - rất hữu ích - nhưng chúng không thực sự mở rộng về cách các tham số này có thể được kết hợp để ảnh hưởng lẫn nhau. Vẫn chưa có bất kỳ đề cập nào ở đây của mẫu này:

${var?if unset parent shell dies and this message is output to stderr}

Đây là một đoạn trích từ một câu trả lời khác của tôi và tôi nghĩ nó thể hiện rất rõ cách thức hoạt động của chúng:

    sh <<-\CMD
    _input_fn() { set -- "$@" #redundant
            echo ${*?WHERES MY DATA?}
            #echo is not necessary though
            shift #sure hope we have more than $1 parameter
            : ${*?WHERES MY DATA?} #: do nothing, gracefully
    }
    _input_fn heres some stuff
    _input_fn one #here
    # shell dies - third try doesnt run
    _input_fn you there?
    # END
    CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?

Một ví dụ khác từ cùng :

    sh <<-\CMD
    N= #N is NULL
    _test=$N #_test is also NULL and
    v="something you would rather do without"    
    ( #this subshell dies
        echo "v is ${v+set}: and its value is ${v:+not NULL}"
        echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
        ${_test:+${N:?so you test for it with a little nesting}}
        echo "sure wish we could do some other things"
    )
    ( #this subshell does some other things 
        unset v #to ensure it is definitely unset
        echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
        echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
        ${_test:+${N:?is never substituted}}
        echo "so now we can do some other things" 
    )
    #and even though we set _test and unset v in the subshell
    echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
    # END
    CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without

Ví dụ trên tận dụng lợi thế của cả 4 hình thức thay thế tham số POSIX và các thử nghiệm :colon nullhoặc khác nhau của chúng not null. Có nhiều thông tin hơn trong liên kết ở trên, và đây là một lần nữa .

Một điều khác mà mọi người thường không cân nhắc ${parameter:+expansion}là nó có thể hữu ích như thế nào trong tài liệu ở đây. Đây là một đoạn trích từ một câu trả lời khác :

HÀNG ĐẦU

Tại đây bạn sẽ đặt một số giá trị mặc định và chuẩn bị in chúng khi được gọi ...

#!/bin/sh
    _top_of_script_pr() ( 
        IFS="$nl" ; set -f #only split at newlines and don't expand paths
        printf %s\\n ${strings}
    ) 3<<-TEMPLATES
        ${nl=
}
        ${PLACE:="your mother's house"}
        ${EVENT:="the unspeakable."}
        ${ACTION:="heroin"}
        ${RESULT:="succeed."}
        ${strings:="
            I went to ${PLACE} and saw ${EVENT}
            If you do ${ACTION} you will ${RESULT}
        "}
    #END
    TEMPLATES

Ở GIỮA

Đây là nơi bạn xác định các chức năng khác để gọi chức năng in của mình dựa trên kết quả của chúng ...

    EVENT="Disney on Ice."
    _more_important_function() { #...some logic...
        [ $((1+one)) -ne 2 ] && ACTION="remedial mathematics"
            _top_of_script_pr
    }
    _less_important_function() { #...more logic...
        one=2
        : "${ACTION:="calligraphy"}"
        _top_of_script_pr
    }

CHAI

Bạn đã có tất cả thiết lập ngay bây giờ, vì vậy đây là nơi bạn sẽ thực hiện và lấy kết quả của mình.

    _less_important_function
    : "${PLACE:="the cemetery"}" 
    _more_important_function
    : "${RESULT:="regret it."}" 
    _less_important_function    

CÁC KẾT QUẢ

Tôi sẽ đi vào lý do tại sao ngay lập tức, nhưng chạy ở trên tạo ra kết quả sau:

_less_important_function()'s lần chạy đầu tiên:

Tôi đã đến nhà mẹ bạn và thấy Disney trên Ice.

Nếu bạn làm thư pháp bạn sẽ thành công.

sau đó _more_important_function():

Tôi đến nghĩa trang và thấy Disney trên Ice.

Nếu bạn làm toán khắc phục, bạn sẽ thành công.

_less_important_function() lần nữa:

Tôi đến nghĩa trang và thấy Disney trên Ice.

Nếu bạn làm toán học khắc phục, bạn sẽ hối tiếc.

LÀM THẾ NÀO NÓ HOẠT ĐỘNG:

Tính năng chính ở đây là khái niệm conditional ${parameter} expansion.Bạn chỉ có thể đặt biến thành giá trị nếu nó không được đặt hoặc null bằng cách sử dụng biểu mẫu:

${var_name: =desired_value}

Nếu thay vào đó, bạn chỉ muốn đặt một biến không đặt, bạn sẽ bỏ qua các :colongiá trị null và vẫn giữ nguyên.

TRÊN PHẠM VI:

Bạn có thể nhận thấy rằng trong ví dụ trên $PLACE$RESULTđược thay đổi khi được đặt qua parameter expansionmặc dù _top_of_script_pr()đã được gọi, có lẽ là đặt chúng khi nó chạy. Lý do công việc này _top_of_script_pr()là một ( subshelled )chức năng - Tôi đã đính kèm nó parensthay vì { curly braces }sử dụng cho các chức năng khác. Bởi vì nó được gọi trong một lớp con, mọi biến nó đặt locally scopedvà khi nó trở về vỏ mẹ, các giá trị đó sẽ biến mất.

Nhưng khi _more_important_function()thiết lập $ACTIONthì globally scopednó ảnh hưởng đến _less_important_function()'sđánh giá thứ hai $ACTIONvì chỉ _less_important_function()thiết lập $ACTIONthông qua${parameter:=expansion}.


1
Thật

8

Kinh nghiệm cá nhân.

Đôi khi tôi sử dụng định dạng này trong các tập lệnh của mình để thực hiện quá nhiều giá trị, ví dụ nếu tôi có:

$ cat script.sh
SOMETHING="${SOMETHING:-something}"; echo "$SOMETHING"; 

Tôi có thể chạy:

$ env SOMETHING="something other than the default value" ./script.sh` 

mà không phải thay đổi giá trị mặc định ban đầu của SOMETHING.

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.