चेक वेरिएबल बॉर्न में शेल की तरह एक सरणी है?


14

बोर्न जैसे शेल में जो ऐरे वेरिएबल को सपोर्ट करता है, हम कुछ पार्सिंग का उपयोग करके यह चेक कर सकते हैं कि वेरिएबल एक एरे है।

नीचे सभी कमांड चलाने के बाद चलाए गए थे a=(1 2 3)

zsh:

$ declare -p a
typeset -a a
a=( 1 2 3 )

bash:

$ declare -p a
declare -a a='([0]="1" [1]="2" [2]="3")'

ksh93:

$ typeset -p a
typeset -a a=(1 2 3)

pdksh और इसके व्युत्पन्न:

$ typeset -p a
set -A a
typeset a[0]=1
typeset a[1]=2
typeset a[2]=3

yash:

$ typeset -p a
a=('1' '2' '3')
typeset a

में एक उदाहरण है bash :

if declare -p var 2>/dev/null | grep -q 'declare -a'; then
  echo array variable
fi

यह दृष्टिकोण बहुत काम का है और एक उपधारा को स्पॉन करने की आवश्यकता है। जैसे अन्य शेल बिलिन का उपयोग करना=~ में [[ ... ]]एक subshell की जरूरत नहीं है, लेकिन अभी भी बहुत जटिल है।

क्या इस कार्य को पूरा करने का आसान तरीका है?


किन परिस्थितियों में आपको यह जाँचने की आवश्यकता होगी कि आपके चर सरणियाँ हैं या नहीं?
Kusalananda

जवाबों:


10

मुझे नहीं लगता कि आप कर सकते हैं, और मुझे नहीं लगता कि यह वास्तव में कोई फर्क पड़ता है।

unset a
a=x
echo "${a[0]-not array}"

x

कि ksh93और दोनों में एक ही बात करता है bash। यह संभवतः जैसा दिखता है सभी चर उन गोले में सरणियाँ हैं, या कम से कम कोई भी नियमित चर है जिसे विशेष विशेषताओं को नहीं सौंपा गया है, लेकिन मैंने इसकी ज्यादा जांच नहीं की।

bashबनाम एक स्ट्रिंग चर एक सरणी के लिए अलग व्यवहार के बारे में मार्गदर्शन वार्ता का उपयोग करते समय +=कार्य है, लेकिन यह बाद में हेजेज और राज्यों सरणी थोड़े अलग तरीके में बर्ताव करता है कि यौगिक काम संदर्भ।

इसमें यह भी कहा गया है कि एक चर को एक सरणी माना जाता है यदि किसी भी सबस्क्रिप्ट को एक मान दिया गया है - और स्पष्ट रूप से नल-स्ट्रिंग की संभावना शामिल है। ऊपर आप देख सकते हैं कि एक नियमित असाइनमेंट निश्चित रूप से एक सबस्क्रिप्ट में असाइन किया गया है - और इसलिए मुझे लगता है कि सब कुछ एक सरणी है।

व्यावहारिक रूप से, संभवतः आप उपयोग कर सकते हैं:

[ 1 = "${a[0]+${#a[@]}}" ] && echo not array

... स्पष्ट रूप से सेट किए गए चर को इंगित करने के लिए जिन्हें केवल मान 0 का एकल उपस्क्रिप्ट असाइन किया गया है।


तो मुझे लगता है कि अगर ${a[1]-not array}यह काम कर सकता है तो जाँच कर सकता है?
कोउंगलम

@cuonglm - ठीक है, bashमैनुअल के अनुसार नहीं : एक सरणी चर सेट माना जाता है अगर एक सबस्क्रिप्ट को एक मान दिया गया है। अशक्त स्ट्रिंग एक मान्य मान है। यदि किसी भी सबस्क्रिप्ट को प्रति सरणी एक सरणी दी जाती है। व्यवहार में, नहीं भी, क्योंकि आप कर सकते हैं a[5]=x। मुझे लगता है कि [ 1 -eq "${#a[@]}" ] && [ -n "${a[0]+1}" ]काम कर सकता है।
5

