Bagaimana cara memeriksa apakah suatu variabel diatur dalam Bash?


1568

Bagaimana saya tahu jika suatu variabel diatur dalam Bash?

Misalnya, bagaimana saya memeriksa apakah pengguna memberikan parameter pertama ke suatu fungsi?

function a {
    # if $1 is set ?
}

12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
Jens

210
Catatan untuk para pencari solusi: Ada banyak jawaban berperingkat tinggi untuk pertanyaan ini yang menjawab pertanyaan "variabel tidak kosong". Solusi koreksi lebih banyak ("adalah set variabel") disebutkan dalam jawaban oleh Jens dan Lionel di bawah ini.
Nathan Kidd

8
Russell Harmon dan Seamus juga benar dengan -vpengujian mereka , meskipun ini tampaknya hanya tersedia pada versi baru bashdan tidak portabel di seluruh cangkang.
Graeme

5
Seperti yang ditunjukkan oleh @NathanKidd, solusi yang benar diberikan oleh Lionel dan Jens. prosseek, Anda harus mengalihkan jawaban yang Anda terima ke salah satunya.
Garrett

3
... atau jawaban yang salah dapat di-downvotasikan oleh yang lebih cerdas di antara kita, karena @prosseek tidak mengatasi masalah tersebut.
dan3

Jawaban:


2305

(Biasanya) Cara yang benar

if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi

di mana ${var+x}adalah ekspansi parameter yang mengevaluasi apa-apa jika vartidak disetel, dan mengganti string xsebaliknya.

Kutipan Digresi

Kutipan dapat dihilangkan (jadi kita dapat mengatakan ${var+x}alih-alih "${var+x}") karena sintaks & penggunaan ini menjamin ini hanya akan meluas ke sesuatu yang tidak memerlukan penawaran (karena diperluas ke x(yang tidak mengandung kata istirahat sehingga tidak perlu mengutip), atau untuk tidak ada (yang menghasilkan [ -z ], yang dengan mudah mengevaluasi nilai yang sama (benar) yang [ -z "" ]berlaku juga)).

Namun, sementara kutipan dapat dihilangkan dengan aman, dan itu tidak segera jelas bagi semua (bahkan tidak jelas bagi penulis pertama dari penjelasan kutipan ini yang juga merupakan pembuat kode Bash utama), kadang-kadang akan lebih baik untuk menulis solusi dengan kutipan sebagai [ -z "${var+x}" ], dengan biaya yang sangat kecil dari penalti kecepatan O (1). Penulis pertama juga menambahkan ini sebagai komentar di sebelah kode menggunakan solusi ini memberikan URL untuk jawaban ini, yang sekarang juga mencakup penjelasan mengapa kutipan dapat dihilangkan dengan aman.

(Sering) dengan cara yang salah

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

Ini sering salah karena tidak membedakan antara variabel yang tidak disetel dan variabel yang disetel ke string kosong. Artinya, jika var='', maka solusi di atas akan menampilkan "var is blank".

Perbedaan antara tidak disetel dan "disetel ke string kosong" sangat penting dalam situasi di mana pengguna harus menentukan ekstensi, atau daftar properti tambahan, dan yang tidak menentukan default untuk nilai yang tidak kosong, sedangkan menentukan string kosong harus buat skrip menggunakan ekstensi kosong atau daftar properti tambahan.

Perbedaannya mungkin tidak penting dalam setiap skenario. Dalam kasus-kasus itu [ -z "$var" ]akan baik-baik saja.


29
@ Garrett, hasil edit Anda membuat jawaban ini salah, ${var+x}adalah pengganti yang tepat untuk digunakan. Menggunakan [ -z ${var:+x} ]tidak menghasilkan hasil yang berbeda dari [ -z "$var" ].
Graeme

11
Ini tidak berhasil. Saya mendapatkan "tidak disetel" terlepas dari apakah var diatur ke nilai atau tidak (dibersihkan dengan "unset var", "echo $ var" tidak menghasilkan keluaran).
Brent212

10
Untuk sintaks solusi $ {parameter + word}, ​​bagian manual resmi adalah gnu.org/software/bash/manual/… ; Namun, bug dalam hal itu, itu tidak terlalu jelas menyebutkan sintaks ini tetapi mengatakan hanya mengutip (Menghilangkan titik dua [":"] menghasilkan tes hanya untuk parameter yang tidak disetel .. $ {parameter: + word} [berarti] Jika parameter nol atau tidak disetel, tidak ada yang diganti, jika tidak, ekspansi kata akan diganti.); ref pubs.opengroup.org/onlinepubs/9699919799/utilities/… yang dikutip (memiliki banyak pengetahuan) memiliki dokumen yang lebih jelas di sini.
Destiny Architect

