كيف تعرف ما إذا لم يتم تعريف السلسلة في نص شل باش؟

StackOverflow https://stackoverflow.com/questions/228544

سؤال

إذا كنت أرغب في التحقق من السلسلة الفارغة سأفعلها

[ -z $mystr ]

ولكن ماذا لو أردت التحقق مما إذا كان المتغير قد تم تعريفه على الإطلاق؟ أم أنه لا يوجد تمييز في البرمجة النصية؟

هل كانت مفيدة؟

المحلول

أعتقد أن الإجابة التي تتبعها ضمني (إن لم يتم ذكرها) فيكوإجابه, ، على الرغم من أنه لم يتم توضيحه ببساطة. لتمييز ما إذا كان VAR تم تعيينه ولكن فارغًا أم لا ، يمكنك استخدام:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

ربما يمكنك الجمع بين الاختبارين على السطر الثاني في واحد مع:

if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

ومع ذلك ، إذا قرأت الوثائق الخاصة بـ AutoconF ، فستجد أنها لا توصي بالجمع بين المصطلحات مع "-aوتوصي باستخدام اختبارات بسيطة منفصلة مع &&. لم أواجه نظامًا حيث توجد مشكلة ؛ هذا لا يعني أنهم لم يعتدوا على الوجود (لكنهم ربما يكونون نادرين للغاية هذه الأيام ، حتى لو لم تكن نادرة في الماضي البعيد).