6

तो आप प्रभावी रूप से declare -pइसके चारों ओर कबाड़ के बिना सिर्फ मध्य भाग चाहते हैं ?

आप एक मैक्रो लिख सकते हैं जैसे:

readonly VARTYPE='{ read __; 
       case "`declare -p "$__"`" in
            "declare -a"*) echo array;; 
            "declare -A"*) echo hash;; 
            "declare -- "*) echo scalar;; 
       esac; 
         } <<<'

ताकि आप कर सकें:

a=scalar
b=( array ) 
declare -A c; c[hashKey]=hashValue;
######################################
eval "$VARTYPE" a #scalar
eval "$VARTYPE" b #array
eval "$VARTYPE" c #hash

(यदि आप फ़ंक्शन-स्थानीय चर पर इसका उपयोग करना चाहते हैं तो एक मात्र कार्य नहीं करेगा)।


उपनाम के साथ

shopt -s expand_aliases
alias vartype='eval "$VARTYPE"'

vartype a #scalar
vartype b #array
vartype c #hash

@ माइकस गुड पॉइंट। उपनाम इसे पहले से देखने योग्य बनाते हैं। +1
PSkocik

मेरा मतलब था - alias vartype="$VARTYPE"... या बिल्कुल नहीं परिभाषित $VARTYPE- यह काम करना चाहिए, है ना? आपको केवल उस shoptचीज़ की आवश्यकता होनी चाहिए bashक्योंकि यह aliasस्क्रिप्ट में विस्तार के संबंध में कल्पना के साथ टूट जाती है ।
15

1
@ mikeserv मुझे यकीन है कि cuonglm अपनी आवश्यकताओं और वरीयताओं के लिए इस दृष्टिकोण को ट्विक करने में सक्षम है। ;-)
पीएसकोकिक

... और सुरक्षा संबंधी विचार।
पीएसकोकिक

किसी भी बिंदु पर उपरोक्त कोड विकसित उपयोगकर्ता प्रदान नहीं करता है। यह किसी फंक्शन से कम सुरक्षित नहीं है। मैंने कभी भी आपको केवल पढ़ने के लिए फ़ंक्शन करने के बारे में उपद्रव करते नहीं देखा है, लेकिन ठीक है, मैं चर को आसानी से चिह्नित कर सकता हूं।
20

6

ज़श में

zsh% a=(1 2 3) s=1
zsh% [[ ${(t)a} == *array* ]] && echo array
array
zsh% [[ ${(t)s} == *array* ]] && echo array
zsh%

शायद echo ${(t)var}सरल है। इसके लिए धन्यवाद।

4

चर संस्करण का परीक्षण करने के लिए

b=("${!var[@]}")
c="${#b[@]}"

एक से अधिक सरणी सूचकांक होने पर परीक्षण करना संभव है:

[[ $c > 1 ]] && echo "Var is an array"

यदि पहला सूचकांक मूल्य शून्य नहीं है:

[[ ${b[0]} -eq 0 ]] && echo "Var is an array"      ## should be 1 for zsh.

एकमात्र कठिन भ्रम तब होता है जब केवल एक सूचकांक मूल्य होता है और वह मूल्य शून्य (या एक) होता है।

उस स्थिति के लिए, एक सरणी तत्व को एक चर से हटाने की कोशिश करने के साइड इफेक्ट का उपयोग करना संभव है जो कि एक सरणी नहीं है:

**bash** reports an error with             unset var[0]
bash: unset: var: not an array variable

**zsh** also reports an error with         $ var[1]=()
attempt to assign array value to non-array

यह बैश के लिए सही तरीके से काम करता है:

# Test if the value at index 0 could be unset.
# If it fails, the variable is not an array.
( unset "var[0]" 2>/dev/null; ) && echo "var is an array."

Zsh के लिए सूचकांक 1 होना चाहिए (जब तक कि कोई संगत मोड सक्रिय न हो)।

उप-शेल को var के इंडेक्स 0 को मिटाने के साइड इफेक्ट से बचने के लिए आवश्यक है।