7
Sepertinya tanda kutip diperlukan ketika Anda menggunakan banyak ekspresi, mis. [ -z "" -a -z ${var+x} ]Mendapat bash: [: argument expectedbash 4.3-ubuntu (tetapi tidak misalnya zsh). Saya benar-benar tidak suka jawabannya menggunakan sintaks yang kebetulan berfungsi dalam beberapa kasus.
FauxFaux

41
Menggunakan simple [ -z $var ]tidak lebih "salah" dari menghilangkan tanda kutip. Either way, Anda membuat asumsi tentang input Anda. Jika Anda baik-baik saja memperlakukan string kosong sebagai tidak disetel, [ -z $var ]itu saja yang Anda butuhkan.
nshew

910

Untuk memeriksa variabel string bukan-nol / bukan-nol, yaitu jika disetel, gunakan

if [ -n "$1" ]

Ini kebalikan dari -z. Saya menemukan diri saya menggunakan -nlebih dari -z.

Anda akan menggunakannya seperti:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

86
Saya biasanya lebih suka [[ ]]lebih [ ], karena [[ ]]lebih kuat dan menyebabkan masalah kurang dalam situasi tertentu (lihat pertanyaan ini untuk penjelasan tentang perbedaan antara keduanya). Pertanyaannya secara khusus meminta solusi bash dan tidak menyebutkan persyaratan portabilitas.
Aliran

18
Pertanyaannya adalah bagaimana seseorang dapat melihat apakah suatu variabel diatur . Maka Anda tidak dapat mengasumsikan bahwa variabel yang ditetapkan.
HelloGoodbye

7
Saya setuju, []berfungsi lebih baik terutama untuk skrip shell (non-bash).
Hengjie


63
Perhatikan bahwa -nini adalah tes default, jadi lebih sederhana [ "$1" ]atau [[ $1 ]]berfungsi juga. Perhatikan juga bahwa dengan [[ ]] mengutip tidak perlu .
tgl

478

Berikut cara menguji apakah parameter tidak disetel , atau kosong ("Null") atau disetel dengan nilai :

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

Sumber: POSIX: Ekspansi Parameter :

Dalam semua kasus yang ditunjukkan dengan "pengganti", ekspresi diganti dengan nilai yang ditunjukkan. Dalam semua kasus yang ditunjukkan dengan "menetapkan", parameter diberi nilai itu, yang juga menggantikan ekspresi.

Untuk menunjukkan ini dalam tindakan:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

5
@ HaloGoodbye Ya itu berhasil: set foo; echo ${1:-set but null or unset}echo "foo"; set --; echo ${1:-set but null or unset}gema ditetapkan tetapi nol ...
Jens

4
@ HaloGoodbye Parameter posisi dapat diatur dengan, eh, set:-)
Jens

95
Jawaban ini sangat membingungkan. Adakah contoh praktis tentang cara menggunakan tabel ini?
Ben Davis

12
@BenDavis Detailnya dijelaskan dalam tautan ke standar POSIX, yang merujuk pada bab dari tabel mana. Satu konstruk yang saya gunakan di hampir semua skrip yang saya tulis adalah : ${FOOBAR:=/etc/foobar.conf}untuk menetapkan nilai default untuk variabel jika tidak disetel atau null.
Jens

1
parameteradalah nama variabel apa saja. wordadalah beberapa string untuk menggantikan tergantung pada sintaksis dalam tabel yang Anda gunakan, dan apakah variabel $parameterdiatur, ditetapkan tetapi null, atau tidak disetel. Bukan untuk membuat hal-hal lebih rumit, tetapi wordjuga bisa memasukkan variabel! Ini bisa sangat berguna untuk melewatkan argumen opsional yang dipisahkan oleh karakter. Misalnya: ${parameter:+,${parameter}}menampilkan koma yang dipisahkan ,$parameterjika diset tetapi tidak nol. Dalam hal ini, wordadalah,${parameter}
TrinitronX

260

Sementara sebagian besar teknik yang dinyatakan di sini adalah benar, bash 4.2 mendukung tes aktual untuk kehadiran variabel ( man bash ), daripada menguji nilai variabel.

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