يمكنك العثور على تفاصيل هذه ، وغيرها ذات الصلة توسعات المعلمة shell, ، ال test أو [ القيادة و تعبيرات مشروطة في دليل باش.


سُئلت مؤخرًا عن طريق البريد الإلكتروني حول هذه الإجابة مع السؤال:

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

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi

ألن ينجز هذا الشيء نفسه؟

if [ -z "${VAR}" ]; then echo VAR is not set at all; fi

سؤال عادل - الجواب هو "لا ، بديلك أبسط لا يفعل الشيء نفسه".

افترض أنني أكتب هذا قبل الاختبار:

VAR=

سيقول الاختبار الخاص بك "VAR لم يتم تعيينه على الإطلاق" ، ولكن سيقول لي (عن طريق الضمني لأنه لا يردد شيئًا) "تم تعيين VAR ولكن قد تكون قيمتها فارغة". جرب هذا السيناريو:

(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo JL:1 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:1 VAR is not set at all; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo JL:2 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:2 VAR is not set at all; fi
)

الإخراج هو:

JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all

في الزوج الثاني من الاختبارات ، يتم تعيين المتغير ، ولكن يتم تعيينه على القيمة الفارغة. هذا هو التمييز الذي ${VAR=value} و ${VAR:=value} الترميزات تجعل. كما سبق ل ${VAR-value} و ${VAR:-value}, ، و ${VAR+value} و ${VAR:+value}, ، وهلم جرا.


كما جيلي يشير في له إجابه, ، اذا ركضت bash مع ال set -o nounset الخيار ، ثم فشل الإجابة الأساسية أعلاه unbound variable. يتم علاجها بسهولة:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

أو يمكنك إلغاء set -o nounset الخيار مع set +u (set -u كونها مكافئة ل set -o nounset).

نصائح أخرى

~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~> 

-Z يعمل للمتغيرات غير المحددة أيضًا. للتمييز بين غير محدد ومحدد ، ستستخدم الأشياء المدرجة هنا أو ، مع تفسيرات أوضح ، هنا.

أنظف طريقة تستخدم التوسع كما في هذه الأمثلة. للحصول على جميع خياراتك ، تحقق من قسم توسيع المعلمة في الدليل.

كلمة بديلة:

~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED

القيمة الافتراضية:

~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED

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

دليل البرمجة النصية المتقدمة ، 10.2. استبدال المعلمة:

  • $ {var+blahblah}: إذا تم تعريف VAR ، يتم استبدال 'blahblah' للتعبير ، وإلا يتم استبدال فارغة
  • $ {var-blahblah}: إذا تم تعريف VAR ، فسيتم استبداله في حد ذاته ، وإلا يتم استبدال "blahblah"
  • $ {var؟ blahblah}: إذا تم تعريف VAR ، فسيتم استبداله ، وإلا فإن الوظيفة موجودة مع "blahblah" كرسالة خطأ.


لإعداد منطق البرنامج الخاص بك على ما إذا كان المتغير $ myster محدد أم لا ، يمكنك القيام بما يلي:

isdefined=0
${mystr+ export isdefined=1}

الآن ، إذا تم تعريف = 0 ، فإن المتغير غير محدد ، إذا تم تعريفه = 1 تم تعريف المتغير

هذه الطريقة في التحقق من المتغيرات أفضل من الإجابة أعلاه لأنها أكثر أناقة وقابلة للقراءة ، وإذا تم تكوين قذيفة bash الخاصة بك إلى خطأ في استخدام المتغيرات غير المحددة (set -u), ، سينتهي البرنامج النصي قبل الأوان.


أشياء مفيدة أخرى:

للحصول على قيمة افتراضية من 7 تم تعيينها إلى $ mystr إذا كانت غير محددة ، وتركها سليمة على خلاف ذلك:

mystr=${mystr- 7}

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

: ${mystr? not defined}

احذر هنا ما استخدمته ":" حتى لا يتم تنفيذ محتويات $ mystr كأمر في حالة تعريفه.

ملخص للاختبارات.

[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set"  # may or may not be empty
[ -n "${var+x}" ] && echo "var is set"  # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"

https://stackoverflow.com/a/9824943/14731 يحتوي على إجابة أفضل (واحدة أكثر قابلية للقراءة وتعمل معها set -o nounset تمكين). إنه يعمل مثل هذا:

if [ -n "${VAR-}" ]; then
    echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
    echo "VAR is set, but empty"
else
    echo "VAR is not set"
fi

الطريقة الصريحة للتحقق من وجود متغير محدد هي:

[ -v mystr ]

خيار اخر: توسع "مؤشرات صفيف القائمة":

$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1

المرة الوحيدة التي يتسع فيها هذا إلى السلسلة الفارغة هي متى foo غير مقلد ، لذلك يمكنك التحقق من ذلك مع السلسلة الشرطية:

$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0

يجب أن يكون متاحًا في أي إصدار باش> = 3.0

ليس لإلقاء هذه الدراجة إلى أبعد من ذلك ، ولكن أراد أن يضيف

shopt -s -o nounset

هو شيء يمكنك إضافته إلى الجزء العلوي من البرنامج النصي ، وهو خطأ إذا لم تكن المتغيرات أعلن في أي مكان في البرنامج النصي. الرسالة التي تراها هي unbound variable, ، ولكن كما ذكر آخرون ، لن يصطاد سلسلة فارغة أو قيمة خالية. للتأكد من أن أي قيمة فردية ليست فارغة ، يمكننا اختبار متغير مع توسيعه ${mystr:?}, ، والمعروف أيضًا باسم توسيع علامة الدولار ، والذي من شأنه أن يخطئ في parameter null or not set.

ال دليل مرجع باش هو مصدر موثوق للمعلومات حول باش.

إليك مثال على اختبار متغير لمعرفة ما إذا كان موجودًا:

if [ -z "$PS1" ]; then
        echo This shell is not interactive
else
        echo This shell is interactive
fi

(من القسم 6.3.2.)

لاحظ أن المساحة البيضاء بعد المفتوحة [ وقبل ] هو ليس اختياريا.


نصائح لمستخدمي VIM

كان لدي نص كان لديه عدة إعلانات على النحو التالي:

export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"

لكنني أردت أن يؤجلوا أي قيم موجودة. لذلك أعيد كتابتهم لتبدو هكذا:

if [ -z "$VARIABLE_NAME" ]; then
        export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi

تمكنت من أتمتة هذا في VIM باستخدام regex سريع:

s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r  export \1=\2\rfi\r/gc

يمكن تطبيق ذلك عن طريق تحديد الخطوط ذات الصلة بصريًا ، ثم الكتابة :. شريط الأوامر يسبق مع :'<,'>. لصق الأمر أعلاه واضغط على Enter.


تم اختباره على هذا الإصدار من VIM:

VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com

قد يرغب مستخدمو Windows في نهايات خطوط مختلفة.

إليك ما أعتقد أنه طريقة أكثر وضوحًا للتحقق مما إذا كان هناك متغير محدد:

var_defined() {
    local var_name=$1
    set | grep "^${var_name}=" 1>/dev/null
    return $?
}

استخدمه على النحو التالي:

if var_defined foo; then
    echo "foo is defined"
else
    echo "foo is not defined"
fi

مجموعة المكالمات دون أي وسيطات .. يخرج جميع VARS المحددة ..
آخرها في القائمة سيكون المحدد في البرنامج النصي الخاص بك ..
لذلك يمكنك تنشيط ناتجه إلى شيء يمكن أن يكتشف الأشياء التي يتم تعريفها وما لا

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top