चेक करें कि क्या बैश में सरणी खाली है


110

मेरे पास एक सरणी है जो मेरी स्क्रिप्ट के चलने के दौरान विभिन्न त्रुटि संदेशों से भर जाती है।

मुझे यह जांचने का एक तरीका चाहिए कि क्या यह स्क्रिप्ट के अंत में खाली नहीं है और यदि यह है तो एक विशिष्ट कार्रवाई करें।

मैंने पहले से ही इसे जांचने के लिए एक सामान्य VAR और -z की तरह इसका इलाज करने की कोशिश की है, लेकिन यह काम नहीं करता है। क्या बैश में कोई सरणी खाली है या नहीं यह जांचने का कोई तरीका है?

जवाबों:


143

मान लीजिए कि आपकी सरणी है $errors, बस यह देखने के लिए जांचें कि क्या तत्वों की गिनती शून्य है।

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

10
कृपया ध्यान दें कि =एक स्ट्रिंग ऑपरेटर है। यह इस मामले में ठीक काम करने के लिए होता है, लेकिन मैं -eqइसके बजाय उचित अंकगणितीय ऑपरेटर का उपयोग करूंगा (बस मामले में मैं -geया तो स्विच करना चाहूंगा -lt, आदि)।
मुसीफिल

6
set -u"अनबाउंड वैरिएबल" के साथ काम नहीं करता है - यदि सरणी खाली है।
इगोर

