Unix kabuk betiğinde ortam değişkenlerinin ayarlandığını kontrol etmenin kısa bir yolu nedir?


500

Bir şeyler yapmaya başlamadan önce belirli ortam değişkenlerinin ayarlandığını kontrol etmem gereken birkaç Unix kabuk komut dosyası var, bu yüzden böyle bir şey yapıyorum:

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

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

bu da çok fazla yazım. Bir dizi ortam değişkeninin ayarlandığını kontrol etmek için daha zarif bir deyim var mı?

EDIT: Bu değişkenlerin anlamlı bir varsayılan değeri olduğunu belirtmek gerekir - herhangi bir unset varsa komut dosyası hata vermelidir.


2
Bu sorunun yanıtlarının çoğu, Code Golf Stack Exchange'de göreceğiniz bir şey gibi geliyor .
Daniel Schilling

Yanıtlar:


587

Parametre Genişletme

Açık cevap, parametre genişletmenin özel formlarından birini kullanmaktır:

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

Veya daha iyisi (aşağıdaki 'Çift tırnakların konumu' bölümüne bakın):

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

İlk varyant (sadece kullanarak ?) STATE ayarlanmasını gerektirir, ancak STATE = "" (boş bir dize) TAMAM - tam olarak ne istediğinizi değil, alternatif ve eski gösterimi.

İkinci varyant (kullanma :?) DEST'in ayarlanmasını ve boş olmamasını gerektirir.

Herhangi bir mesaj sağlamazsanız, kabuk varsayılan bir mesaj sağlar.

${var?}Yapı, Versiyon 7 UNIX ve Bourne Shell (1978 veya oralarda) taşınabilir geri döndü. ${var:?}Ben 1981 dolaylarında Sistem III UNIX içinde olduğunu düşünüyorum, ama bundan önce PWB UNIX içinde olabilir: yapı biraz daha yenidir. Bu nedenle Korn Shell'de ve özellikle Bash dahil POSIX mermilerinde.

Genellikle kabuğun kılavuz sayfasında Parametre Genişletme adlı bir bölümde belgelenir . Örneğin, bashel kitabı şöyle diyor:

${parameter:?word}

Null veya Unet ise Hata görüntüler. Parametre null veya unset ise, sözcüğün genişletilmesi (veya sözcük yoksa bu etkiye yönelik bir mesaj) standart hataya yazılır ve kabuk, etkileşimli değilse çıkar. Aksi takdirde, parametrenin değeri değiştirilir.

Kolon Komutanlığı

Muhtemelen iki nokta üst üste komutunun argümanlarını değerlendirdiğini ve başarılı olduğunu eklemeliyim. Orijinal kabuk yorum göstergesidir (' #' öncesinde satır sonuna kadar). Uzun bir süre, Bourne kabuk betiklerinde ilk karakter olarak iki nokta üst üste vardı. C Kabuğu bir komut dosyasını okur ve C Kabuğu (bir ' #' karma) veya Bourne kabuğu (bir ' :' iki nokta üst üste) için olup olmadığını belirlemek için ilk karakteri kullanır . Daha sonra çekirdek harekete geçti ve ' #!/path/to/program' ve Bourne kabuğu ' ' için destek ekledi #ve kolon konvansiyonu yol kenarına gitti. Ancak iki nokta üst üste ile başlayan bir senaryo ile karşılaşırsanız, şimdi nedenini bileceksiniz.


Çift tırnakların konumu

blong bir yorumda sordu :

Bu tartışma hakkında bir fikriniz var mı? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749

Tartışmanın özü:

… Ancak, ben shellcheck(0.4.1 sürümü ile), bu mesajı alıyorum:

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.

Bu durumda ne yapmam gerektiğine dair bir tavsiye var mı?

Kısa cevap " shellcheckönerdiği gibi yap":

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

Nedenini göstermek için aşağıdakileri inceleyin. :Komutun bağımsız değişkenlerini yinelemediğini unutmayın (ancak kabuk bağımsız değişkenleri değerlendirir). Argümanları görmek istiyoruz, bu yüzden aşağıdaki kod printf "%s\n"yerine kullanıyor :.

$ 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
$ 

