Apa cara singkat untuk memeriksa bahwa variabel lingkungan diatur dalam skrip Unix shell?


500

Saya punya beberapa skrip shell Unix di mana saya perlu memeriksa bahwa variabel lingkungan tertentu diatur sebelum saya mulai melakukan hal-hal, jadi saya melakukan hal semacam ini:

if [ -z "$STATE" ]; then
    echo "Need to set STATE"
    exit 1
fi  

if [ -z "$DEST" ]; then
    echo "Need to set DEST"
    exit 1
fi

yang banyak mengetik. Apakah ada idiom yang lebih elegan untuk memeriksa bahwa satu set variabel lingkungan diatur?

EDIT: Saya harus menyebutkan bahwa variabel-variabel ini tidak memiliki nilai default yang berarti - skrip harus keluar jika ada yang tidak disetel.


2
Banyak jawaban untuk pertanyaan ini terasa seperti sesuatu yang akan Anda lihat di Code Golf Stack Exchange .
Daniel Schilling

Jawaban:


587

Ekspansi Parameter

Jawaban yang jelas adalah menggunakan salah satu bentuk khusus dari ekspansi parameter:

: ${STATE?"Need to set STATE"}
: ${DEST:?"Need to set DEST non-empty"}

Atau, lebih baik (lihat bagian 'Posisi kutipan ganda' di bawah):

: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"

Varian pertama (hanya menggunakan ?) membutuhkan NEGARA yang akan ditetapkan, tetapi NEGARA = "" (string kosong) OK - tidak persis apa yang Anda inginkan, tetapi alternatif dan notasi yang lebih tua.

Varian kedua (menggunakan :?) membutuhkan DEST untuk diatur dan tidak kosong.

Jika Anda tidak memberikan pesan, shell menyediakan pesan default.

The ${var?}membangun portabel kembali ke Versi 7 UNIX dan Bourne Shell (1978 atau sekitar itu). The ${var:?}membangun sedikit lebih baru: Saya pikir itu di Sistem III UNIX sekitar tahun 1981, tetapi mungkin telah di PWB UNIX sebelum itu. Oleh karena itu di Shell Korn, dan di shell POSIX, termasuk Bash khusus.

Biasanya didokumentasikan dalam halaman manual shell di bagian yang disebut Parameter Expansion . Misalnya, bashmanual mengatakan:

${parameter:?word}

Tampilan Galat jika Null atau Unset. Jika parameter nol atau tidak disetel, perluasan kata (atau pesan ke efek itu jika kata tidak ada) ditulis ke kesalahan standar dan shell, jika tidak interaktif, keluar. Jika tidak, nilai parameter diganti.

Perintah Colon

