การใช้“ $ {a: -b}” สำหรับการกำหนดตัวแปรในสคริปต์


426

ฉันดูสคริปต์บางตัวที่คนอื่นเขียน (โดยเฉพาะ Red Hat) และตัวแปรจำนวนมากถูกกำหนดโดยใช้สัญกรณ์ต่อไปนี้ VARIABLE1="${VARIABLE1:-some_val}" หรือบางตัวขยายตัวแปรอื่น ๆ VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

จุดประสงค์ของการใช้สัญลักษณ์นี้แทนที่จะประกาศเพียงค่าโดยตรงVARIABLE1=some_valคืออะไร?

มีประโยชน์กับสัญกรณ์นี้หรือข้อผิดพลาดที่เป็นไปได้ที่จะป้องกัน?

ที่ไม่:-ได้มีความหมายเฉพาะในบริบทนี้?


ลองดูที่man bash; ค้นหาบล็อก "การขยายพารามิเตอร์" (ประมาณ 28%) การมอบหมายเหล่านี้เป็นเช่นฟังก์ชั่นเริ่มต้น: "ใช้ค่าเริ่มต้นเฉพาะในกรณีที่ยังไม่มีการตั้งค่า"
Hauke ​​Laging

คำตอบ:


695

เทคนิคนี้อนุญาตให้ตัวแปรถูกกำหนดค่าหากตัวแปรอื่นว่างเปล่าหรือไม่ได้กำหนด หมายเหตุ: "ตัวแปรอื่น" นี้อาจเป็นตัวแปรเดียวกันหรือตัวแปรอื่น

สิ่งที่สกัดมา

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

หมายเหตุ:แบบฟอร์มนี้ใช้งานได้เช่น${parameter-word}กัน หากคุณต้องการดูรายการทั้งหมดของการขยายพารามิเตอร์ทุกรูปแบบที่มีอยู่ใน Bash ฉันขอแนะนำให้คุณดูหัวข้อนี้ในวิกิของ Bash แฮ็กเกอร์หัวข้อ: " การขยายพารามิเตอร์ "

ตัวอย่าง

ตัวแปรไม่มีอยู่
$ echo "$VAR1"

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
default value
มีตัวแปรอยู่
$ VAR1="has value"
$ echo "$VAR1"
has value

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

สิ่งเดียวกันสามารถทำได้โดยการประเมินตัวแปรอื่น ๆ หรือเรียกใช้คำสั่งภายในส่วนค่าเริ่มต้นของสัญกรณ์

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

$

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

ตัวอย่างเพิ่มเติม

VARX=${VARX-<def. value>}นอกจากนี้คุณยังสามารถใช้สัญกรณ์แตกต่างกันเล็กน้อยที่มันเป็นเพียงแค่

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

ในข้างต้น$VAR1และ$VAR2ถูกกำหนดไว้แล้วด้วยสตริง "มีค่าอื่น" แต่ก็ไม่ได้กำหนดดังนั้นค่าเริ่มต้นถูกนำมาใช้แทน$VAR30

ตัวอย่างอื่น

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

การตรวจสอบและการกำหนดโดยใช้:=สัญกรณ์

สุดท้ายฉันจะพูดถึงผู้ให้บริการที่ใช้งาน:=ง่าย, . สิ่งนี้จะทำการตรวจสอบและกำหนดค่าถ้าตัวแปรภายใต้การทดสอบว่างเปล่าหรือไม่ได้กำหนด

ตัวอย่าง

ขอให้สังเกตว่า$VAR1มีการตั้งค่าในขณะนี้ ผู้ประกอบ:=การทำการทดสอบและการมอบหมายในการดำเนินการเดียว

$ unset VAR1
$ echo "$VAR1"

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

อย่างไรก็ตามหากตั้งค่าไว้ก่อนหน้านี้จะถูกทิ้งไว้ตามลำพัง

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

ตารางอ้างอิง Handy Dandy

    เอสเอสของตาราง

อ้างอิง


8
bashไม่ทราบว่าสิ่งเหล่านี้เป็นขยายการบันทึกไว้ใน ${var:-word}หนึ่งใน Q แต่ไม่ได้${var-word}ดังกล่าวข้างต้น เอกสาร POSIX มีตารางที่ดี แต่อาจคุ้มค่าที่จะคัดลอกลงในคำตอบนี้ - pubs.opengroup.org/onlinepubs/9699919799/utilities/ …
แกรม

5
@ Graeme จะมีการบันทึกไว้คุณเพียงแค่ให้ความสนใจกับการละเว้นการโคลอนทำให้ได้ผลลัพธ์ในการทดสอบเฉพาะพารามิเตอร์ที่ไม่ได้ตั้งค่า
Stéphane Chazelas

7
ใช้ดีมากถ้าคุณต้องการจริงecho "${FOO:=default}" echoแต่ถ้าคุณทำไม่ได้ลองใช้:บิวท์อิน ... ตั้งค่า: ${FOO:=default} ของคุณเป็นด้านบน (เช่นถ้ายังไม่ได้ตั้งค่า) แต่ไม่มีเสียงสะท้อนในกระบวนการ $FOOdefault$FOO
fbicknel

2
ตกลงพบคำตอบ: stackoverflow.com/q/24405606/1172302 การใช้${4:-$VAR}งานจะ
Nikos Alexandris