İçindeki değerin önce nasıl $xgenişletildiğine *ve ardından genel ifade çift tırnak içinde olmadığında dosya adlarının bir listesine dikkat edin. shellcheckÖnerilmesi gereken budur . İfadenin çift tırnak içine alındığı forma itiraz etmediğini doğrulamamıştım, ancak iyi olacağı makul bir varsayım.


2
İhtiyacım olan şey bu. 1987'den beri Unix'in çeşitli versiyonlarını kullanıyorum ve bu sözdizimini hiç görmedim - göstermeye devam ediyor ...
AndrewR

Bu nasıl çalışıyor ve nerede belgeleniyor? Değişken var olup olmadığını kontrol etmek için değiştirilebilir ve belirli bir değere ayarlanmış merak ediyorum.
jhabbott

6
Kabuk kılavuzu sayfasında veya Bash kılavuzunda, genellikle 'Parametre Genişletme' başlığı altında belgelenmiştir. Standart yol değişken varsa ve belirli bir değere ayarlanır ( 'abc' demek) basitçe olup olmadığını kontrol etmek: if [ "$variable" = "abc" ]; then : OK; else : variable is not set correctly; fi.
Jonathan Leffler

Bu sözdizimini boşlukları olan bir dize değişkeni ile nasıl kullanabilirim. Kontrol etmek istediğim değişkeni "Bu bir sınama" olarak ayarladığımda "This: command not found" yazan bir hata alıyorum. Herhangi bir fikir?
user2294382

1
Bu tartışma hakkında bir fikriniz var mı? github.com/koalaman/shellcheck/issues/…
blong

138

Bunu dene:

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

8
Bu oldukça ayrıntılı. Yarı kolon gereksizdir.
Jonathan Leffler

3
Ya da daha kısa bu blog yazısı[ -z "$STATE" ] && { echo "Need to set STATE"; exit 1; } görmek
zipizap

36
Bu ne okunabilir. Ben hepiniz bunun için. Bash betiklerinin o bölümde alabilecekleri her türlü yardıma ihtiyacı var!
Iain Duncan

orijinal cevap sözdiziminde daha tutarlıdır.
hızlı diş

4
Ancak bu, ayarlanmamış bir değişken ile boş dizeye ayarlanmış bir değişken arasında ayrım yapmaz.
chepner

57

Sorunuz kullandığınız kabuğa bağlıdır.

Bourne kabuğu peşinde olduğunuz şeyin yolunda çok az bırakır.

FAKAT...

Hemen hemen her yerde çalışıyor.

Sadece denemek ve csh uzak kalmak. Bourne kabuğuna kıyasla eklediği çan ve ıslık için iyiydi, ama şimdi gerçekten gıcırdıyor. Bana inanmıyorsanız, STDERR'ı csh olarak ayırmaya çalışın! (-:

Burada iki olasılık var. Yukarıdaki örnek, yani:

${MyVariable:=SomeDefault}

ilk kez $ MyVariable'a başvurmanız gerekir. Bu env alır. var MyVariable ve şu anda ayarlanmamışsa daha sonra kullanmak üzere değişkene SomeDefault değerini atar.

Ayrıca şunları da yapabilirsiniz:

${MyVariable:-SomeDefault}

ki bu yapıyı kullandığınız değişkenin yerine SomeDefault yazın. Değişkene SomeDefault değerini atamaz ve MyVariable'ın değeri bu ifadeyle karşılaşıldıktan sonra da null olur.


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

Bourne kabuğu gerekli olanı yapar.
Jonathan Leffler

51

Şüphesiz en basit yaklaşım, kullandığınız -uvarsayılarak, shebang'a (komut dosyanızın üstündeki satır) geçiş eklemektir bash:

#!/bin/sh -u

Bu, ilişkisiz değişkenler içinde gizlenirse komut dosyasının çıkmasına neden olur.


40
${MyVariable:=SomeDefault}

Eğer MyVariablenull adlı kurmak ve edilmez, değişken değerini (= hiçbir şey olmaz) sıfırlar.
Else, MyVariableolarak ayarlanır SomeDefault.

Yukarıdakiler çalıştırmayı dener ${MyVariable}, bu yüzden do değişkenini ayarlamak istiyorsanız:

MyVariable=${MyVariable:=SomeDefault}

