كيف تتحقق من تعيين متغير في Bash؟




shell variables (20)

كيف أعرف ما إذا تم تعيين متغير في Bash؟

على سبيل المثال ، كيف أتحقق مما إذا كان المستخدم قد قدم المعلمة الأولى إلى إحدى الوظائف؟

function a {
    # if $1 is set ?
}

Functions to check if variable is declared/unset

including empty $array=()


The following functions test if the given name exists as a variable

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • By first testing if the variable is (un)set, the call to declare can be avoided, if not necessary.
  • If however $1 contains the name of an empty $array=() , the call to declare would make sure we get the right result
  • There's never much data passed to /dev/null as declare is only called if either the variable is unset or an empty array.

This functions would test as showed in the following conditions:

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset

.

For more details

and a test script see my answer to the question "How do I check if a variable exists in bash?" .

ملاحظة: إن استخدام مماثل من declare -p، كما هو مبين أيضا من قبل الصورة answer ، هو من قبيل الصدفة حقا. وإلا فإنني بالطبع أود أن أذكر ذلك!


الطريق الصحيح

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

حيث ${var+x} عبارة عن توسع معلمة يتم تقييمه إلى لا شيء إذا لم يتم var ، واستبدال السلسلة x بخلاف ذلك.

اقتباسات Digress

يمكن حذف علامات الاقتباس (حتى يمكننا أن نقول ${var+x} بدلاً من "${var+x}" ) لأن هذا النحو و الاستخدام يضمنان أن هذا سيوسع فقط إلى شيء لا يتطلب علامات اقتباس (حيث إما أن يتوسع إلى x (التي لا تحتوي على أي فواصل للكلمة بحيث لا تحتاج إلى علامات اقتباس) ، أو لا شيء (مما يؤدي إلى [ -z ] ، والتي تقيم بشكل ملائم إلى نفس القيمة (true) التي يقوم بها [ -z "" ] أيضًا)).

ومع ذلك ، في حين أن علامات الاقتباس يمكن حذفها بأمان ، ولم يكن الأمر واضحا على الفور (لم يكن من الواضح حتى لأول مؤلف هذا الاقتباس الشرح من هو أيضا المبرمج الرئيسي باش) ، فإنه سيكون من الأفضل في بعض الأحيان كتابة الحل مع علامات الاقتباس كـ [ -z "${var+x}" ] ، بتكلفة منخفضة جدًا جدًا لعقوبة السرعة O (1). أضاف المؤلف الأول هذا أيضًا كتعليق بجوار الشفرة مستخدمًا هذا الحل ، مما يعطي عنوان URL لهذه الإجابة ، والذي يتضمن الآن أيضًا تفسير سبب حذف علامات الاقتباس بأمان.

الطريق الخطأ

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

وهذا لأنه لا يميز بين المتغير الذي لا يتم تعيينه والمتغير الذي تم تعيينه على السلسلة الفارغة. وهذا يعني ، إذا var='' ، فإن الحل أعلاه سوف ينتج بشكل غير صحيح أن var غير معيّن.

لكن هذا التمييز ضروري في الحالات التي يكون فيها على المستخدم تحديد ملحق ، أو قائمة إضافية من الخصائص ، والتي لا تحددها افتراضيا لقيمة غير فارغة ، في حين أن تحديد السلسلة الفارغة يجب أن يجعل البرنامج النصي يستخدم امتدادا أو قائمة فارغة من خصائص إضافية.


أستخدم دائمًا هذا المستند ، استنادًا إلى حقيقة أنه يبدو من السهل فهم أي شخص يرى الشفرة للمرة الأولى:

if [ "$variable" = "" ]
    then
    echo "Variable X is empty"
fi

وإذا كنت تريد التحقق إن لم يكن فارغًا ،

if [ ! "$variable" = "" ]
    then
    echo "Variable X is not empty"
fi

هذا هو.


إذا كان var يمكن أن يكون صفيفًا ، فإن استبدال المعلمة [ -z "${var+x}" ] غير صحيح. لكي تكون متأكدًا حقًا في Bash ، يجب استخدام بنية مصفوفة مثل [ "${#var[@]}" = 0 ] ، كما هو موضح أدناه.

is-var-set () {
    results="\${var+x}=${var+x}\t\${#var[@]}=${#var[@]}"
    if [ -z "${var+x}" ] && [ "${#var[@]}" = 0 ]; then
        echo -e "$1: var's unset.\t$results"
    elif [ -n "${var+x}" ] && [ "${#var[@]}" != 0 ]; then
        echo -e "$1: var is set. \t$results"
    else
        echo -e "$1: Is var set? \t$results"
    fi
    unset var # so we don't have to do it everywhere else
}