मुझे इसे ksh में काम करने का कोई तरीका नहीं मिला है।

संपादित करें 1

यह फ़ंक्शन केवल bash4.2 + में काम करता है

getVarType(){
    varname=$1;
    case "$(typeset -p "$varname")" in
        "declare -a"*|"typeset -a"*)    echo array; ;;
        "declare -A"*|"typeset -A"*)    echo hash; ;;
        "declare -- "*|"typeset "$varname*| $varname=*) echo scalar; ;;
    esac;
}

var=( foo bar );  getVarType var

संपादित करें २

यह भी केवल bash4.2 + के लिए काम करता है

{ typeset -p var | grep -qP '(declare|typeset) -a'; } && echo "var is an array"

नोट: यह गलत सकारात्मकता देगा यदि var में परीक्षण किए गए तार हैं।


शून्य तत्वों के साथ सरणी के बारे में कैसे?
congonglm

1
डाट संपादित, थ। बहुत मौलिक दिखता है। : D
पीएसकोकिक

@cuonglm चेक ( unset "var[0]" 2>/dev/null; ) && echo "var is an array."सही ढंग से रिपोर्ट करता है जब var=()शून्य तत्वों के साथ एक सरणी में सेट किया गया है तो var एक सरणी है। यह घोषित करने के लिए बिल्कुल समान है।

स्केलर के लिए परीक्षण काम नहीं करेगा यदि स्केलर को निर्यात किया जाता है या पूर्णांक / लोअरकेस / आसानी से चिह्नित किया जाता है ... आप संभवतः इसे सुरक्षित रूप से बना सकते हैं कि कोई अन्य गैर-रिक्त आउटपुट स्केलर चर का मतलब है। मैं grep -Eबजाय grep -PGNU grep पर निर्भरता से बचने के लिए उपयोग करता हूँ ।
स्टीफन चेजलस

@ StéphaneChazelas पूर्णांक और / या लोअरकेस और / या आसानी के साथ स्केलर के लिए परीक्षण (bash में) -aइस तरह से शुरू होता है declare -airl var='()':। इसलिए grep टेस्ट काम करेगा ।

3

के लिए बैश , यह एक हैक (यद्यपि प्रलेखित) का एक छोटा सा है: उपयोग करने का प्रयास typeset"सरणी" विशेषता को निकालने के लिए:

$ typeset +a BASH_VERSINFO
bash: typeset: BASH_VERSINFO: cannot destroy array variables in this way
echo $?
1

(आप ऐसा नहीं कर सकते zsh, यह आपको एक सरणी को स्केलर में बदलने की अनुमति देता है, मेंbash स्पष्ट रूप से निषिद्ध है।)

इसलिए:

 typeset +A myvariable 2>/dev/null || echo is assoc-array
 typeset +a myvariable 2>/dev/null || echo is array

या एक समारोह में, अंत में चेतावनी को देखते हुए:

function typeof() {
    local _myvar="$1"
    if ! typeset -p $_myvar 2>/dev/null ; then
        echo no-such
    elif ! typeset -g +A  $_myvar 2>/dev/null ; then
        echo is-assoc-array
    elif ! typeset -g +a  $_myvar 2>/dev/null; then
        echo is-array
    else
        echo scalar
    fi
}

typeset -g(Bash-4.2 या बाद के) के उपयोग पर ध्यान दें , यह एक फ़ंक्शन के भीतर आवश्यक है ताकि typeset(syn। declare) उस तरह से काम न करे localऔर जिस मान का आप निरीक्षण करने का प्रयास कर रहे हैं उसे क्लोब करें। यह फ़ंक्शन "वैरिएबल" प्रकारों को भी नहीं संभालता है, typeset -fयदि आवश्यक हो तो आप एक और शाखा परीक्षण जोड़ सकते हैं ।


इसका उपयोग करने के लिए एक और (लगभग पूर्ण) विकल्प है:

    ${!name[*]}
          If name is an array variable, expands to  the  list
          of  array indices (keys) assigned in name.  If name
          is not an array, expands to 0 if name  is  set  and
          null  otherwise.   When @ is used and the expansion
          appears within double quotes, each key expands to a
          separate word.

