Menggunakan "$ {a: -b}" untuk penugasan variabel dalam skrip

427

Saya telah melihat beberapa skrip yang ditulis orang lain (khususnya Red Hat), dan banyak variabel mereka ditugaskan menggunakan notasi berikut VARIABLE1="${VARIABLE1:-some_val}" atau beberapa memperluas variabel lain VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

Apa gunanya menggunakan notasi ini daripada hanya mendeklarasikan nilai secara langsung (misalnya, VARIABLE1=some_val)?

Apakah ada manfaat dari notasi ini atau kemungkinan kesalahan yang akan dicegah?

Apakah :-ada makna khusus dalam konteks ini?

Rothgar
sumber
Lihatlah man bash; cari blok "Ekspansi Parameter" (sekitar 28%). Tugas-tugas ini misalnya fungsi default: "Gunakan nilai default hanya jika belum ada yang ditetapkan."
Hauke ​​Laging

Jawaban:

698

Teknik ini memungkinkan suatu variabel diberi nilai jika variabel lain kosong atau tidak terdefinisi. CATATAN: "variabel lain" ini bisa sama atau variabel lain.

kutipan

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

CATATAN: Formulir ini juga berfungsi ${parameter-word},. Jika Anda ingin melihat daftar lengkap semua bentuk ekspansi parameter yang tersedia dalam Bash maka saya sangat menyarankan Anda melihat topik ini di wiki Bash Hacker berjudul: " Ekspansi parameter ".

Contohnya

variabel tidak ada
$ echo "$VAR1"

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

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

Hal yang sama dapat dilakukan dengan mengevaluasi variabel lain, atau menjalankan perintah dalam bagian nilai default dari notasi.

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

$

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

Lebih banyak contoh

Anda juga dapat menggunakan notasi yang sedikit berbeda di mana saja VARX=${VARX-<def. value>}.

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

Di atas $VAR1& $VAR2sudah ditentukan dengan string "memiliki nilai lain" tetapi $VAR3tidak terdefinisi, jadi nilai default yang digunakan sebagai gantinya 0,.

Contoh lain

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

Memeriksa dan menetapkan menggunakan :=notasi

Terakhir saya akan menyebutkan operator yang praktis :=,. Ini akan melakukan pemeriksaan dan menetapkan nilai jika variabel yang diuji kosong atau tidak terdefinisi.

Contoh

Perhatikan bahwa $VAR1sekarang sudah diatur. Operator :=melakukan tes dan penugasan dalam satu operasi.

$ unset VAR1
$ echo "$VAR1"

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

Namun jika nilainya diatur sebelumnya, maka dibiarkan saja.

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

Tabel Referensi Handy Dandy

    ss dari tabel

Referensi

slm
sumber
8
Perhatikan bahwa tidak semua ekspansi ini didokumentasikan di bash. Yang ${var:-word}ada di Q adalah, tetapi tidak di ${var-word}atas. Dokumentasi POSIX memiliki tabel yang bagus, mungkin layak untuk menyalinnya ke jawaban ini - pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Graeme
5
@Gema, itu didokumentasikan, Anda hanya perlu memperhatikan Menghilangkan hasil usus besar dalam tes hanya untuk parameter yang tidak disetel.
Stéphane Chazelas
7
Menggunakan echo "${FOO:=default}"itu bagus jika Anda benar-benar menginginkannya echo. Tetapi jika Anda tidak melakukannya, maka coba :built-in ... : ${FOO:=default} Anda $FOOdiatur ke defaultatas (yaitu, jika belum ditetapkan). Tapi tidak ada gaung $FOOdalam proses.
fbicknel
2
Oke, temukan jawaban: stackoverflow.com/q/24405606/1172302 . Menggunakan surat ${4:-$VAR}wasiat akan berhasil.
Nikos Alexandris
1
Di sini, dapatkan +1 untuk membawa Anda ke 500. Selamat! :)
XtraSimplicity
17

@slm telah menyertakan dokumen POSIX - yang sangat membantu - tetapi mereka tidak benar-benar memperluas bagaimana parameter ini dapat digabungkan untuk mempengaruhi satu sama lain. Belum ada sebutan di sini tentang formulir ini:

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

Ini adalah kutipan dari jawaban saya yang lain, dan saya pikir itu menunjukkan dengan sangat baik bagaimana ini bekerja:

    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?

Contoh lain dari yang sama :

    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

Contoh di atas mengambil keuntungan dari semua 4 bentuk substitusi parameter POSIX dan berbagai :colon nullatau not nullpengujian. Ada informasi lebih lanjut di tautan di atas, dan ini dia lagi .

Hal lain yang sering tidak dipertimbangkan orang ${parameter:+expansion}adalah betapa bermanfaatnya dokumen ini. Berikut kutipan lain dari jawaban yang berbeda :

TERATAS

Di sini Anda akan menetapkan beberapa default dan bersiap untuk mencetaknya saat dipanggil ...

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

TENGAH

Di sinilah Anda mendefinisikan fungsi lain untuk memanggil fungsi cetak Anda berdasarkan hasil mereka ...

    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
    }

BAWAH

Anda telah menyiapkan semuanya sekarang, jadi di sinilah Anda akan menjalankan dan menarik hasil Anda.

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

HASIL

Saya akan membahas mengapa suatu saat, tetapi menjalankan di atas menghasilkan hasil sebagai berikut:

_less_important_function()'s Lari pertama:

Saya pergi ke rumah ibumu dan melihat Disney on Ice.

Jika Anda melakukan kaligrafi, Anda akan berhasil.

kemudian _more_important_function():

Saya pergi ke kuburan dan melihat Disney on Ice.

Jika Anda melakukan matematika perbaikan Anda akan berhasil.

_less_important_function() lagi:

Saya pergi ke kuburan dan melihat Disney on Ice.

Jika Anda melakukan matematika perbaikan Anda akan menyesalinya.

BAGAIMANA ITU BEKERJA:

Fitur utama di sini adalah konsep conditional ${parameter} expansion.Anda dapat mengatur variabel ke nilai hanya jika tidak disetel atau null menggunakan formulir:

${var_name: =desired_value}

Jika Anda hanya ingin menetapkan variabel yang tidak disetel, Anda akan menghilangkan :colondan nilai-nilai nol akan tetap apa adanya.

TENTANG RUANG LINGKUP:

Anda mungkin memperhatikan bahwa dalam contoh di atas $PLACEdan $RESULTdiubah ketika diatur melalui parameter expansionmeskipun _top_of_script_pr()telah dipanggil, mungkin pengaturan mereka ketika dijalankan. Alasan ini bekerja adalah bahwa _top_of_script_pr()adalah ( subshelled )fungsi - Saya sertakan dalam parensdaripada { curly braces }yang digunakan untuk orang lain. Karena itu disebut dalam subkulit, setiap variabel yang disetel adalah locally scopeddan saat ia kembali ke shell induknya, nilai-nilai itu menghilang.

Tetapi ketika _more_important_function()set $ACTIONitu globally scopedbegitu mempengaruhi _less_important_function()'sevaluasi kedua $ACTIONkarena _less_important_function()set $ACTIONhanya melalui${parameter:=expansion}.

mikeserv
sumber
1
Senang melihat bahwa nilai default steno berfungsi di Bourne Shell
Sridhar Sarnobat
8

Pengalaman pribadi.

Saya menggunakan format ini kadang-kadang dalam skrip saya untuk melakukan ad-hoc nilai yang berlebihan, misalnya jika saya memiliki:

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

Saya bisa berlari:

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

tanpa harus mengubah nilai default asli dari SOMETHING.

hjk
sumber