Bu, iletiyi oluşturmaz ve Q, varsayılan bir seçenek olmadığını söyler (buna rağmen, cevabınızı yazdığınızda bunu söylemedi).
Jonathan Leffler

10
Doğru sürümler: (1): $ {MyVariable: = SomeDefault} veya (2): MyVariable = $ {MyVariable: -SomeDefault}
Jonathan Leffler

23

Bence #! / Bin / sh için en basit ve en uyumlu kontrol :

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

Yine, bu / bin / sh içindir ve eski Solaris sistemleriyle de uyumludur.


Bu 'çalışıyor', ancak eski Solaris sistemleri bile gösterimi /bin/shdestekleyen bir sisteme sahiptir ${var:?}.
Jonathan Leffler

Şimdiye kadarki en iyi cevap.
Ole

4
Boş dizeye ayarlanmak hiç ayarlanmamaktan farklıdır. Bu test her ikisinden sonra unset MYVARve "Yok" yazdıracaktır MYVAR=.
chepner

@ chepner, muhtemelen değerli kafa kafaları için yorumunuzu onayladım, ancak daha sonra test etmeye devam ettim (bash ile) ve aslında ayarlanmadan boş bir dizeye bir var ayarlayamadım. Bunu nasıl yaptın?
Sz.

@Sz Ne demek istediğinden emin değilim. "Ayarsız", adın kendisine atanmış hiçbir değeri olmadığı anlamına gelir. Eğer fooayarlanmazsa, "$foo"hala boş dizeye genişler.
chepner

17

bash4.2 -vbir adın herhangi bir değere, boş dizeye bile ayarlanıp ayarlanmadığını test eden operatörü tanıttı .

$ 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

Her zaman kullandım:

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

Çok daha kısa değil, korkarım.

CSH altında $? STATE var.


2
Bu STATE, yalnızca ayarlanmamış değil, boş veya ayarsız olup olmadığını kontrol eder .
chepner

7

Benim gibi gelecekteki insanlar için, bir adım ileri gitmek ve var adını parametreleştirmek istedim, böylece değişken boyutlu değişken adları listesi üzerinde döngü yapabilirim:

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

Bir sürü değişkeni bir kerede kontrol etmek için güzel bir iddia yazabiliriz:

#
# 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
}

Örnek çağırma:

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

Çıktı:

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

Bu türden daha fazla iddia burada: https://github.com/codeforester/base/blob/master/lib/assertions.sh



1

Yukarıdaki çözümlerin hiçbiri benim amacım için işe yaramadı, çünkü kısmen uzun bir işleme başlamadan önce ayarlanması gereken değişkenlerin açık uçlu bir listesini kontrol ediyorum. Ben bununla sona erdi:

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

Harici kabuk komut dosyalarını kullanmak yerine, giriş kabuğumdaki işlevlere yüklenme eğilimindeyim. Herhangi bir set değişkeni yerine ortam değişkenlerini denetlemek için yardımcı bir işlev olarak böyle bir şey kullanın:

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

1
Bu yanlış. is_this_an_env_variable Pçünkü başarı geri dönecektir PATH.
Eric

Bir ortam değişkeni olduğu için var. Eqaul null dize olarak ayarlanıp ayarlanmadığı başka bir konudur.
nafdef

-7

$?Sözdizimi çok zarif:

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

Bu işe yarıyor, ancak burada kimse sözdizimindeki dokümanları nerede bulabileceğimi biliyor mu? , $? normalde önceki dönüş değeridir.
Danny Staple

Benim hatam - bu işe yaramıyor gibi görünüyor. Bu setle birlikte ve bu set olmadan basit bir komut dosyasında deneyin.
Danny Staple

11
Bourne, Korn, Bash ve POSIX mermilerinde, $?önceki komutun çıkış durumudur; $?BLAH'BLAH' ile birleştirilmiş önceki komutun çıkış durumundan oluşan dizedir. Bu, değişkeni hiç test etmez $BLAH. (Gerek duyulan şeyleri az çok yapabileceği söylentisi var csh, ancak deniz kabukları en iyi deniz kıyısında bırakılıyor.)
Jonathan Leffler

Bash söz konusu olduğunda bu tamamen yanlış bir cevaptır.
codeforester
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.