एक मामूली समस्या है, हालांकि, 0 की एकल उपप्रकार वाली एक सरणी उपरोक्त स्थितियों में से दो से मेल खाती है। यह कुछ ऐसा है जिसे mikeserv भी संदर्भित करता है, बैश वास्तव में एक कठिन अंतर नहीं है, और इसमें से कुछ (यदि आप चैंगेलॉग की जांच करते हैं) को ksh और संगति पर कैसे ${name[*]}या कैसे दोषी ठहराया जा सकता है?${name[@]} एक गैर-सरणी पर व्यवहार करते हैं।

तो एक आंशिक समाधान है:

if [[ ${!BASH_VERSINFO[*]} == '' ]]; then
    echo no-such
elif [[ ${!BASH_VERSINFO[*]} == '0' ]]; then 
    echo not-array
elif [[ ${!BASH_VERSINFO[*]} != '0' ]]; 
    echo is-array    
fi

मैंने अतीत में इस पर भिन्नता का उपयोग किया है:

while read _line; do
   if [[ $_line =~ ^"declare -a" ]]; then 
     ...
   fi 
done < <( declare -p )

यह भी हालांकि एक उपधारा की जरूरत है।

एक और संभवतः उपयोगी तकनीक है compgen:

compgen -A arrayvar

यह सभी अनुक्रमित सरणियों को सूचीबद्ध करेगा, हालांकि साहचर्य सरणियों को विशेष रूप से नियंत्रित नहीं किया जाता है (बैश-4.4 तक) और नियमित चर के रूप में दिखाई देते हैं ( compgen -A variable)


typeset +aभी ksh में किसी त्रुटि की रिपोर्ट। हालांकि zsh में नहीं।

1

संक्षिप्त जवाब:

दो शंखों के लिए जिन्होंने इस संकेतन ( bashऔर ksh93) को पेश किया, एक अदिश चर एक तत्व के साथ एक सरणी है

सरणी बनाने के लिए न तो किसी विशेष घोषणा की आवश्यकता होती है। बस असाइनमेंट पर्याप्त है, और एक सादा असाइनमेंट var=valueसमान है var[0]=value


प्रयास करें: bash -c 'unset var; var=foo; typeset -p var'। बैश जवाब रिपोर्ट एक सरणी (एक की जरूरत है) ?. अब के साथ तुलना करें: bash -c 'unset var; var[12]=foo; typeset -p var'। ये अंतर क्यों है?। ए: शेल बनाए रखता है (अच्छे या बुरे के लिए) एक धारणा है कि कौन से संस्करण स्केलर या सरणियां हैं। शेल ksh दोनों अवधारणाओं को एक में मिलाता है।

1

यश के arrayबिलिन में कुछ विकल्प हैं जो केवल सरणी चर के साथ काम करते हैं। उदाहरण: -dविकल्प गैर-सरणी चर पर एक त्रुटि रिपोर्ट करेगा:

$ a=123
$ array -d a
array: no such array $a

तो हम ऐसा कुछ कर सकते हैं:

is_array() (
  array -d -- "$1"
) >/dev/null 2>&1

a=(1 2 3)
if is_array a; then
  echo array
fi

b=123
if ! is_array b; then
  echo not array
fi

यदि सरणी चर को आसानी से पढ़ा जाए तो यह दृष्टिकोण काम नहीं करेगा । एक संशोधित करने के लिए कोशिश कर रहा है केवल पढ़ने के लिए चर एक त्रुटि को प्रेरित किया:

$ a=()
$ readonly a
$ array -d a
array: $a is read-only

0
#!/bin/bash

var=BASH_SOURCE

[[ "$(declare -pa)" =~ [^[:alpha:]]$var= ]]

case "$?" in 
  0)
      echo "$var is an array variable"
      ;;
  1)
      echo "$var is not an array variable"
      ;;
  *)
      echo "Unknown exit code"
      ;;
esac
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.