Khususnya, pendekatan ini tidak akan menyebabkan kesalahan ketika digunakan untuk memeriksa variabel set -u/ set -o nounsetmode yang tidak disetel , tidak seperti banyak pendekatan lain, seperti menggunakan [ -z.


9
Dalam bash 4.1.2, terlepas dari apakah variabel diatur, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Dan

32
Argumen '-v' pada builtin 'test' ditambahkan di bash 4.2.
Ti Strga

19
argumen -v ditambahkan sebagai pelengkap opsi -u shell (nounset). Dengan 'nounset' dihidupkan (set -u), shell akan mengembalikan kesalahan dan berakhir, jika tidak interaktif. $ # bagus untuk memeriksa parameter posisi. Namun, variabel bernama memerlukan beberapa cara lain, selain clobbering, untuk mengidentifikasi mereka sebagai tidak disetel. Sangat disayangkan bahwa fitur ini datang sangat terlambat karena banyak yang terikat dengan versi yang lebih awal, dalam hal ini mereka tidak dapat menggunakannya. Satu-satunya pilihan lain adalah dengan menggunakan trik atau 'peretasan' untuk menyiasatinya seperti yang ditunjukkan di atas tetapi itu paling tidak elegan.
osirisgothra

2
Catatan ini tidak berfungsi untuk variabel yang dideklarasikan tetapi tidak disetel. misalnyadeclare -a foo
miken32

17
Inilah sebabnya saya terus membaca setelah jawaban yang diterima / terpilih tertinggi.
Joe

176

Ada banyak cara untuk melakukan ini dengan yang berikut ini salah satunya:

if [ -z "$1" ]

Ini berhasil jika $ 1 adalah nol atau tidak disetel


46
Ada perbedaan antara parameter yang tidak disetel dan parameter dengan nilai nol.
chepner

21
Saya hanya ingin menjadi bertele-tele dan menunjukkan ini adalah jawaban yang berlawanan dengan apa yang diajukan pertanyaan. Pertanyaan bertanya apakah variabel diatur, bukan jika variabel tidak diatur.
Philluminati

47
[[ ]]hanyalah perangkap portabilitas. if [ -n "$1" ]harus digunakan di sini.
Jens

73
Jawaban ini salah. Pertanyaannya menanyakan cara untuk mengetahui apakah suatu variabel disetel, bukan apakah itu disetel ke nilai yang tidak kosong. Diberikan foo="", $foomemiliki nilai, tetapi -zhanya akan melaporkan bahwa itu kosong. Dan -z $fooakan meledak jika sudah set -o nounset.
Keith Thompson

4
Itu tergantung pada definisi "variabel diatur". Jika Anda ingin memeriksa apakah variabel diatur ke string bukan nol , maka jawaban mbranning sudah benar. Tetapi jika Anda ingin memeriksa apakah variabel dideklarasikan tetapi tidak diinisialisasi (yaitu foo=), maka jawaban Russel benar.
Aliran

77

Saya selalu menemukan tabel POSIX di jawaban lain lambat untuk grok, jadi inilah pendapat saya:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Perhatikan bahwa setiap kelompok (dengan dan tanpa usus besar sebelumnya) memiliki set dan kasus yang tidak disetel yang sama , jadi satu-satunya hal yang berbeda adalah bagaimana kasus-kasus kosong ditangani.

Dengan usus besar sebelumnya, case kosong dan unset identik, jadi saya akan menggunakan yang mungkin (yaitu penggunaan :=, bukan hanya =, karena case kosong tidak konsisten).

Judul:

  • set berarti VARIABLEnon-kosong ( VARIABLE="something")
  • kosong berarti VARIABLEkosong / nol ( VARIABLE="")
  • tidak disetel berarti VARIABLEtidak ada ( unset VARIABLE)

Nilai:

  • $VARIABLE berarti hasilnya adalah nilai asli dari variabel.
  • "default" berarti hasilnya adalah string pengganti yang disediakan.
  • "" berarti hasilnya nol (string kosong).
  • exit 127 berarti skrip berhenti dijalankan dengan kode keluar 127.
  • $(VARIABLE="default")berarti hasilnya adalah nilai asli dari variabel dan string pengganti yang diberikan ditugaskan ke variabel untuk digunakan di masa depan.

1
Anda memperbaiki hanya kesalahan pengodean aktual (contoh tipuan yang tidak diinginkan ketika bekerja dengan variabel). Saya membuat edit untuk memperbaiki beberapa kasus lagi di mana dolar membuat perbedaan dalam menafsirkan deskripsi. BTW, semua variabel shell (dengan pengecualian array) adalah variabel string; mungkin saja mengandung string yang dapat diartikan sebagai angka. Berbicara tentang string hanya mengaburkan pesan. Kutipan tidak ada hubungannya dengan string, mereka hanya sebuah alternatif untuk melarikan diri. VARIABLE=""dapat ditulis sebagai VARIABLE=. Namun, yang pertama lebih mudah dibaca.
Palec

Terima kasih untuk tabelnya, ini sangat berguna, namun saya mencoba membuat skrip keluar jika tidak disetel atau kosong. Tetapi kode keluar yang saya dapatkan adalah 1 bukan 127.
Astronaut

64

Untuk melihat apakah suatu variabel tidak kosong, saya menggunakan

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

Tes sebaliknya jika variabel tidak disetel atau kosong:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

Untuk melihat apakah suatu variabel disetel (kosong atau kosong), saya menggunakan

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

Tes sebaliknya jika variabel tidak disetel:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments

Saya juga telah menggunakan ini untuk sementara waktu; hanya dalam cara yang dikurangi:[ $isMac ] && param="--enable-shared"

@ Bhaskar Saya pikir itu karena jawaban ini sudah memberikan jawaban yang pada dasarnya sama (gunakan ${variable+x}untuk mengatur xiff $variable) hampir setengah tahun sebelumnya. Juga memiliki lebih banyak detail menjelaskan mengapa itu benar.
Mark Haferkamp

tetapi ${variable+x}kurang dapat dibaca (& jelas)
knocte

35

Pada versi modern Bash (4,2 atau lebih baru, saya pikir; saya tidak tahu pasti), saya akan mencoba ini:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi

5
Perhatikan juga yang [ -v "$VARNAME" ]tidak salah, tetapi cukup lakukan tes yang berbeda. Misalkan VARNAME=foo; kemudian memeriksa apakah ada variabel bernama fooyang disetel.
chepner

5
Catatan untuk mereka yang menginginkan portabilitas, -vtidak sesuai dengan POSIX
kzh

Jika salah satu mendapat [: -v: unexpected operator, kita harus memastikan untuk menggunakan bashbukan sh.
koppor

25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

Meskipun untuk argumen biasanya yang terbaik untuk menguji $ #, yang merupakan jumlah argumen, menurut pendapat saya.

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

1
Tes pertama mundur; Saya pikir Anda ingin [ "$1" != "" ](atau [ -n "$1" ]) ...
Gordon Davisson

10
Ini akan gagal jika $1diatur ke string kosong.
HelloGoodbye,

2
Bagian kedua dari jawaban (menghitung parameter) adalah solusi yang berguna untuk bagian pertama dari jawaban yang tidak berfungsi saat $1diatur ke string kosong.
Mark Berry

Ini akan gagal jika set -o nounsetaktif.
Flimm

21

Catatan

Saya memberikan jawaban yang sangat terfokus pada Bash karena bashtag tersebut.

Jawaban singkat

Selama Anda hanya berurusan dengan variabel bernama di Bash, fungsi ini harus selalu memberi tahu Anda jika variabel telah ditetapkan, bahkan jika itu adalah array kosong.

variable-is-set() {
    declare -p "$1" &>/dev/null
}

Kenapa ini bekerja?

Dalam Bash (setidaknya sejauh 3.0), jika varmerupakan variabel yang dideklarasikan / ditetapkan, maka declare -p varoutput sebuah declareperintah yang akan mengatur variabel varuntuk apa pun jenis dan nilainya saat ini, dan mengembalikan kode status 0(sukses). Jika vartidak dideklarasikan, kemudian declare -p varmenampilkan pesan kesalahan ke stderrdan mengembalikan kode status 1. Menggunakan &>/dev/null, mengalihkan baik reguler stdoutdan stderroutput ke /dev/null, tidak pernah terlihat, dan tanpa mengubah kode status. Dengan demikian fungsi hanya mengembalikan kode status.

Mengapa metode lain (kadang-kadang) gagal di Bash

  • [ -n "$var" ]: Ini hanya memeriksa jika ${var[0]}tidak kosong. (Dalam Bash, $varsama dengan ${var[0]}.)
  • [ -n "${var+x}" ]: Ini hanya memeriksa jika ${var[0]}diatur.
  • [ "${#var[@]}" != 0 ]: Ini hanya memeriksa jika setidaknya satu indeks $vardiatur.

Ketika metode ini gagal di Bash

Ini hanya bekerja untuk variabel bernama (termasuk $_), tidak variabel khusus tertentu ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., dan setiap saya mungkin sudah lupa). Karena tidak satupun dari ini adalah array, gaya POSIX [ -n "${var+x}" ]berfungsi untuk semua variabel khusus ini. Tetapi berhati-hatilah untuk membungkusnya dalam suatu fungsi karena banyak variabel khusus mengubah nilai / keberadaan ketika fungsi dipanggil.

Catatan kompatibilitas shell

Jika naskah Anda memiliki array dan Anda mencoba untuk membuatnya kompatibel dengan banyak kerang mungkin, maka pertimbangkan untuk menggunakan typeset -pbukan declare -p. Saya pernah membaca bahwa ksh hanya mendukung yang pertama, tetapi belum dapat menguji ini. Saya tahu bahwa Bash 3.0+ dan Zsh 5.5.1 masing-masing mendukung keduanya , typeset -pdan declare -phanya berbeda satu yang merupakan alternatif untuk yang lain. Tapi saya belum menguji perbedaan di luar dua kata kunci tersebut, dan saya belum menguji shell lain.

Jika Anda membutuhkan skrip agar POSIX sh kompatibel, maka Anda tidak dapat menggunakan array. Tanpa array, [ -n "{$var+x}" ]berfungsi.

Kode perbandingan untuk berbagai metode di Bash

Fungsi ini menghapus variabel var, evalsebagai kode yang diteruskan, menjalankan tes untuk menentukan apakah vardiatur oleh evalkode d, dan akhirnya menunjukkan kode status yang dihasilkan untuk pengujian yang berbeda.

Saya melewatkan test -v var,, [ -v var ]dan [[ -v var ]]karena mereka menghasilkan hasil yang identik dengan standar POSIX [ -n "${var+x}" ], sementara membutuhkan Bash 4.2+. Saya juga melewatkan typeset -pkarena sama seperti declare -pdi shell saya sudah diuji (Bash 3.0 hingga 5.0, dan Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

Kode uji kasus

Perhatikan bahwa hasil tes mungkin tidak terduga karena Bash memperlakukan indeks array non-numerik sebagai "0" jika variabel belum dinyatakan sebagai array asosiatif. Juga, array asosiatif hanya berlaku di Bash 4.0+.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Uji keluaran

Tes mnemonik dalam berkorespondensi baris header untuk [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], dan declare -p varmasing-masing.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

Ringkasan

  • declare -p var &>/dev/null apakah (100%?) dapat diandalkan untuk menguji variabel bernama di Bash sejak setidaknya 3,0.
  • [ -n "${var+x}" ] dapat diandalkan dalam situasi yang sesuai dengan POSIX, tetapi tidak dapat menangani array.
  • Tes lain ada untuk memeriksa apakah suatu variabel adalah kosong, dan untuk memeriksa variabel yang dideklarasikan dalam shell lain. Tetapi tes ini tidak cocok untuk skrip Bash atau POSIX.

3
Dari jawaban yang saya coba, yang ini ( declare -p var &>/dev/null) adalah SATU-SATUNYA yang berfungsi. TERIMA KASIH!
user1387866

2
@ mark-haferkamp Saya mencoba mengedit jawaban Anda untuk menambahkan kritis sebelumnya "/" ke kode fungsi Anda sehingga terbaca ... &> /dev/nulltetapi SO tidak akan membiarkan saya mengedit satu karakter 🙄 (harus> 6 karakter - bahkan mencoba menambah spasi putih tetapi tidak berhasil). Juga, mungkin ada baiknya mengubah fungsi untuk beralih ke $1variabel bernama sampel (misalnya OUTPUT_DIR) karena seperti yang Anda tunjukkan, variabel khusus seperti $1tidak berfungsi di sini. Bagaimanapun, ini adalah pengeditan yang kritis jika tidak, kode tersebut mencari untuk membuat file yang disebut nulldalam direktori relatif bernama dev. BTW - jawaban yang bagus!
joehanna

Juga, menggunakan nama variabel tanpa awalan $ juga berfungsi: declare -p PATH &> /dev/nullmengembalikan 0 di mana sebagai declare -p ASDFASDGFASKDF &> /dev/nullpengembalian 1. (pada bash 5.0.11 (1) -release)
joehanna

1
Kerja bagus! 1 kata hati-hati: declare varmenyatakan var variabel tetapi tidak mengaturnya dalam hal set -o nounset: Uji dengandeclare foo bar=; set -o nounset; echo $bar; echo $foo
xebeche

@joehanna Terima kasih telah menangkap yang hilang /! Saya memperbaikinya, tapi saya pikir logikanya cukup membingungkan untuk menjamin suatu fungsi, terutama mengingat komentar @ xebeche. @ xebeche Itu tidak nyaman untuk jawaban saya.… Sepertinya saya harus mencoba sesuatu seperti declare -p "$1" | grep =atau test -v "$1".
Mark Haferkamp

19

Bagi mereka yang ingin memeriksa tidak disetel atau kosong ketika dalam skrip dengan set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

Pemeriksaan reguler [ -z "$var" ]akan gagal var; unbound variablejika set -utetapi [ -z "${var-}" ] berkembang menjadi string kosong jika vartidak disetel tanpa gagal.


Anda kehilangan titik dua. Seharusnya ${var:-}tidak ${var-}. Tapi di Bash, saya bisa memastikan itu berfungsi bahkan tanpa tanda titik dua.
Palec

3
${var:-}dan ${var-}berarti hal yang berbeda. ${var:-}akan diperluas ke string kosong jika vartidak disetel atau null dan ${var-}akan diperluas ke string kosong hanya jika tidak disetel.
RubenLaguna

Baru belajar sesuatu yang baru, terima kasih! Menghilangkan titik dua menghasilkan tes hanya untuk parameter yang tidak disetel. Dengan kata lain, jika titik dua dimasukkan, operator menguji keberadaan parameter dan nilainya tidak nol; jika titik dua dihilangkan, operator hanya menguji keberadaannya. Tidak ada bedanya di sini, tetapi baik untuk diketahui.
Palec

17

Anda ingin keluar jika tidak disetel

Ini berhasil untuk saya. Saya ingin skrip saya keluar dengan pesan kesalahan jika parameter tidak disetel.

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

Ini kembali dengan kesalahan saat dijalankan

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

Periksa saja, tidak ada jalan keluar - Kosong dan Tidak diset adalah INVALID

Coba opsi ini jika Anda hanya ingin memeriksa apakah nilai yang diset = VALID atau tidak disetel / kosong = INVALID.

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Atau, Bahkan tes singkat ;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

Periksa saja, tidak ada jalan keluar - Hanya kosong yang tidak valid

Dan ini adalah jawaban untuk pertanyaan itu. Gunakan ini jika Anda hanya ingin memeriksa apakah nilai set / kosong = VALID atau tidak diset = INVALID.

CATATAN, "1" dalam "..- 1}" tidak signifikan, bisa berupa apa saja (seperti x)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Tes singkat

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

Saya mendedikasikan jawaban ini untuk @ mklement0 (komentar) yang menantang saya untuk menjawab pertanyaan secara akurat.

Referensi http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02


15

Untuk memeriksa apakah suatu variabel diatur dengan nilai yang tidak kosong, gunakan [ -n "$x" ], seperti yang telah ditunjukkan orang lain.

Sebagian besar waktu, itu adalah ide yang baik untuk memperlakukan variabel yang memiliki nilai kosong dengan cara yang sama dengan variabel yang tidak disetel. Tetapi Anda dapat membedakan keduanya jika Anda perlu: [ -n "${x+set}" ]( "${x+set}"memperluas ke setjika xdiatur dan ke string kosong jika xtidak disetel).

Untuk memeriksa apakah suatu parameter telah dilewati, uji $#, yang merupakan jumlah parameter yang dilewatkan ke fungsi (atau ke skrip, ketika tidak dalam suatu fungsi) (lihat jawaban Paul ).


14

Baca bagian "Ekspansi Parameter" pada bashhalaman manual. Ekspansi parameter tidak memberikan tes umum untuk variabel yang ditetapkan, tetapi ada beberapa hal yang dapat Anda lakukan untuk parameter jika tidak diatur.

Sebagai contoh:

function a {
    first_arg=${1-foo}
    # rest of the function
}

akan diset first_argsama dengan $1jika ditugaskan, jika tidak menggunakan nilai "foo". Jika abenar-benar harus mengambil parameter tunggal, dan tidak ada default yang baik, Anda dapat keluar dengan pesan kesalahan ketika tidak ada parameter yang diberikan:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(Perhatikan penggunaan :sebagai perintah nol, yang hanya memperluas nilai argumennya. Kami tidak ingin melakukan apa pun dengan $1contoh ini, keluar saja jika tidak disetel)


1
Saya bingung bahwa tidak ada jawaban lain yang menyebutkan yang sederhana dan elegan : ${var?message}.
tripleee

Dari mana Anda mengambil ini? Hebat! Itu hanya berfungsi! Dan pendek dan elegan! Terima kasih!
Roger

13

Dalam bash, Anda dapat menggunakan -vdi dalam [[ ]]builtin:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR

8

Menggunakan [[ -z "$var" ]]adalah cara termudah untuk mengetahui apakah suatu variabel disetel atau tidak, tetapi opsi -zitu tidak membedakan antara variabel yang tidak disetel dan variabel yang disetel ke string kosong:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

Cara terbaik untuk memeriksanya sesuai dengan jenis variabel: variabel env, parameter atau variabel biasa.

Untuk variabel env:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

Untuk parameter (misalnya, untuk memeriksa keberadaan parameter $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

Untuk variabel reguler (menggunakan fungsi bantu, lakukan dengan cara yang elegan):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

Catatan:

  • $#: memberi Anda jumlah parameter posisi.
  • declare -p: memberi Anda definisi variabel yang diteruskan sebagai parameter. Jika ada, kembalikan 0, jika tidak, kembalikan 1 dan cetak pesan kesalahan.
  • &> /dev/null: menekan output dari declare -ptanpa mempengaruhi kode pengembaliannya.

5

Jawaban di atas tidak berfungsi ketika opsi Bash set -udiaktifkan. Juga, mereka tidak dinamis, misalnya, bagaimana menguji variabel dengan nama "dummy" didefinisikan? Coba ini:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

Terkait: Di Bash, bagaimana saya menguji jika variabel didefinisikan dalam mode "-u"


1
Saya ingin tahu banyak mengapa jawaban ini dibatalkan ... Sangat bagus untuk saya.
LavaScornedOven

Dalam Bash, Anda bisa melakukan hal yang sama tanpa eval: perubahan eval "[ ! -z \${$1:-} ]"evaluasi tidak langsung: [ ! -z ${!1:-} ];.

@BinaryZebra: Ide yang menarik. Saya sarankan Anda memposting sebagai jawaban terpisah.
kevinarpe

Menggunakan "eval" membuat solusi ini rentan terhadap injeksi kode: $ is_var_defined 'x}]; gema "gotcha"> & 2; # '
Gotcha


4

Cara pilihan saya adalah ini:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

Jadi pada dasarnya, jika suatu variabel diset, itu menjadi "negasi dari yang dihasilkan false" (apa yang akan true= "diatur").

Dan, jika tidak disetel, itu akan menjadi "negasi dari hasil true" (sebagaimana hasil kosong dievaluasi true) (jadi akan berakhir sebagai false= "TIDAK diatur").



1

Dalam sebuah shell Anda dapat menggunakan -zoperator yang Benar jika panjang string adalah nol.

Satu MY_VARgaris sederhana untuk menetapkan default jika tidak disetel, jika tidak, Anda dapat menampilkan pesan:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."

0
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi

$1, $2dan hingga $Nselalu diatur. Maaf, ini gagal.

Saya mencobanya, dan itu tidak berhasil, mungkin versi bash saya kurang mendukung untuk itu. Idk, maaf kalau saya salah. :)

0

Saya menemukan (banyak) kode yang lebih baik untuk melakukan ini jika Anda ingin memeriksa apa pun di $@.

jika [[$ 1 = ""]]
kemudian
  gema '$ 1 kosong'
lain
  gema '$ 1 terisi'
fi

Kenapa ini semua? Semua yang $@ada di Bash, tetapi secara default kosong, jadi test -zdan test -ntidak dapat membantu Anda.

Pembaruan: Anda juga dapat menghitung jumlah karakter dalam suatu parameter.

if [$ {# 1} = 0]
kemudian
  gema '$ 1 kosong'
lain
  gema '$ 1 terisi'
fi

Aku tahu, tapi hanya itu. Jika ada sesuatu yang kosong Anda akan mendapatkan kesalahan! Maaf. : P

0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

11
Selamat datang di StackOverflow. Sementara jawaban Anda dihargai, yang terbaik adalah memberikan lebih dari sekadar kode. Bisakah Anda menambahkan alasan untuk jawaban Anda atau penjelasan kecil? Lihat halaman ini untuk ide-ide lain tentang memperluas jawaban Anda.
Celeo

0

Inilah yang saya gunakan setiap hari:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set()
{
    [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"
}

Ini berfungsi dengan baik di Linux dan Solaris hingga bash 3.0.

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1

1
Karena Bash 2.0+ Ekspansi tidak langsung tersedia. Anda dapat mengganti panjang dan kompleks (dengan eval juga termasuk) test -n "$(eval "echo "\${${1}+x}"")"untuk yang setara:[[ ${!1+x} ]]

0

Saya suka fungsi bantu untuk menyembunyikan detail kasar bash. Dalam hal ini, melakukannya menambah lebih banyak kekasaran (tersembunyi):

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

Karena saya pertama kali memiliki bug dalam implementasi ini (terinspirasi oleh jawaban Jens dan Lionel), saya datang dengan solusi yang berbeda:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

Saya merasa lebih lurus ke depan, lebih malu dan lebih mudah dipahami / diingat. Uji kasus menunjukkan itu setara:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

Tes kasus juga menunjukkan bahwa local vartidak TIDAK menyatakan var (kecuali jika diikuti oleh '='). Untuk beberapa waktu saya pikir saya mendeklarasikan variabel dengan cara ini, hanya untuk mengetahui sekarang bahwa saya hanya menyatakan niat saya ... Ini adalah no-op, saya kira.

IsDeclared_Tricky xyz: 1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky bar: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared foo: 1
IsDeclared baz: 0

BONUS: usecase

Saya kebanyakan menggunakan tes ini untuk memberikan (dan mengembalikan) parameter ke fungsi dengan cara yang agak "elegan" dan aman (hampir menyerupai antarmuka ...):

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

Jika dipanggil dengan semua, variabel harus dideklarasikan:

Halo Dunia!

lain:

Kesalahan: variabel tidak dideklarasikan: outputStr


0

Setelah membaca semua jawaban, ini juga berfungsi:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"

jika Anda tidak meletakkan SOME_VARapa yang saya miliki $SOME_VAR, itu akan menetapkannya ke nilai kosong; $diperlukan untuk ini berfungsi.


tidak akan berfungsi saat Anda memiliki set -uefek yang akan mencetak unboud variablekesalahan
Michael Heuberger

0

Ringkasan

  • Gunakan test -n "${var-}"untuk memeriksa apakah variabel tidak kosong (dan karenanya harus didefinisikan / ditetapkan juga)
  • Gunakan test ! -z "${var+}"untuk memeriksa apakah variabel didefinisikan / disetel (meskipun kosong)

Perhatikan bahwa use case pertama jauh lebih umum di skrip shell dan ini adalah apa yang biasanya ingin Anda gunakan.

Catatan

  • Solusi ini harus bekerja di semua shell POSIX (sh, bash, zsh, ksh, dash)
  • Beberapa jawaban lain untuk pertanyaan ini benar tetapi mungkin membingungkan bagi orang yang tidak berpengalaman dalam skrip shell, jadi saya ingin memberikan jawaban TLDR yang paling tidak membingungkan bagi orang-orang seperti itu.

Penjelasan

Untuk memahami bagaimana solusi ini bekerja, Anda perlu memahami POSIX testperintah dan perluasan parameter POSIX shell ( spek ), jadi mari kita menutupi dasar-dasar mutlak diperlukan untuk memahami jawabannya.

Perintah tes mengevaluasi ekspresi dan mengembalikan benar atau salah (melalui status keluarnya). Operator -nmengembalikan true jika operan adalah string yang tidak kosong. Jadi misalnya, test -n "a"mengembalikan true, sementara test -n ""mengembalikan false. Sekarang, untuk memeriksa apakah suatu variabel tidak kosong (yang artinya harus didefinisikan), Anda dapat menggunakan test -n "$var". Namun, beberapa skrip shell memiliki set opsi ( set -u) yang menyebabkan referensi apa pun untuk variabel yang tidak terdefinisi untuk memunculkan kesalahan, jadi jika variabel vartidak didefinisikan, ekspresi $aakan menyebabkan kesalahan. Untuk menangani kasus ini dengan benar, Anda harus menggunakan ekspansi variabel, yang akan memberi tahu shell untuk mengganti variabel dengan string alternatif jika tidak ditentukan, menghindari kesalahan yang disebutkan di atas.

Ekspansi variabel ${var-}berarti: jika variabel vartidak terdefinisi (juga disebut "tidak disetel"), ganti dengan string kosong. Jadi test -n "${var-}"akan mengembalikan true jika $vartidak kosong, yang hampir selalu Anda ingin memeriksa skrip shell. Pemeriksaan sebaliknya, jika $vartidak ditentukan atau tidak kosong, akan menjadi test -z "${var-}".

Sekarang untuk use case kedua: memeriksa apakah variabel vardidefinisikan, apakah kosong atau tidak. Ini adalah kasus penggunaan yang kurang umum dan sedikit lebih kompleks, dan saya akan menyarankan Anda untuk membaca jawaban bagus Lionels untuk lebih memahaminya.


-1

Untuk memeriksa apakah var diatur atau tidak

var=""; [[ $var ]] && echo "set" || echo "not set"

3
Ini akan gagal jika $vardiatur ke string kosong.
HelloGoodbye

-1

Jika Anda ingin menguji apakah suatu variabel terikat atau tidak terikat, ini berfungsi dengan baik, bahkan setelah Anda mengaktifkan opsi nounset:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi

Saya pikir maksud Anda set -o nounset, bukan set -o noun set. Ini hanya berfungsi untuk variabel yang telah exportdiedit. Ini juga mengubah pengaturan Anda dengan cara yang aneh untuk dibatalkan.
Keith Thompson
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.