Saya mungkin harus menambahkan bahwa perintah titik dua hanya memiliki argumen dievaluasi dan kemudian berhasil. Ini adalah notasi komentar shell asli (sebelum ' #' ke akhir baris). Untuk waktu yang lama, skrip Bourne shell memiliki tanda titik dua sebagai karakter pertama. C Shell akan membaca skrip dan menggunakan karakter pertama untuk menentukan apakah itu untuk C Shell ( #hash ') atau Bourne shell (a' :'colon). Kemudian kernel ikut serta dan menambahkan dukungan untuk ' #!/path/to/program' dan Bourne shell mendapat ' #' komentar, dan konvensi usus besar berjalan di pinggir jalan. Tetapi jika Anda menemukan skrip yang dimulai dengan titik dua, sekarang Anda akan tahu mengapa.


Posisi tanda kutip ganda

blong bertanya dalam komentar :

Ada pemikiran tentang diskusi ini? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749

Inti dari diskusi ini adalah:

... Namun, ketika saya shellcheckmenggunakannya (dengan versi 0.4.1), saya menerima pesan ini:

In script.sh line 13:
: ${FOO:?"The environment variable 'FOO' must be set and non-empty"}
  ^-- SC2086: Double quote to prevent globbing and word splitting.

Adakah saran tentang apa yang harus saya lakukan dalam kasus ini?

Jawaban singkatnya adalah "lakukan seperti yang shellcheckdisarankan":

: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"

Untuk mengilustrasikan alasannya, pelajarilah yang berikut ini. Perhatikan bahwa :perintah tersebut tidak menggemakan argumennya (tetapi shell mengevaluasi argumennya). Kami ingin melihat argumen, jadi kode di bawah ini digunakan printf "%s\n"sebagai pengganti :.

$ mkdir junk
$ cd junk
$ > abc
$ > def
$ > ghi
$ 
$ x="*"
$ printf "%s\n" ${x:?You must set x}    # Careless; not recommended
abc
def
ghi
$ unset x
$ printf "%s\n" ${x:?You must set x}    # Careless; not recommended
bash: x: You must set x
$ printf "%s\n" "${x:?You must set x}"  # Careful: should be used
bash: x: You must set x
$ x="*"
$ printf "%s\n" "${x:?You must set x}"  # Careful: should be used
*
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
abc
def
ghi
$ x=
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
bash: x: You must set x
$ unset x
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
bash: x: You must set x
$ 

Perhatikan bagaimana nilai dalam $xdiperluas menjadi pertama *dan kemudian daftar nama file ketika ekspresi keseluruhan tidak dalam tanda kutip ganda. Inilah yang shellcheckmerekomendasikan harus diperbaiki. Saya belum memverifikasi bahwa itu tidak keberatan dengan bentuk di mana ekspresi dilampirkan dalam tanda kutip ganda, tetapi itu adalah asumsi yang masuk akal bahwa itu akan baik-baik saja.


2
Itu yang saya butuhkan. Saya telah menggunakan berbagai versi Unix sejak 1987 dan saya belum pernah melihat sintaks ini - hanya menunjukkan ...
AndrewR

Bagaimana cara kerjanya dan di mana dokumen itu didokumentasikan? Saya bertanya-tanya apakah itu dapat dimodifikasi untuk memeriksa variabel yang ada dan diatur ke nilai tertentu.
jhabbott

6
Itu didokumentasikan di halaman manual shell, atau manual Bash, biasanya di bawah header 'Parameter Expansion'. Cara standar untuk memeriksa apakah variabel ada dan diatur ke nilai tertentu (mengatakan 'abc') hanya: if [ "$variable" = "abc" ]; then : OK; else : variable is not set correctly; fi.
Jonathan Leffler

Bagaimana cara menggunakan sintaks ini dengan variabel string dengan spasi di dalamnya. Saya mendapatkan kesalahan yang mengatakan "Ini: perintah tidak ditemukan" ketika saya mengatur variabel saya ingin memeriksa ke "Ini adalah tes". Ada ide?
user2294382

1
Ada pemikiran tentang diskusi ini? github.com/koalaman/shellcheck/issues/…
blong

138

Coba ini:

[ -z "$STATE" ] && echo "Need to set STATE" && exit 1;

8
Itu agak bertele-tele. Semi-kolon adalah redundan.
Jonathan Leffler

3
Atau bahkan lebih singkat [ -z "$STATE" ] && { echo "Need to set STATE"; exit 1; } lihat posting blog ini
zipizap

36
Apa itu, bisa dibaca. Saya semua untuk yang ini. Skrip Bash membutuhkan semua bantuan yang bisa mereka dapatkan di departemen itu!
Iain Duncan

jawaban aslinya lebih konsisten dalam sintaksisnya.
gigi cepat

4
Namun, ini tidak membedakan antara variabel yang tidak disetel dan variabel yang disetel ke string kosong.
chepner

56

Pertanyaan Anda tergantung pada shell yang Anda gunakan.

Bourne shell menyisakan sangat sedikit di jalan apa yang Anda cari.

TAPI...

Itu berhasil, hampir di mana-mana.

Cobalah dan menjauhlah dari csh. Itu bagus untuk lonceng dan peluit yang ditambahkannya, membandingkan cangkang Bourne, tetapi sekarang benar-benar berderit. Jika Anda tidak percaya, coba dan pisahkan STDERR dalam csh! (-:

Ada dua kemungkinan di sini. Contoh di atas, yaitu menggunakan:

${MyVariable:=SomeDefault}

untuk pertama kalinya Anda perlu merujuk ke $ MyVariable. Ini membutuhkan env. var MyVariable dan, jika saat ini tidak disetel, memberikan nilai SomeDefault ke variabel untuk digunakan nanti.

Anda juga memiliki kemungkinan:

${MyVariable:-SomeDefault}

yang hanya menggantikan SomeDefault untuk variabel tempat Anda menggunakan konstruk ini. Itu tidak menetapkan nilai SomeDefault ke variabel, dan nilai MyVariable masih akan nol setelah pernyataan ini ditemukan.


3
CSH: (foo> foo.out)> & foo.err
Mr.Ree

Shell Bourne melakukan apa yang diperlukan.
Jonathan Leffler

51

Tentunya pendekatan yang paling sederhana adalah menambahkan -usaklar ke shebang (baris di bagian atas skrip Anda), dengan asumsi Anda menggunakan bash:

#!/bin/sh -u

Ini akan menyebabkan skrip keluar jika ada variabel tidak mengikat yang bersembunyi di dalamnya.


40
${MyVariable:=SomeDefault}

Jika MyVariablediatur dan bukan nol, itu akan mengatur ulang nilai variabel (= tidak ada yang terjadi).
Lain, MyVariablediatur ke SomeDefault.

Di atas akan berusaha untuk mengeksekusi ${MyVariable}, jadi jika Anda hanya ingin mengatur variabel lakukan:

MyVariable=${MyVariable:=SomeDefault}

Ini tidak menghasilkan pesan - dan Q mengatakan tidak ada default (meskipun tidak mengatakan itu ketika Anda mengetik jawaban Anda).
Jonathan Leffler

10
Versi yang benar: (1): $ {MyVariable: = SomeDefault} atau (2): MyVariable = $ {MyVariable: -SomeDefault}
Jonathan Leffler

23

Menurut pendapat saya pemeriksaan paling sederhana dan paling kompatibel untuk #! / Bin / sh adalah:

if [ "$MYVAR" = "" ]
then
   echo "Does not exist"
else
   echo "Exists"
fi

Sekali lagi, ini untuk / bin / sh dan kompatibel juga pada sistem Solaris lama.


Ini 'berhasil', tetapi bahkan sistem Solaris yang lama memiliki /bin/shyang mendukung ${var:?}notasi, dll.
Jonathan Leffler

Jawaban terbaik sejauh ini.
Ole

4
Diatur ke string kosong berbeda dari tidak diatur sama sekali. Tes ini akan mencetak "Tidak ada" setelah keduanya unset MYVARdan MYVAR=.
chepner

@ chepner, saya meningkatkan komentar Anda untuk head-up yang mungkin berharga, tapi kemudian saya mengujinya (dengan bash), dan tidak bisa benar-benar mengatur var ke string kosong tanpa itu juga tidak disetel. Bagaimana Anda akan melakukannya?
Sz.

@ Jadi saya tidak yakin apa yang Anda maksud. "Tidak disetel" berarti nama tidak memiliki nilai sama sekali yang ditetapkan untuknya. Jika footidak disetel, "$foo"masih meluas ke string kosong.
chepner

17

bash4.2 memperkenalkan -voperator yang menguji apakah suatu nama diset ke nilai apa pun , bahkan string kosong.

$ unset a
$ b=
$ c=
$ [[ -v a ]] && echo "a is set"
$ [[ -v b ]] && echo "b is set"
b is set
$ [[ -v c ]] && echo "c is set"
c is set

16

Saya selalu menggunakan:

if [ "x$STATE" == "x" ]; then echo "Need to set State"; exit 1; fi

Tidak terlalu ringkas, aku takut.

Di bawah CSH Anda memiliki $? STATE.


2
Ini memeriksa apakah STATEkosong atau tidak disetel, bukan hanya tidak disetel.
chepner

7

Untuk orang-orang masa depan seperti saya, saya ingin melangkah maju dan parameter parameter nama var, jadi saya dapat mengulang daftar ukuran variabel nama variabel:

#!/bin/bash
declare -a vars=(NAME GITLAB_URL GITLAB_TOKEN)

for var_name in "${vars[@]}"
do
  if [ -z "$(eval "echo \$$var_name")" ]; then
    echo "Missing environment variable $var_name"
    exit 1
  fi
done

3

Kita dapat menulis pernyataan yang bagus untuk memeriksa banyak variabel sekaligus:

#
# assert if variables are set (to a non-empty string)
# if any variable is not set, exit 1 (when -f option is set) or return 1 otherwise
#
# Usage: assert_var_not_null [-f] variable ...
#
function assert_var_not_null() {
  local fatal var num_null=0
  [[ "$1" = "-f" ]] && { shift; fatal=1; }
  for var in "$@"; do
    [[ -z "${!var}" ]] &&
      printf '%s\n' "Variable '$var' not set" >&2 &&
      ((num_null++))
  done

  if ((num_null > 0)); then
    [[ "$fatal" ]] && exit 1
    return 1
  fi
  return 0
}

Doa contoh:

one=1 two=2
assert_var_not_null one two
echo test 1: return_code=$?
assert_var_not_null one two three
echo test 2: return_code=$?
assert_var_not_null -f one two three
echo test 3: return_code=$? # this code shouldn't execute

Keluaran:

test 1: return_code=0
Variable 'three' not set
test 2: return_code=1
Variable 'three' not set

Lebih banyak pernyataan seperti itu di sini: https://github.com/codeforester/base/blob/master/lib/assertions.sh



1

Tidak ada solusi di atas yang berfungsi untuk tujuan saya, sebagian karena saya memeriksa lingkungan untuk daftar variabel terbuka yang perlu diatur sebelum memulai proses yang panjang. Saya berakhir dengan ini:

mapfile -t arr < variables.txt

EXITCODE=0

for i in "${arr[@]}"
do
   ISSET=$(env | grep ^${i}= | wc -l)
   if [ "${ISSET}" = "0" ];
   then
      EXITCODE=-1
      echo "ENV variable $i is required."
   fi
done

exit ${EXITCODE}

-1

Alih-alih menggunakan skrip shell eksternal, saya cenderung memuat fungsi di shell login saya. Saya menggunakan sesuatu seperti ini sebagai fungsi pembantu untuk memeriksa variabel lingkungan daripada variabel yang ditetapkan:

is_this_an_env_variable ()
    local var="$1"
    if env |grep -q "^$var"; then
       return 0
    else
       return 1
    fi
 }

1
Ini salah. is_this_an_env_variable Pakan mengembalikan kesuksesan karena PATHada.
Eric

Itu ada karena merupakan variabel lingkungan. Apakah sudah diatur string nonqull eqaul adalah masalah lain.
nafdef

-7

The $?sintaks cukup rapi:

if [ $?BLAH == 1 ]; then 
    echo "Exists"; 
else 
    echo "Does not exist"; 
fi

Ini berfungsi, tetapi apakah ada orang di sini yang tahu di mana saya dapat menemukan dokumen di sintaks itu? $? biasanya nilai pengembalian sebelumnya.
Danny Staple

Buruk saya - ini tampaknya tidak berfungsi. Cobalah dalam skrip sederhana, dengan dan tanpa set itu.
Danny Staple

11
Dalam shell Bourne, Korn, Bash dan POSIX, $?adalah status keluar dari perintah sebelumnya; $?BLAHadalah string yang terdiri dari status keluar dari perintah sebelumnya yang disatukan dengan 'BLAH'. Ini tidak menguji variabel $BLAHsama sekali. (Ada desas-desus bahwa ia mungkin melakukan lebih atau kurang apa yang diperlukan csh, tetapi kerang-kerang laut sebaiknya ditinggalkan di pantai.)
Jonathan Leffler

Ini adalah jawaban yang sepenuhnya salah sejauh menyangkut Bash.
codeforester
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.