Menggunakan unset vs. menyetel variabel menjadi kosong

108

Saat ini saya sedang menulis kerangka pengujian bash, di mana dalam fungsi pengujian, baik pengujian bash standar ( [[) serta pencocokan yang telah ditentukan sebelumnya dapat digunakan. Pencocokan adalah pembungkus untuk '[[' dan selain mengembalikan kode kembali, setel beberapa pesan bermakna yang mengatakan apa yang diharapkan.

Contoh:

string_equals() {
    if [[ ! $1 = $2 ]]; then
            error_message="Expected '$1' to be '$2'."

            return 1
    fi
}

Jadi, saat matcher digunakan, dan gagal, baru error_message ditetapkan.

Sekarang, di beberapa titik kemudian, saya menguji apakah tes tersebut berhasil. Jika berhasil, saya cetak ekspektasi dengan warna hijau, jika gagal dengan warna merah.

Selain itu, mungkin ada kumpulan error_message, jadi saya menguji apakah ada pesan, mencetaknya, dan kemudian membatalkannya (karena pengujian berikut mungkin tidak menetapkan error_message):

if [[ $error_message ]]; then
    printf '%s\n' "$error_message"

    unset -v error_message
fi

Sekarang pertanyaan saya adalah, apakah lebih baik untuk menghapus variabel, atau hanya mengaturnya ke '', seperti

error_message=''

Mana yang lebih baik? Apakah itu benar-benar membuat perbedaan? Atau mungkin saya harus memiliki bendera tambahan yang menunjukkan bahwa pesan tersebut telah disetel?

metode pembantu
sumber
1
Jika Anda tidak pernah membandingkan error_messagedengan hal lain, saya akan mengatakan itu tidak masalah. Namun, saya pikir Anda menginginkannya [[ $error_message ]], jika tidak Anda menguji bahwa string literal "error_message" ada.
chepner
@chepner Ya, itu salah ketik. Diperbaiki.
helpermethod

Jawaban:

143

Sebagian besar Anda tidak melihat perbedaan, kecuali Anda menggunakan set -u:

/home/user1> var=""
/home/user1> echo $var

/home/user1> set -u
/home/user1> echo $var

/home/user1> unset var
/home/user1> echo $var
-bash: var: unbound variable

Jadi sungguh, itu tergantung pada bagaimana Anda akan menguji variabel.

Saya akan menambahkan bahwa cara pengujian yang saya sukai jika disetel adalah:

[[ -n $var ]]  # True if the length of $var is non-zero

atau

[[ -z $var ]]  # True if zero length
cdarke.dll
sumber
43
var=tidak "tidak disetel". Itu hanya string kosong yang tidak diberi tanda kutip. Selain itu set -u, berbagai bentuk perluasan parameter bash juga dapat membedakan antara nilai tidak disetel dan null: ${foo:bar}meluas ke "bar" jika footidak disetel, tetapi "" saat foonull, sementara ${foo:-bar}meluas ke "bar" jika foo tidak disetel atau null.
chepner
1
[[ -n $var ]]bernilai false jika vardisetel ke string kosong.
chepner
1
jika Anda menggunakan 'mendeklarasikan -p' untuk memeriksa variabel 'keberadaan' maka var = akan menampilkan 'mendeklarasikan - var = ""' Anda harus menggunakan unset untuk menyingkirkannya, tentu saja jika itu adalah variabel hanya-baca maka Anda tidak bisa mendapatkannya menyingkirkannya tentu saja. Selain itu, jika Anda var = sesuatu di dalam fungsi, Anda tidak perlu khawatir untuk menghapusnya jika Anda menggunakan "local var = value", berhati-hatilah, karena "mendeklarasikan var = value" juga bersifat lokal. Dalam kasus mendeklarasikan Anda harus secara eksplisit menyetelnya secara global menggunakan "mendeklarasikan -g var = value", tanpa mendeklarasikan Anda harus secara eksplisit menyetelnya menjadi lokal menggunakan "local". Variabel global hanya dihapus / tidak disetel.
osirisgothra
@osirisgothra: poin bagus tentang variabel lokal. Tentu saja secara umum yang terbaik adalah mendeklarasikan (atau melokalkan) semua variabel dalam suatu fungsi sehingga dienkapsulasi.
cdarke
3
@chepner seharusnya ${foo-bar}bukan ${foo:bar}. tes sendiri:unset a; echo ">${a:-foo}-${a:foo}-${a-foo}<"
Liviu Chircu
17

Seperti yang telah dikatakan, penggunaan unset juga berbeda dengan array

$ foo=(4 5 6)

$ foo[2]=

$ echo ${#foo[*]}
3

$ unset foo[2]

$ echo ${#foo[*]}
2
Steven Penny
sumber
2

Jadi, dengan membatalkan pengaturan indeks array 2, Anda pada dasarnya menghapus elemen tersebut dalam array dan mengurangi ukuran array (?).

Saya membuat tes sendiri ..

foo=(5 6 8)
echo ${#foo[*]}
unset foo
echo ${#foo[*]}

Yang mengakibatkan..

3
0

Jadi hanya untuk mengklarifikasi bahwa menghapus seluruh array sebenarnya akan menghapusnya seluruhnya.

PdC
sumber
0

Berdasarkan komentar di atas, berikut adalah tes sederhana:

isunset() { [[ "${!1}" != 'x' ]] && [[ "${!1-x}" == 'x' ]] && echo 1; }
isset()   { [ -z "$(isunset "$1")" ] && echo 1; }

Contoh:

$ unset foo; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's unset
$ foo=     ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
$ foo=bar  ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
Jonathan H.
sumber