في جميع الحالات تقريبا ، يتفقون. الوضع الوحيد الذي وجدته حيث تكون طريقة الصفيف أكثر دقة هو عندما يكون المتغير مصفوفة غير فارغة بموضع 0 unset (على سبيل المثال ، في الاختبارين 7 و A أدناه). يأتي هذا الاختلاف من اختلاف $var لقيمة ${var[0]} ، لذلك [ -z "${var+x}" ] لا يتحقق من الصفيف بأكمله.

فيما يلي حالات الاختبار الخاصة بي.

unset var;      is-var-set 1 # var unset
var='';         is-var-set 2 # var[0] set to ''
var=foo;        is-var-set 3 # var[0] set to 'foo'
var=();         is-var-set 4 # var unset (all indices)
var=(foo);      is-var-set 5 # var[0] set to 'foo'
var=([0]=foo);  is-var-set 6 # var[0] set to 'foo'
var=([1]=foo);  is-var-set 7 # var[0] unset, but var[1] set to 'foo'
declare -a var; is-var-set 8 # var empty, but declared as an array
declare -A var; is-var-set 9 # var empty, but declared as an associative array
declare -A var  # Because is-var-set() conveniently unsets it
var=([xz]=foo); is-var-set A # var[xz] set to 'foo', but var's otherwise empty
declare -a var  # Demonstrate that Bash knows about var, even when there's
declare -A var; is-var-set B # apparently no way to just _check_ its existence

وهنا الإخراج.