@Igor: मेरे लिए बश 4.4 में काम करता है। set -u; foo=(); [ ${#foo[@]} -eq 0 ] && echo empty। यदि मैं unset foo, तो यह प्रिंट करता है foo: unbound variable, लेकिन यह अलग है: सरणी चर मौजूदा और खाली होने के बजाय बिल्कुल भी मौजूद नहीं है।
पीटर कॉर्ड्स

उपयोग करते समय बैश 3.2 (OSX) में भी परीक्षण किया गया set -u- जब तक आपने अपना चर पहले घोषित किया था, यह पूरी तरह से काम करता है।
शून्य

15

मैं आमतौर पर इस मामले में अंकगणितीय विस्तार का उपयोग करता हूं:

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

अच्छा और साफ! मुझें यह पसंद है। मैं यह भी ध्यान देता हूं कि यदि सरणी का पहला तत्व हमेशा गैर-रिक्त है, (( ${#a} ))(पहले तत्व की लंबाई) भी काम करेगा। हालाँकि, यह विफल हो जाएगा a=(''), जबकि (( ${#a[@]} ))उत्तर में दिया गया सफल होगा।
cxw

8

आप सरणी को एक साधारण चर के रूप में भी मान सकते हैं। उस तरह से, बस का उपयोग कर

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एक स्ट्रिंग के रूप में व्याख्या करने की कोशिश करता है ( [@]उस मामले में एक सरल शाब्दिक स्ट्रिंग है) और इसलिए हमेशा झूठे के रूप में रिपोर्ट किया जाता है: "क्या शाब्दिक स्ट्रिंग [@]खाली है?" स्पष्ट रूप से नहीं।


7
[ -z "$array" ]या [ -n "$array" ]काम नहीं करता है। कोशिश करें array=('' foo); [ -z "$array" ] && echo empty, और यह प्रिंट होगा emptyभले ही arrayस्पष्ट रूप से खाली न हो।
मुशीफिल

2
[[ -n "${array[*]}" ]]पूरे सरणी को एक स्ट्रिंग के रूप में प्रक्षेपित करता है, जिसे आप गैर-शून्य लंबाई के लिए जांचते हैं। यदि आप array=("" "")दो खाली तत्वों के बजाय खाली होना मानते हैं, तो यह उपयोगी हो सकता है।
पीटर कॉर्ड्स

@PeterCordes मुझे नहीं लगता कि यह काम करता है। अभिव्यक्ति एक एकल अंतरिक्ष चरित्र का मूल्यांकन करती है, और [[ -n " " ]]"सच है," जो एक दया है। आपकी टिप्पणी ठीक वही है जो मैं करना चाहता हूं।
माइकल

@ मिचेल: बकवास, आप सही कह रहे हैं। यह केवल एक खाली स्ट्रिंग के 1-तत्व सरणी के साथ काम करता है, न कि 2 तत्वों के साथ। मैंने पुराने बैश की भी जाँच की और यह अभी भी वहाँ गलत है; जैसे आप कहते हैं set -xकि यह दिखाता है कि यह कैसे फैलता है। मुझे लगता है कि मैंने पोस्ट करने से पहले उस टिप्पणी का परीक्षण नहीं किया। >। <आप इसे स्थापित कर सकते हैं IFS=''(इस कथन के इर्द-गिर्द इसे बचा / बचा सकते हैं ), क्योंकि "${array[*]}"विस्तार IFS के पहले चरित्र के साथ तत्वों को अलग करता है। (या अंतरिक्ष अगर परेशान)। लेकिन " अगर IFS शून्य है, तो पैरामीटर बिना विभाजक के हस्तक्षेप में शामिल हो जाते हैं। " (डॉक्स $ $ स्थितीय पैरामेट्स के लिए, लेकिन मैं सरणियों के लिए समान मानता हूं)।
पीटर कॉर्ड्स

@ मिचेल: एक उत्तर मिला जो ऐसा करता है।
पीटर कॉर्ड्स

3

मैंने इसकी जाँच की 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 को अनदेखा किया जा रहा है, तो कंपाउंड कमांड या फ़ंक्शन बॉडी के भीतर निष्पादित कमांड में से कोई भी -e सेटिंग से प्रभावित नहीं होगा, भले ही -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

आपके साथ -uवास्तव में ${array[@]+"${array[@]}"}cf stackoverflow.com/a/34361807/1237617
Jakub Bochenski

@JakubBochenski बैश के किस संस्करण के बारे में आप बात कर रहे हैं? gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9
x-yuri

एकल कोष्ठक उदाहरण में समस्या @निश्चित रूप से है। आप *सरणी विस्तार का उपयोग कर सकते हैं [ "${array[*]}" ], क्या आप नहीं कर सकते? फिर भी, [[ठीक भी काम करता है। कई खाली तारों के साथ एक सरणी के लिए इन दोनों का व्यवहार थोड़ा आश्चर्य की बात है। दोनों [ ${#array[*]} ]और [[ "${array[@]}" ]]के लिए झूठे हैं array=()और array=('')लेकिन के लिए सच array=('' '')(दो या अधिक रिक्त स्ट्रिंग)। यदि आप एक या एक से अधिक खाली तार चाहते हैं, तो सभी सही हैं, आप उपयोग कर सकते हैं [ ${#array[@]} -gt 0 ]। यदि आप उन सभी को झूठा चाहते थे, तो आप //उन्हें बाहर निकाल सकते हैं।
आइस्ड सेप

@eisd मैं उपयोग कर सकता था [ "${array[*]}" ], लेकिन अगर मैं ऐसी अभिव्यक्ति में भाग लेता, तो मेरे लिए यह समझना मुश्किल होता कि यह क्या करता है। चूंकि [...]प्रक्षेप के परिणाम पर तार के संदर्भ में काम करता है। के रूप में विरोध किया गया था [[...]], जिसके बारे में पता किया जा सकता है कि क्या प्रक्षेप किया गया था। यही है, यह जान सकता है कि यह एक सरणी पारित किया गया था। [[ ${array[@]} ]]मुझे पढ़ता है "चेक के रूप में यदि सरणी गैर-खाली है", जबकि [ "${array[*]}" ]"चेक करें कि क्या सभी सरणी तत्वों के प्रक्षेप का परिणाम गैर-रिक्त स्ट्रिंग है"।
x-yuri

... दो खाली तारों के साथ व्यवहार के रूप में यह मेरे लिए बिल्कुल आश्चर्य की बात नहीं है। क्या आश्चर्य की बात है कि एक खाली स्ट्रिंग के साथ व्यवहार है। लेकिन यकीनन वाजिब है। के बारे में [ ${#array[*]} ], आप शायद मतलब है [ "${array[*]}" ], क्योंकि पूर्व तत्वों के किसी भी संख्या के लिए सच है। क्योंकि तत्वों की संख्या हमेशा गैर-रिक्त स्ट्रिंग होती है। दो तत्वों के साथ उत्तरार्द्ध के बारे में, कोष्ठक के अंदर की अभिव्यक्ति का विस्तार होता है, ' 'जो गैर-रिक्त स्ट्रिंग है। के रूप में [[ ${array[@]} ]], वे बस सोचते हैं (और ठीक ही तो) कि दो तत्वों में से कोई भी सरणी गैर-रिक्त है।
x-yuri

2

यदि आप रिक्त तत्वों के साथ एक सरणी का पता लगाना चाहते हैं , जैसेarr=("" "") खाली, जैसे हीarr=()

आप सभी तत्वों को एक साथ चिपका सकते हैं और जांच सकते हैं कि परिणाम शून्य-लंबाई है या नहीं। (यदि सरणी बहुत बड़ी हो सकती है, तो सरणी सामग्री की एक चपटी प्रतिलिपि बनाना प्रदर्शन के लिए आदर्श नहीं है। लेकिन उम्मीद है कि आप इस तरह के कार्यक्रमों के लिए बैश का उपयोग नहीं कर रहे हैं ...)

लेकिन "${arr[*]}"पहले चरित्र द्वारा अलग किए गए तत्वों के साथ फैलता है IFS। तो आपको IFS को बचाने / पुनर्स्थापित IFS=''करने और इस कार्य को करने की आवश्यकता है, या फिर यह जांचें कि स्ट्रिंग एलिमेंट्स == # एरे तत्वों की - 1. (एरे nएलीमेंट्स ऑफ n-1सेपरेटर्स)। उस ऑफ-बाय-वन से निपटने के लिए, 1 से कॉन्सेप्टेशन को पैड करना सबसे आसान है

arr=("" "")

## Assuming default non-empty IFS
## TODO: also check for ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 separators + 1 space + array elements
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array

के साथ परीक्षण के मामले set -x

### a non-empty element
$ arr=("" "x")
  + arr=("" "x")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat=' x '
  + [[ 3 -ne 2 ]]
  + echo not empty array
not empty array

### 2 empty elements
$ arr=("" "")
  + arr=("" "")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat='  '
  + [[ 2 -ne 2 ]]
  + echo empty array
empty array

दुर्भाग्य से इस के लिए विफल रहता है arr=(): [[ 1 -ne 0 ]] तो आपको पहले अलग से वास्तव में खाली सरणियों की जांच करनी होगी।


या के साथIFS='' । संभवतः आप एक सब-हेल्प का उपयोग करने के बजाय IFS को सहेजना / पुनर्स्थापित करना चाहते हैं, क्योंकि आप आसानी से एक सब-आउट से परिणाम प्राप्त नहीं कर सकते।

# inside a () subshell so we don't modify our own IFS
(IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)

उदाहरण:

$ arr=("" "")
$ (IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)
   + IFS=
   + [[ -n '' ]]
   + echo empty array
empty array

के साथ काम करता हैarr=() - यह अभी भी खाली स्ट्रिंग है।


मैंने उत्थान किया, लेकिन मैंने उपयोग करना शुरू कर दिया है [[ "${arr[*]}" = *[![:space:]]* ]], क्योंकि मैं कम से कम एक गैर-डब्ल्यूएस चरित्र पर भरोसा कर सकता हूं।
माइकल

@ मिचेल: हाँ, यह एक अच्छा विकल्प है अगर आपको अस्वीकार करने की आवश्यकता नहीं है arr=(" ")
पीटर कॉर्ड्स

0

मेरे मामले में, दूसरा उत्तर पर्याप्त नहीं था क्योंकि व्हाट्सएप हो सकता है। मैं साथ आया:

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

echo | wcशेल-इन का उपयोग करने की तुलना में अनावश्यक रूप से अक्षम लगता है।
पीटर कॉर्ड्स

निश्चित नहीं कि अगर मैं @PeterCordes को समझूं, तो क्या मैं व्हाट्सएप [ ${#errors[@]} -eq 0 ];समस्या के आसपास काम करने के लिए दूसरे उत्तरों को संशोधित कर सकता हूं ? मैं बिल्ट-इन भी पसंद करूंगा।
मीका

व्हाट्सएप कैसे वास्तव में एक समस्या का कारण बनता है? $#एक नंबर तक फैलता है, और ठीक काम करता है के बाद भी opts+=("")। जैसे unset opts; opts+=("");opts+=(" "); echo "${#opts[@]}"और मुझे मिलता है 2। क्या आप किसी ऐसी चीज़ का उदाहरण दिखा सकते हैं जो काम नहीं करती?
पीटर कॉर्ड्स

बहुत समय पहले की बात है। IIRC का उद्गम स्रोत हमेशा कम से कम "" मुद्रित होता है। इस प्रकार, opts = "" या opts = ("") के लिए मुझे 0 की आवश्यकता थी, 1 की नहीं, खाली न्यूलाइन या खाली स्ट्रिंग को अनदेखा करना।
मीका

ठीक है, तो आप के opts=("")रूप में ही इलाज की जरूरत है opts=()? यह एक खाली सरणी नहीं है, लेकिन आप खाली सरणी या खाली पहले तत्व के साथ जांच कर सकते हैं opts=(""); [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty। ध्यान दें कि आपका वर्तमान उत्तर "कोई विकल्प नहीं" कहता है opts=("" "-foo"), जो पूरी तरह से फर्जी है, और यह उस व्यवहार को पुन: पेश करता है। आप [[ -z "${opts[*]}" ]]अनुमान लगा सकते हैं कि सभी सरणी तत्वों को एक फ्लैट स्ट्रिंग में विभाजित किया जा सकता है, जो -zगैर-शून्य लंबाई की जांच करता है। यदि पहला तत्व जांचना पर्याप्त है, तो -z "$opts"काम करता है।
पीटर कॉर्ड्स

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.