سؤال تحقق مما إذا كان الصفيف فارغًا في Bash


لدي صفيف يتم تعبئته برسائل خطأ مختلفة أثناء تشغيل النص البرمجي.

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

لقد حاولت بالفعل معالجتها مثل VAR عادي واستخدام -z للتحقق من ذلك ، ولكن لا يبدو أن العمل. هل هناك طريقة للتحقق مما إذا كان الصفيف فارغًا أم لا في Bash؟

شكر


82
2018-02-11 03:59


الأصل




الأجوبة:


لنفترض أن صفيفك هو $errors، تحقق فقط لمعرفة ما إذا كان عدد العناصر صفرًا.

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi

113
2018-02-11 04:10



يرجى ملاحظة ذلك = هو مشغل سلسلة. يحدث أن تعمل بشكل جيد في هذه الحالة ، لكني سأستخدم المشغل الحسابي الصحيح -eq بدلا من ذلك (فقط في حالة كنت أريد أن التحول إلى -ge أو -lt، وما إلى ذلك). - musiphil
لا يعمل مع set -u: "متغير غير منضم" - إذا كان الصفيف فارغًا. - Igor
@ ايجور: يعمل لي في باش 4.4. set -u;  foo=();  [ ${#foo[@]} -eq 0 ] && echo empty. اذا انا unset foo، ثم يطبع foo: unbound variable، لكن هذا مختلف: لا يوجد متغير الصفيف على الإطلاق ، بدلاً من وجوده وكونه فارغًا. - Peter Cordes
تم اختباره أيضًا في Bash 3.2 (OSX) عند استخدامه set -u - طالما أعلنت المتغير الخاص بك أولاً ، فهذا يعمل بشكل مثالي. - zeroimpl


يمكنك أيضًا اعتبار الصفيف كمتغير بسيط. بهذه الطريقة ، فقط باستخدام

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi

أو استخدام الجانب الآخر

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi

المشكلة في هذا الحل هي أنه إذا تم الإعلان عن صفيف مثل هذا: array=('' foo). ستقوم هذه الشيكات بالإبلاغ عن الصفيف على أنه فارغ ، بينما من الواضح أنه ليس كذلك. (شكرا @ musiphil!)

عن طريق [ -z "$array[@]" ] من الواضح أنه ليس حلا لا. عدم تحديد الأقواس المتعرجة يحاول تفسيرها $array كسلسلة ([@] في هذه الحالة سلسلة حرفية بسيطة) ولذلك يتم الإبلاغ عنها دائمًا كاذبة: "هي السلسلة الحرفية [@] فارغة؟ "واضح لا.


6
2018-06-23 10:29



[ -z "$array" ] أو [ -n "$array" ] لا يعمل. محاولة array=('' foo); [ -z "$array" ] && echo emptyوسوف تطبع empty بالرغم من ذلك array من الواضح ليست فارغة. - musiphil
[[ -n "${array[*]}" ]] يقوم بتحويل الصفيف بأكمله كسلسلة ، والتي تقوم بالتحقق من طولها غير الصفري. إذا كنت تفكر array=("" "") لتكون فارغة ، بدلاً من وجود عنصرين فارغين ، قد يكون هذا مفيدًا. - Peter Cordes


أنا عموما استخدام التوسع الحسابي في هذه الحالة:

if (( ${#a[@]} )); then
    echo not empty
fi

3
2017-08-02 06:04



جميلة ونظيفة! احب ذلك. ألاحظ أيضًا أنه إذا كان العنصر الأول من المصفوفة دائمًا غير فارغ ، (( ${#a} )) (طول العنصر الأول) ستعمل أيضا. ومع ذلك ، سوف يفشل ذلك a=('')، بينما (( ${#a[@]} )) في الجواب سوف تنجح. - cxw


راجعت ذلك bash-4.4.0:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

و bash-4.1.5:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

في الحالة الأخيرة تحتاج إلى البناء التالي:

${array[@]:+${array[@]}}

لأنها لا تفشل على صفيف فارغ أو غير محدود. هذا إذا كنت تفعل set -eu كما أفعل عادة. هذا يوفر التحقق من الأخطاء أكثر دقة. من عند المستندات:

-e

قم بالخروج فورًا إذا كان خط الأنابيب (انظر خطوط الأنابيب) ، والذي قد يتكون من أمر بسيط واحد (انظر أوامر بسيطة) ، أو قائمة (انظر القوائم) ، أو أمر مركب (راجع أوامر مركبة) ، يُرجع حالة غير صفرية. لا تخرج shell إذا كان الأمر الذي يفشل جزءًا من قائمة الأوامر فورًا بعد فترة أو حتى كلمة رئيسية ، جزء من الاختبار في عبارة if ، جزء من أي أمر يتم تنفيذه في && أو || قائمة باستثناء الأمر التالي للنهائي && أو || ، أي أمر في خط أنابيب ولكن الأخير ، أو إذا تم عكس حالة إرجاع الأمر مع! إذا أرجع أمر مركب آخر غير subshell حالة غير صفرية بسبب فشل أمر أثناء -e ، لا يتم إنهاء shell. يتم تنفيذ اعتراض على ERR ، إذا تم ضبطه ، قبل أن يتم إخراج shell.

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

إذا تم تنفيذ أمر مركب أو دالة shell في سياق يتم فيه تجاهل -e ، فلن يتأثر أي من الأوامر التي يتم تنفيذها داخل أمر مركب أو جسد الوظيفة بالإعداد -e ، حتى إذا تم تعيين -e وأمر بإرجاع حالة الفشل. في حالة تعيين أمر مركب أو دالة shell أثناء التنفيذ في سياق حيث يتم تجاهل -e ، لن يكون لهذا الإعداد أي تأثير حتى يكتمل الأمر المركب أو الأمر الذي يحتوي على استدعاء الوظيفة.

-u

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

إذا لم تكن بحاجة لذلك ، فلا تتردد في حذفها :+${array[@]} جزء.

لاحظ أيضًا أنه ضروري للاستخدام [[ المشغل هنا ، مع [ لقد حصلت:

$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh
_/1.sh: line 4: [: too many arguments
empty

2
2017-12-04 14:58



مع -u يجب عليك في الواقع استخدام ${array[@]+"${array[@]}"} CF stackoverflow.com/a/34361807/1237617 - Jakub Bochenski
JakubBochenski أي إصدار من bash تتحدث؟ gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9 - x-yuri
المشكلة في المثال بين قوسين واحدة هي @، بالتاكيد. يمكنك استخدام * توسيع مجموعة مثل [ "${array[*]}" ]، هل يمكن أن لا؟ ما يزال، [[ كما يعمل بشكل جيد. سلوك كل من هذه لصفيف مع سلاسل فارغة متعددة هو مفاجئ بعض الشيء. على حد سواء [ ${#array[*]} ] و [[ "${array[@]}" ]] خاطئة لـ array=() و array=('') لكن صحيح ل array=('' '') (سلسلتان فارغتان أو أكثر). إذا كنت ترغب في جعل واحدة أو أكثر من السلاسل الفارغة صحيحة ، فيمكنك استخدامها [ ${#array[@]} -gt 0 ]. إذا كنت تريدهم جميعًا كاذبة ، فيمكنك ربما // منهم بها. - eisd
eisd يمكنني استخدامها [ "${array[*]}" ]ولكن إذا واجهت مثل هذا التعبير ، سيكون من الصعب علي فهم ما يفعله. منذ [...] تعمل من حيث سلاسل على نتيجة الاستيفاء. في مقابل [[...]]، والتي يمكن أن تكون مدركة لما تم تقريبه. بمعنى ، يمكن معرفة أنه تم تمرير صفيف. [[ ${array[@]} ]] يقرأ لي "تحقق إذا كان الصفيف غير فارغ" ، في حين [ "${array[*]}" ] "تحقق مما إذا كانت نتيجة الاستيفاء لجميع عناصر الصفيف سلسلة غير فارغة". - x-yuri
... أما بالنسبة للسلوك مع سلسلتين فارغتين فليس من قبيل المفاجأة بالنسبة لي. ما يثير الدهشة هو السلوك مع سلسلة فارغة واحدة. لكن يمكن القول إن ذلك معقول. بخصوص [ ${#array[*]} ]ربما تقصد [ "${array[*]}" ]، لأن الأول ينطبق على أي عدد من العناصر. لأن عدد العناصر دائمًا هو سلسلة غير فارغة. بخصوص الأخير بعنصرين ، يتوسع التعبير داخل الأقواس ' ' وهي سلسلة غير فارغة. أما بالنسبة لل [[ ${array[@]} ]]، يعتقدون فقط (وهذا صحيح) أن أي صفيف من عنصرين غير فارغ. - x-yuri


في حالتي ، و الجواب الثاني لم يكن كافيا لأنه يمكن أن تكون هناك مساحات بيضاء. جئت مع:

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi

0
2018-02-17 19:54



echo | wc يبدو غير فعال بشكل لا مبرر له مقارنة باستخدام shell المضمن. - Peter Cordes
لست متأكدًا من أنني أفهمPeterCordes ، هل يمكنني تعديل الإجابات الثانية؟ [ ${#errors[@]} -eq 0 ]; بطريقة للعمل حول مشكلة المسافة البيضاء؟ كنت تفضل أيضا المدمج في. - Micha
كيف تسبب مشكلة whitespace بالضبط مشكلة؟ $# يوسع إلى رقم ، ويعمل بشكل جيد حتى بعد opts+=(""). مثلا unset opts;  opts+=("");opts+=(" "); echo "${#opts[@]}" واحصل على 2. هل يمكنك عرض مثال على شيء لا يعمل؟ - Peter Cordes
منذ زمن طويل IIRC المصدر الأصلي المطبوعة دائما على الأقل "". وبالتالي ، بالنسبة إلى opts = "" أو opts = ("") احتجت إلى 0 ، وليس 1 ، تجاهل السطر الجديد الفارغ أو السلسلة الفارغة. - Micha
حسنا ، لذلك تحتاج إلى علاج opts=("") كمثل opts=()؟ هذا ليس مصفوفة فارغة ، ولكن يمكنك التحقق من وجود صفيف فارغ أو عنصر أول فارغ مع opts=("");  [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty. لاحظ أن إجابتك الحالية تقول "لا توجد خيارات" لـ opts=("" "-foo")وهو زائف تمامًا ، وهذا يُعيد إنتاج هذا السلوك. يمكنك [[ -z "${opts[*]}" ]] أعتقد ، لإقحام جميع عناصر مجموعة في سلسلة مسطحة ، والتي -z يتحقق لمدة غير صفرية.  إذا كان التحقق من العنصر الأول كافياً ، -z "$opts" يعمل. - Peter Cordes


أفضل استخدام الأقواس المزدوجة:

if [[ !${array[@]} ]]
then
    echo "Array is empty"
else
    echo "Array is not empty"
fi

الأقواس المزدوجة: https://stackoverflow.com/questions/669452/is-preferable-over-in-bash


0
2017-11-04 06:40