1: var's unset. ${var+x}=       ${#var[@]}=0
2: var is set.  ${var+x}=x      ${#var[@]}=1
3: var is set.  ${var+x}=x      ${#var[@]}=1
4: var's unset. ${var+x}=       ${#var[@]}=0
5: var is set.  ${var+x}=x      ${#var[@]}=1
6: var is set.  ${var+x}=x      ${#var[@]}=1
7: Is var set?  ${var+x}=       ${#var[@]}=1
8: var's unset. ${var+x}=       ${#var[@]}=0
9: var's unset. ${var+x}=       ${#var[@]}=0
A: Is var set?  ${var+x}=       ${#var[@]}=1
./foo.sh: line 26: declare: var: cannot convert indexed to associative array
B: var's unset. ${var+x}=       ${#var[@]}=0

باختصار:

  • يعمل بناء جملة المعلمة بالتوسعة ${var+x} ، فضلاً عن بناء جملة الصفيف ${#var[@]} في معظم الحالات ، مثل التحقق من المعاملات إلى الدالات. الطريقة الوحيدة لكسر هذه الحالة هي إذا أضاف إصدار مستقبلي من Bash طريقة لتمرير صفائف إلى وظائف بدون تحويل المحتويات إلى وسيطات فردية.
  • مطلوب صيغة المصفوفة للصفائف غير الفارغة (اقتران أم لا) مع عنصر 0 محدود.
  • Neither syntax explains what's going on if declare -a var has been used without assigning even a null value somewhere in the array. Bash still distinguishes the case somewhere (as seen in test B above), so this answer's not foolproof. Fortunately Bash converts exported environment variables into strings when running a program/script, so any issues with declared-but-unset variables will be contained to a single script, at least if it's not sourcing other scripts.

إن استخدام [[ -z "$var" ]] هو أسهل طريقة لمعرفة ما إذا كان المتغير قد تم ضبطه أم لا ، ولكن هذا الخيار -z لا يميز بين متغير غير مُتغيّر ، ومتغير معين إلى سلسلة فارغة:

 set=''

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

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

من الأفضل التحقق من ذلك وفقًا لنوع المتغير: متغير env أو معامل أو متغير منتظم.

للمتغير env:

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

لمعلمة (على سبيل المثال ، للتحقق من وجود المعلمة "$ 5":

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

لمتغير منتظم (باستخدام وظيفة auxiliar ، للقيام بذلك بطريقة أنيقة):

function declare_var {
   declare -p "$1" &> /dev/null

   return $?
}

declare_var "var_name" && echo "Set" || echo "Unset"

ملاحظات:

$#                  says you the number of positional parameters.

declare -p          gives you the definition of the variable passed as parameter. If it 
                    exists, returns 0, if not, returns 1 and prints an error message.

$?                  gives you the status code of the last executed command.

اقرأ قسم "توسيع المعامل" في صفحة رجل bash . لا يوفر توسيع المعلمة اختبارًا عامًا لضبط متغير ، ولكن هناك العديد من الأشياء التي يمكنك القيام بها للمعلمة إذا لم يتم تعيينها.

فمثلا:

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

سيتم تعيين first_arg يساوي $1 إذا تم تعيينه ، وإلا فإنه يستخدم القيمة "foo". إذا كان يجب أن تأخذ أي معلمة مفردة ، ولا يوجد إعداد افتراضي جيد ، فيمكنك الخروج برسالة خطأ عند عدم إعطاء أي معلمة:

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

(لاحظ استخدام : كأمر فارغ ، والذي يوسع فقط قيم الحجج الخاصة به. نحن لا نريد أن نفعل أي شيء مع $1 في هذا المثال ، فقط قم بإنهاء إذا لم يتم تعيينه)


بالنسبة لأولئك الذين يتطلعون إلى التحقق من عدم set -u أو تفريغها في برنامج نصي مع set -u :

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

ستفشل عملية التحقق [ -z "$var" ] العادية مع var; unbound variable var; unbound variable إذا تم set -u لكن [ -z "${var-}" ] expands إلى سلسلة فارغة إذا لم يتم تعيين var بدون الفشل.


دائمًا ما أجد جدول POSIX في الإجابة الأخرى بطيئًا في التحديق ، لذا إليك ما يلي:

   +----------------------+------------+-----------------------+-----------------------+
   |   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"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

لاحظ أن كل مجموعة (مع أو بدون القولون) لها نفس المجموعة وحالات عدم التعيين ، لذلك فإن الشيء الوحيد الذي يختلف هو كيفية التعامل مع الحالات الفارغة .

مع النقطتين السابقتين ، تكون الحالات الفارغة وغير المضبوطة متطابقة ، لذا سأستخدم تلك الحالات حيثما أمكن (أي := ، وليس فقط = ، لأن الحالة الفارغة غير متناسقة).

عناوين:

  • set يعني أن VARIABLE ليس فارغًا ( VARIABLE="something" )
  • فارغة تعني VARIABLE فارغ / خالٍ ( VARIABLE="" )
  • unset تعني VARIABLE غير موجود (غير unset VARIABLE )

القيم:

  • $VARIABLE تعني أن النتيجة هي القيمة الأصلية للمتغير.
  • "default" يعني أن النتيجة هي سلسلة الاستبدال المتوفرة.
  • "" تعني أن النتيجة فارغة (سلسلة فارغة).
  • exit 127 يعني توقف البرنامج النصي تنفيذ مع رمز الإنهاء 127.
  • $(VARIABLE="default") تعني أن النتيجة هي القيمة الأصلية للمتغير ويتم تعيين سلسلة الاستبدال المقدمة للمتغير للاستخدام في المستقبل.

على نسخة حديثة من باش (4.2 أو في وقت لاحق أعتقد ، لا أعرف على وجه اليقين) ، كنت أحاول هذا:

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

في shell ، يمكنك استخدام مشغل -z الذي يمثل True إذا كان طول السلسلة صفر.

A simple-liner لتعيين MY_VAR افتراضي إذا لم يتم تعيينه ، وإلا يمكنك اختياريًا عرض الرسالة:

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

في حين أن معظم التقنيات المذكورة هنا صحيحة ، يدعم bash 4.2 اختبارًا فعليًا لوجود متغير ( man bash ) ، بدلاً من اختبار قيمة المتغير.

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

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

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

لقد عثرت على رمز (أفضل بكثير) لإجراء ذلك إذا كنت تريد التحقق من وجود أي شيء في [email protected] .

if [[ $1 = "" ]]
then
  echo '$1 is blank'
else
  echo '$1 is filled up'
fi

لماذا هذا كله؟ كل شيء موجود في [email protected] موجود في Bash ، ولكن افتراضيًا يكون فارغًا ، لذلك لا يمكن أن يساعدك test -z test -n .

تحديث: يمكنك أيضًا حساب عدد الأحرف في المعلمات.

if [ ${#1} = 0 ]
then
  echo '$1 is blank'
else
  echo '$1 is filled up'
fi

للتحقق من متغير السلسلة غير الصفرية / الخالية من الصفر ، أي في حالة التعيين ، استخدم

if [ -n "$1" ]

انها عكس -z . أجد نفسي باستخدام -n أكثر من -z .


لمعرفة ما إذا كان المتغير غير فارغ ، يمكنني استخدامه

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

الاختبارات المعاكسة إذا كان المتغير إما غير محدد أو فارغ:

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

لمعرفة ما إذا تم تعيين متغير (فارغ أو غير فارغ) ، وأنا استخدم

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

الاختبارات المعاكسة في حالة عدم تحديد متغير:

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

هناك العديد من الطرق للقيام بذلك من خلال ما يلي:

if [ -z "$1" ]

وينجح هذا إذا كان $ 1 هو null أو unset


يمكنك ان تفعل:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'
        fi
}

If you wish to test that a variable is bound or unbound, this works well, even after you've turned on the nounset option:

set -o noun set

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

[[ $foo ]]

أو

(( ${#foo} ))

أو

let ${#foo}

أو

declare -p foo

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

على الرغم من أنه من الأفضل عادة اختبار $ # ، وهو عدد الحجج ، في رأيي.

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

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




variables