Uji apakah beberapa variabel ditetapkan


19

Saya ingin memastikan bahwa pada titik tertentu skrip, setelah sourcememasukkan file konfigurasi, beberapa variabel ditetapkan dan, jika tidak, untuk menghentikan eksekusi, memberi tahu pengguna tentang variabel yang hilang. saya telah mencoba

for var in $one $two $three ; do
    ...

tetapi jika misalnya $twotidak disetel, loop tidak pernah dieksekusi untuk $two. Hal berikutnya yang saya coba adalah

for var in one two three ; do
    if [ -n ${!var} ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Tetapi jika dua tidak diatur, saya masih mendapatkan "dua diatur ke" bukannya "dua tidak diatur".

Bagaimana saya bisa memastikan bahwa semua variabel yang diperlukan ditetapkan?

Pembaruan / Solusi: Saya tahu bahwa ada perbedaan antara "set" dan "set, tetapi kosong". Saya sekarang menggunakan (terima kasih kepada /programming//a/16753536/3456281 dan jawaban untuk pertanyaan ini) berikut ini:

if [ -n "${!var:-}" ] ; then

jadi, jika vardisetel tetapi kosong, itu masih dianggap tidak valid.


1
Anda juga dapat menambahkan set -uke awal skrip Anda untuk segera menghentikannya ketika variabel yang tidak disetel digunakan.
n.st

Jawaban:


8

Mengutip kesalahan.

if [ -n "${!var}" ] ; then

Untuk masa depan: Pengaturan

set -x

sebelum menjalankan kode akan menunjukkan Anda masalah. Alih-alih menambahkan itu ke kode Anda dapat memanggil skrip Anda dengan

bash -vx ./my/script.sh

Ini berfungsi, tetapi apa yang terjadi dengan / tanpa tanda kutip? Apakah pendekatan umum saya benar pada awalnya?
Jasper

Ya, itu prekognitif: Saya menjawab pertanyaan sebelum ditanya ... 8-)
Hauke ​​Laging

4
@Jasper Anda harus selalu mengutip variabel. Itu tidak memerlukan biaya lebih banyak waktu daripada memikirkan apakah ini perlu setiap kali. Tapi itu menghindari kesalahan.
Hauke ​​Laging

Sebaliknya, mengutip hanya melindungi terhadap ekspansi. Jika ekspansi adalah hal yang Anda minati, mengutip tentu bukan cara yang tepat. Misalnya: cs = 673.290.765,; set - $ (IFS =,; echo $ cs); echo $ #; output: 3
mikeserv

2
Hanya @mikeserv Kutipan tunggal melindungi terhadap ekspansi apa yang saya jelas tidak menyarankan. Kutipan ganda melindungi terhadap pemisahan kata. Saya tidak menyangkal bahwa mungkin ada kasus di mana mengutip harus dihilangkan. Tapi itu bukan argumen yang menentang aturan umum saya. Orang macam apa yang akan menggunakan sesuatu seperti set -- $(IFS=, ; echo $cs)? Jenis orang yang harus bertanya di sini mengapa if [ -n ${!var} ] ; thentidak bekerja? Mungkin tidak.
Hauke ​​Laging

5

Satu-satunya yang Anda butuhkan adalah kutipan dalam tes Anda:

for var in one two three ; do
    if [ -n "${!var}" ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Bekerja untukku.


4

Jika Anda ingin program berhenti:

N= 
${one?var 1 is unset} 
${two:?var 2 is unset or null}
${three:+${N:?var 3 is set and not null}}

Itu akan melakukan triknya. Setiap pesan setelah tanda tanya dicetak stderrdan shell induknya mati. Baiklah, jadi tidak setiap pesan - hanya satu - hanya pesan pertama yang gagal mencetak pesan yang menyebabkan shell mati. Saya suka menggunakan tes ini seperti ini:

( for v in "$one" "$two" "$three" ; do
    i=$((i+1)) ; : ${v:?var $i is unset or null...} 
done ) || _handle_it

Saya punya banyak hal untuk dikatakan tentang ini di sini .


2

Kamu bisa menambahkan

set -u

ke awal skrip Anda untuk menghentikannya ketika mencoba menggunakan variabel yang tidak disetel.

Script seperti

#!/bin/sh
set -u
echo $foo

akan menghasilkan

script.sh: 3: script.sh: foo: parameter tidak disetel

Jika Anda menggunakan bashsebagai gantinya, kesalahan akan terlihat seperti ini:

script.sh: baris 3: foo: variabel tidak terikat


Dan menurut Anda ini masuk akal untuk menjalankan pemeriksaan waktu?
Hauke ​​Laging

2
@ HaukeLaging Saya tidak cukup mengikuti Anda - set -umencegah persis jenis kesalahan OP mencoba untuk menghindari dan (bertentangan dengan semua solusi lain) tidak terbatas pada serangkaian variabel tertentu. Ini sebenarnya merupakan tindakan pencegahan yang berguna bagi hampir semua skrip shell untuk membuatnya gagal dengan aman daripada melakukan hal-hal yang tidak terduga ketika suatu variabel tidak disetel. Artikel ini adalah referensi praktis tentang topik ini.
n.st

@ n.st saya tidak setuju - null bisa sama bermanfaatnya dengan nilai bukan nol jika Anda berencana untuk itu.
mikeserv

1
@ n.st Ini tidak masuk akal untuk OP karena dia tidak ingin perlindungan terhadap mengakses variabel yang tidak disetel (BTW: satu set tetapi variabel kosong akan menyebabkan kesalahan yang sama tetapi juga tidak bereaksi terhadap "perlindungan" Anda). Dia ingin memeriksa waktu berjalan apakah variabel tidak disetel / kosong. Saran Anda mungkin bermanfaat sebagai bantuan pengembangan umum tetapi tidak menyelesaikan masalah OP.
Hauke ​​Laging

set -udapat digunakan juga saya pikir, tetapi tidak sefleksibel ekspansi parameter yang tepat (lihat pertanyaan saya yang diperbarui). Dan dengan set -usaya harus membuat dummy-akses ke semua variabel target jika saya ingin memeriksa keberadaan mereka di satu tempat.
Jasper

2

Sebuah solusi yang ramah maksimum menguji semua persyaratan, dan melaporkannya bersama-sama, daripada gagal pada yang pertama dan membutuhkan bolak-balik untuk memperbaiki keadaan:

#!/bin/bash

required_vars=(one two three)

missing_vars=()
for i in "${required_vars[@]}"
do
    test -n "${!i:+y}" || missing_vars+=("$i")
done
if [ ${#missing_vars[@]} -ne 0 ]
then
    echo "The following variables are not set, but should be:" >&2
    printf ' %q\n' "${missing_vars[@]}" >&2
    exit 1
fi

Saya menggunakan variabel array untuk melacak variabel mana yang belum ditetapkan, dan menggunakan hasilnya untuk membuat pesan yang menghadap pengguna.

Catatan:

  • Saya mengutip ${required_vars[@]}dalam forloop terutama karena kebiasaan - saya tidak akan menyarankan termasuk metacharacters shell dalam nama variabel Anda!
  • Saya tidak mengutip ${#missing_vars[@]}, karena itu selalu bilangan bulat, bahkan jika Anda cukup sesat untuk mengabaikan saran sebelumnya.
  • Saya menggunakan %qsaat mencetak; %sbiasanya akan cukup.
  • Output kesalahan selalu menuju ke stream kesalahan dengan >&2, sehingga tidak bisa disalurkan ke perintah hilir
  • Diam adalah emas - jangan cetak informasi kemajuan atau info debug kecuali diminta secara khusus. Itu membuat kesalahan lebih jelas.

1

bash4.2 memungkinkan Anda menguji apakah suatu variabel diatur dengan -voperator; variabel yang tidak disetel dan variabel yang disetel ke string kosong adalah dua kondisi yang berbeda:

$ unset foo
$ [[ -v foo ]] && echo foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
$ foo=
$ [[ -v foo ]] && echo foo is set
foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty

Saya bertanya tentang variabel "banyak", jadi saya mencari sesuatu seperti tipuan ...
Jasper

Anda tidak perlu tipuan, karena -vmengambil nama dari variabel, sehingga for var in one two three; [[ -v $var ]] && ...; doneakan memeriksa apakah masing-masing one, twodan threediatur secara berurutan.
chepner

1

Saya pikir jika Anda maksud not set, jadi variabel tidak boleh diinisialisasi. Jika Anda menggunakan [ -n "${!var}" ], jadi variabel yang kosong seperti two=""akan gagal, sementara itu disetel . Anda dapat mencoba ini:

one=1
three=3

for var in one two three; do
  declare -p $var > /dev/null 2>&1 \
  && printf '%s is set to %s\n' "$var" "${!var}" \
  || printf '%s is not set\n' "$var"
done

0

Solusi ringkas (meskipun sedikit ruwet), untuk menangani kasus di mana tidak masalah bagi sourceskrip d untuk mengatur variabel ke nilai nol, adalah menginisialisasi variabel ke beberapa nilai khusus sebelum sumber skrip, dan kemudian memeriksa nilai itu setelah itu misalnya

one=#UNSET#
two=#UNSET#
three=#UNSET#

. set_vars_script

if [[ $one$two$three == *#UNSET#* ]] ; then
  echo "One or more essential variables are unset" >&2
  exit 1
fi

1
solusi bagus lain, tapi saya ingin memberikan pesan kesalahan yang lebih spesifik kemudian "satu atau lebih variabel tidak disetel" ...
Jasper

0

Saya telah memperpanjang jawaban @ mikeserv .

Versi ini mengambil daftar variabel untuk memeriksa keberadaan, mencetak nama variabel yang hilang dan memicu panggilan ke penggunaan () pada kesalahan.

REQD_VALUES=("VARIABLE" "FOO" "BAR" "OTHER_VARIABLE")
( i=0; for var_name in ${REQD_VALUES[@]}; do
    VALUE=${!var_name} ;
    i=$((i+1)) ; : ${VALUE:?$var_name is missing}
done ) || usage

Contoh output ketika nilai hilang:

./myscript.sh: line 42: VALUE: OTHER_VARIABLE is missing

Perhatikan bahwa Anda dapat mengubah variabel yang disebut VALUE ke nama alternatif yang lebih sesuai dengan output yang Anda inginkan.

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.