1
ที่นี่มี +1 เพื่อนำคุณไปยัง 500 ขอแสดงความยินดี! :)
XtraSimplicity

17

@slm ได้รวมเอกสาร POSIX แล้ว - ซึ่งมีประโยชน์มาก - แต่พวกเขาไม่ได้ขยายว่าการรวมพารามิเตอร์เหล่านี้มีผลกระทบต่อกันอย่างไร ยังไม่มีการกล่าวถึงที่นี่ในแบบฟอร์มนี้:

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

นี่คือข้อความที่ตัดตอนมาจากคำตอบของฉันอีกและฉันคิดว่ามันแสดงให้เห็นถึงวิธีการทำงานเหล่านี้:

    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?

อีกตัวอย่างจากที่เดียวกัน :

    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

ตัวอย่างข้างต้นใช้ประโยชน์จากการทดแทนพารามิเตอร์ POSIX ทั้ง 4 รูปแบบและการทดสอบ:colon nullหรือnot nullการทดสอบที่หลากหลาย มีข้อมูลเพิ่มเติมในการเชื่อมโยงดังกล่าวข้างต้นและที่นี่ก็เป็นอีกครั้ง

อีกสิ่งหนึ่งที่ผู้คนมักไม่คำนึงถึง${parameter:+expansion}ก็คือมีประโยชน์มากแค่ไหนในเอกสารที่นี่ นี่เป็นข้อความที่ตัดตอนมาจากคำตอบอื่น :

TOP

ที่นี่คุณจะตั้งค่าเริ่มต้นและเตรียมพิมพ์เมื่อเรียก ...

#!/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

กลาง

นี่คือที่คุณกำหนดฟังก์ชั่นอื่น ๆ เพื่อเรียกใช้ฟังก์ชั่นการพิมพ์ของคุณขึ้นอยู่กับผลของพวกเขา ...

    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
    }

ด้านล่าง

คุณได้รับการตั้งค่าทั้งหมดแล้วดังนั้นนี่คือที่ที่คุณจะดำเนินการและดึงผลลัพธ์ของคุณ

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

ผล

ฉันจะไปทำไมในไม่ช้า แต่การเรียกใช้ข้างต้นสร้างผลลัพธ์ต่อไปนี้:

_less_important_function()'s วิ่งครั้งแรก:

ฉันไปที่บ้านแม่ของคุณและเห็นDisney on Ice

ถ้าคุณเขียนตัวอักษรคุณจะประสบความสำเร็จ

แล้วก็ _more_important_function():

ฉันไปที่สุสานและเห็น Disney on Ice

หากคุณทำคณิตศาสตร์การแก้ไขคุณจะประสบความสำเร็จ

_less_important_function() อีกครั้ง:

ฉันไปที่สุสานและเห็น Disney on Ice

หากคุณทำคณิตศาสตร์เพื่อการเยียวยาคุณจะต้องเสียใจ

มันทำงานอย่างไร:

คุณสมบัติที่สำคัญที่นี่เป็นแนวคิดของconditional ${parameter} expansion.คุณสามารถตั้งค่าตัวแปรให้เป็นค่าเฉพาะถ้ามันเป็น unset หรือ null โดยใช้แบบฟอร์ม:

${var_name=desired_value}

หากคุณต้องการตั้งค่าเฉพาะตัวแปรที่ไม่มีการตั้งค่าคุณจะละเว้น:colonและค่า Null จะยังคงเหมือนเดิม

ในขอบเขต:

คุณอาจสังเกตเห็นว่าในตัวอย่างข้างต้น$PLACEและ$RESULTได้รับการเปลี่ยนแปลงเมื่อตั้งค่าผ่านparameter expansionแม้ว่าจะ_top_of_script_pr()ถูกเรียกไปแล้วแต่ก็น่าจะตั้งค่าพวกเขาเมื่อมันทำงาน เหตุผลที่ทำให้งานนี้_top_of_script_pr()เป็น( subshelled )ฟังก์ชั่น - ฉันใส่มันไว้ในตัวparensแทนที่จะ{ curly braces }ใช้กับคนอื่น เนื่องจากมันถูกเรียกใน subshell ตัวแปรทุกตัวที่มันตั้งค่าlocally scopedและเมื่อมันกลับไปที่เปลือกแม่ของมันค่าเหล่านั้นจะหายไป

แต่เมื่อ_more_important_function()ตั้งค่า$ACTIONแล้วมันจะglobally scopedส่งผลกระทบต่อ_less_important_function()'sการประเมินผลที่สองของ$ACTIONเพราะ_less_important_function()กำหนด$ACTIONผ่านเท่านั้น${parameter:=expansion}.


1
ดีที่จะเห็นว่าค่าเริ่มต้นชวเลขและการทำงานในบอร์นเชลล์
Sridhar Sarnobat

8

ประสบการณ์ส่วนตัว.

ฉันใช้รูปแบบนี้บางครั้งในสคริปต์ของฉันเพื่อทำโฆษณาเกินค่าเช่นถ้าฉันมี:

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

ฉันวิ่งได้:

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

SOMETHINGโดยไม่ต้องเปลี่ยนค่าเริ่มต้นเดิมของ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.