Check variable is an array in Bourne like shell?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












13














In Bourne like shell which support array variable, we can use some parsing to check if variable is an array.



All commands below were run after running 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 and its derivative:



$ 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


An example in bash:



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


This approach is too much work and need to spawn a subshell. Using other shell builtin like =~ in [[ ... ]] do not need a subshell, but is still too complicated.



Is there easier way to accomplish this task?










share|improve this question























  • Under what circumstances would you need to check whether your variables are arrays or not?
    – Kusalananda
    Dec 20 '18 at 10:07















13














In Bourne like shell which support array variable, we can use some parsing to check if variable is an array.



All commands below were run after running 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 and its derivative:



$ 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


An example in bash:



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


This approach is too much work and need to spawn a subshell. Using other shell builtin like =~ in [[ ... ]] do not need a subshell, but is still too complicated.



Is there easier way to accomplish this task?










share|improve this question























  • Under what circumstances would you need to check whether your variables are arrays or not?
    – Kusalananda
    Dec 20 '18 at 10:07













13












13








13


0





In Bourne like shell which support array variable, we can use some parsing to check if variable is an array.



All commands below were run after running 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 and its derivative:



$ 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


An example in bash:



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


This approach is too much work and need to spawn a subshell. Using other shell builtin like =~ in [[ ... ]] do not need a subshell, but is still too complicated.



Is there easier way to accomplish this task?










share|improve this question















In Bourne like shell which support array variable, we can use some parsing to check if variable is an array.



All commands below were run after running 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 and its derivative:



$ 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


An example in bash:



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


This approach is too much work and need to spawn a subshell. Using other shell builtin like =~ in [[ ... ]] do not need a subshell, but is still too complicated.



Is there easier way to accomplish this task?







bash shell zsh ksh yash






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 28 '15 at 17:21

























asked Nov 28 '15 at 4:04









cuonglm

102k23201301




102k23201301











  • Under what circumstances would you need to check whether your variables are arrays or not?
    – Kusalananda
    Dec 20 '18 at 10:07
















  • Under what circumstances would you need to check whether your variables are arrays or not?
    – Kusalananda
    Dec 20 '18 at 10:07















Under what circumstances would you need to check whether your variables are arrays or not?
– Kusalananda
Dec 20 '18 at 10:07




Under what circumstances would you need to check whether your variables are arrays or not?
– Kusalananda
Dec 20 '18 at 10:07










8 Answers
8






active

oldest

votes


















9














I don't think you can, and I don't think it actually makes any difference.



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



x


That does the same thing in either of ksh93 and bash. It looks like possibly all variables are arrays in those shells, or at least any regular variable which has not been assigned special attributes, but I didn't check much of that.



The bash manual talks about different behaviors for an array versus a string variable when using += assignments, but it afterwards hedges and states that the the array only behaves differently in a compound assignment context.



It also states that a variable is considered an array if any subscript has been assigned a value - and explicitly includes the possibility of a null-string. Above you can see that a regular assignment definitely results in a subscript being assigned - and so I guess everything is an array.



Practically, possibly you can use:



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


...to clearly pinpoint set variables that have only been assigned a single subscript of value 0.






share|improve this answer






















  • So I guess checking if $a[1]-not array can do the task, can't it?
    – cuonglm
    Nov 28 '15 at 4:58











  • @cuonglm - Well, not according to the bash manual: An array variable is considered set if a subscript has been assigned a value. The null string is a valid value. If any subscript is assigned its an array per spec. In practice, also no, because you can do a[5]=x. I guess [ 1 -eq "$#a[@]" ] && [ -n "$a[0]+1" ] could work.
    – mikeserv
    Nov 28 '15 at 5:03



















5














So you effectively want just the middle part of declare -p without the junk around it?



You could write a macro such as:



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


so that you can do:



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


(A mere function won't do if you'll want to use this on function-local variables).




With aliases



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

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





share|improve this answer






















  • @mikeserv Good point. Aliases make it look prettier. +1
    – PSkocik
    Nov 28 '15 at 19:41











  • i meant - alias vartype="$VARTYPE"... or just not defining the $VARTYPE at all - it should work, right? you only should need that shopt thing in bash because it breaks with the spec regarding alias expansion in scripts.
    – mikeserv
    Nov 28 '15 at 19:42







  • 1




    @mikeserv I'm sure cuonglm is well capable of tweaking this approach to his needs and preferences. ;-)
    – PSkocik
    Nov 28 '15 at 19:46










  • ... and security considerations.
    – PSkocik
    Nov 28 '15 at 19:47










  • At no point does the above code eval user provided text. It's no less secure than a function. I've never seen you fussing about making functions read-only, but OK, I can mark the variable readonly.
    – PSkocik
    Nov 28 '15 at 20:06


















5














In zsh



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





share|improve this answer




















  • Maybe echo $(t)var is simpler. Thanks for this.
    – user79743
    Nov 28 '15 at 21:00


















3














To test variable var, with



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


It is possible to test if there are more than one array index:



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


If the first index value is not zero:



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


The only hard confusion is when there is only one index value and that value is zero (or one).



For that condition, it is possible to use a side effect of trying to remove an array element from a variable that is not an array:



**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


This works correctly for bash:



# 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."


For zsh the index may need to be 1 (unless a compatible mode is active).



The sub-shell is needed to avoid the side effect of erasing index 0 of var.



I have found no way to make it work in ksh.



Edit 1



This function only works in bash4.2+



getVarType() $varname=*) echo scalar; ;;
esac;


var=( foo bar ); getVarType var


Edit 2



This also work only for bash4.2+



 typeset -p var && echo "var is an array"


Note: This will give false positives if var contains the tested strings.






share|improve this answer






















  • How about array with zero elements?
    – cuonglm
    Nov 28 '15 at 8:32






  • 1




    Dat edit, tho. Looks very original. :D
    – PSkocik
    Nov 28 '15 at 20:30










  • @cuonglm The check ( unset "var[0]" 2>/dev/null; ) && echo "var is an array." correctly reports var is an array when var has been set to var=() an array with zero elements. It acts exactly equal to declare.
    – user79743
    Nov 29 '15 at 12:53










  • The test for scalar won't work if the scalar is exported or marked integer/lowercase/readonly... You can probably safely make it that any other non-empty output means scalar variable. I'd use grep -E instead of grep -P to avoid the dependency on GNU grep.
    – Stéphane Chazelas
    Nov 30 '15 at 9:30










  • @StéphaneChazelas The test (in bash) for scalar with integer and/or lowercase and /or readonly always start with -a, like this: declare -airl var='()'. Therefore the grep test will work.
    – user79743
    Nov 30 '15 at 17:32


















3














For bash, it's a little bit of a hack (albeit documented): attempt to use typeset to remove the "array" attribute:



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


(You cannot do this in zsh, it allows you to convert an array to a scalar, in bash it's explicitly forbidden.)



So:



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


Or in a function, noting the caveats at the end:



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



Note the use of typeset -g (bash-4.2 or later), this is required within a function so that typeset (syn. declare) doesn't work like local and clobber the value you are trying to inspect. This also does not handle function "variable" types, you can add another branch test using typeset -f if needed.




Another (nearly complete) option is to use this:



 $!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.


There's one slight problem though, an array with a single subscript of 0 matches two of the above conditions. This is something that mikeserv also references, bash really doesn't have a hard distinction, and some of this (if you check the Changelog) can be blamed on ksh and compatibilty with how $name[*] or $name[@] behave on a non-array.



So a partial solution is:



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


I have used in the past a variation on this:



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


this too needs a subshell though.



One more possibly useful technique is compgen:



compgen -A arrayvar


This will list all indexed arrays, however associative arrays are not handled specially (up to bash-4.4) and appear as regular variables (compgen -A variable)






share|improve this answer






















  • The typeset +a also reports an error in ksh. Not in zsh, though.
    – user79743
    Nov 28 '15 at 20:38


















1














Short answer:



For the two shells that introduced this notation (bash and ksh93) a scalar variable is just an array with a single element.



Neither needs a special declaration to create an array. Just the assignment is enough, and a plain assignment var=value is identical to var[0]=value.






share|improve this answer




















  • Try: bash -c 'unset var; var=foo; typeset -p var'. Do bash answer report an array (needs an -a)?. Now compare with: bash -c 'unset var; var[12]=foo; typeset -p var'. Why is there a difference?. A: The shell maintains (for good or for bad) a notion of which vars are scalars or arrays. The shell ksh do mix both concepts into one.
    – user79743
    Nov 29 '15 at 23:23


















1














yash's array builtin has some options that only work with array variables. Example: the -d option will report an error on non-array variable:



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


So we can do something like this:



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



This approach won't work if array variable is readonly. Trying to modify a readonly variable leading to an error:



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





share|improve this answer






























    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





    share|improve this answer




















      Your Answer








      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "106"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













      draft saved

      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f246026%2fcheck-variable-is-an-array-in-bourne-like-shell%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      8 Answers
      8






      active

      oldest

      votes








      8 Answers
      8






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      9














      I don't think you can, and I don't think it actually makes any difference.



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



      x


      That does the same thing in either of ksh93 and bash. It looks like possibly all variables are arrays in those shells, or at least any regular variable which has not been assigned special attributes, but I didn't check much of that.



      The bash manual talks about different behaviors for an array versus a string variable when using += assignments, but it afterwards hedges and states that the the array only behaves differently in a compound assignment context.



      It also states that a variable is considered an array if any subscript has been assigned a value - and explicitly includes the possibility of a null-string. Above you can see that a regular assignment definitely results in a subscript being assigned - and so I guess everything is an array.



      Practically, possibly you can use:



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


      ...to clearly pinpoint set variables that have only been assigned a single subscript of value 0.






      share|improve this answer






















      • So I guess checking if $a[1]-not array can do the task, can't it?
        – cuonglm
        Nov 28 '15 at 4:58











      • @cuonglm - Well, not according to the bash manual: An array variable is considered set if a subscript has been assigned a value. The null string is a valid value. If any subscript is assigned its an array per spec. In practice, also no, because you can do a[5]=x. I guess [ 1 -eq "$#a[@]" ] && [ -n "$a[0]+1" ] could work.
        – mikeserv
        Nov 28 '15 at 5:03
















      9














      I don't think you can, and I don't think it actually makes any difference.



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



      x


      That does the same thing in either of ksh93 and bash. It looks like possibly all variables are arrays in those shells, or at least any regular variable which has not been assigned special attributes, but I didn't check much of that.



      The bash manual talks about different behaviors for an array versus a string variable when using += assignments, but it afterwards hedges and states that the the array only behaves differently in a compound assignment context.



      It also states that a variable is considered an array if any subscript has been assigned a value - and explicitly includes the possibility of a null-string. Above you can see that a regular assignment definitely results in a subscript being assigned - and so I guess everything is an array.



      Practically, possibly you can use:



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


      ...to clearly pinpoint set variables that have only been assigned a single subscript of value 0.






      share|improve this answer






















      • So I guess checking if $a[1]-not array can do the task, can't it?
        – cuonglm
        Nov 28 '15 at 4:58











      • @cuonglm - Well, not according to the bash manual: An array variable is considered set if a subscript has been assigned a value. The null string is a valid value. If any subscript is assigned its an array per spec. In practice, also no, because you can do a[5]=x. I guess [ 1 -eq "$#a[@]" ] && [ -n "$a[0]+1" ] could work.
        – mikeserv
        Nov 28 '15 at 5:03














      9












      9








      9






      I don't think you can, and I don't think it actually makes any difference.



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



      x


      That does the same thing in either of ksh93 and bash. It looks like possibly all variables are arrays in those shells, or at least any regular variable which has not been assigned special attributes, but I didn't check much of that.



      The bash manual talks about different behaviors for an array versus a string variable when using += assignments, but it afterwards hedges and states that the the array only behaves differently in a compound assignment context.



      It also states that a variable is considered an array if any subscript has been assigned a value - and explicitly includes the possibility of a null-string. Above you can see that a regular assignment definitely results in a subscript being assigned - and so I guess everything is an array.



      Practically, possibly you can use:



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


      ...to clearly pinpoint set variables that have only been assigned a single subscript of value 0.






      share|improve this answer














      I don't think you can, and I don't think it actually makes any difference.



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



      x


      That does the same thing in either of ksh93 and bash. It looks like possibly all variables are arrays in those shells, or at least any regular variable which has not been assigned special attributes, but I didn't check much of that.



      The bash manual talks about different behaviors for an array versus a string variable when using += assignments, but it afterwards hedges and states that the the array only behaves differently in a compound assignment context.



      It also states that a variable is considered an array if any subscript has been assigned a value - and explicitly includes the possibility of a null-string. Above you can see that a regular assignment definitely results in a subscript being assigned - and so I guess everything is an array.



      Practically, possibly you can use:



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


      ...to clearly pinpoint set variables that have only been assigned a single subscript of value 0.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 28 '15 at 5:09

























      answered Nov 28 '15 at 4:52









      mikeserv

      45.3k567153




      45.3k567153











      • So I guess checking if $a[1]-not array can do the task, can't it?
        – cuonglm
        Nov 28 '15 at 4:58











      • @cuonglm - Well, not according to the bash manual: An array variable is considered set if a subscript has been assigned a value. The null string is a valid value. If any subscript is assigned its an array per spec. In practice, also no, because you can do a[5]=x. I guess [ 1 -eq "$#a[@]" ] && [ -n "$a[0]+1" ] could work.
        – mikeserv
        Nov 28 '15 at 5:03

















      • So I guess checking if $a[1]-not array can do the task, can't it?
        – cuonglm
        Nov 28 '15 at 4:58











      • @cuonglm - Well, not according to the bash manual: An array variable is considered set if a subscript has been assigned a value. The null string is a valid value. If any subscript is assigned its an array per spec. In practice, also no, because you can do a[5]=x. I guess [ 1 -eq "$#a[@]" ] && [ -n "$a[0]+1" ] could work.
        – mikeserv
        Nov 28 '15 at 5:03
















      So I guess checking if $a[1]-not array can do the task, can't it?
      – cuonglm
      Nov 28 '15 at 4:58





      So I guess checking if $a[1]-not array can do the task, can't it?
      – cuonglm
      Nov 28 '15 at 4:58













      @cuonglm - Well, not according to the bash manual: An array variable is considered set if a subscript has been assigned a value. The null string is a valid value. If any subscript is assigned its an array per spec. In practice, also no, because you can do a[5]=x. I guess [ 1 -eq "$#a[@]" ] && [ -n "$a[0]+1" ] could work.
      – mikeserv
      Nov 28 '15 at 5:03





      @cuonglm - Well, not according to the bash manual: An array variable is considered set if a subscript has been assigned a value. The null string is a valid value. If any subscript is assigned its an array per spec. In practice, also no, because you can do a[5]=x. I guess [ 1 -eq "$#a[@]" ] && [ -n "$a[0]+1" ] could work.
      – mikeserv
      Nov 28 '15 at 5:03














      5














      So you effectively want just the middle part of declare -p without the junk around it?



      You could write a macro such as:



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


      so that you can do:



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


      (A mere function won't do if you'll want to use this on function-local variables).




      With aliases



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

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





      share|improve this answer






















      • @mikeserv Good point. Aliases make it look prettier. +1
        – PSkocik
        Nov 28 '15 at 19:41











      • i meant - alias vartype="$VARTYPE"... or just not defining the $VARTYPE at all - it should work, right? you only should need that shopt thing in bash because it breaks with the spec regarding alias expansion in scripts.
        – mikeserv
        Nov 28 '15 at 19:42







      • 1




        @mikeserv I'm sure cuonglm is well capable of tweaking this approach to his needs and preferences. ;-)
        – PSkocik
        Nov 28 '15 at 19:46










      • ... and security considerations.
        – PSkocik
        Nov 28 '15 at 19:47










      • At no point does the above code eval user provided text. It's no less secure than a function. I've never seen you fussing about making functions read-only, but OK, I can mark the variable readonly.
        – PSkocik
        Nov 28 '15 at 20:06















      5














      So you effectively want just the middle part of declare -p without the junk around it?



      You could write a macro such as:



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


      so that you can do:



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


      (A mere function won't do if you'll want to use this on function-local variables).




      With aliases



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

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





      share|improve this answer






















      • @mikeserv Good point. Aliases make it look prettier. +1
        – PSkocik
        Nov 28 '15 at 19:41











      • i meant - alias vartype="$VARTYPE"... or just not defining the $VARTYPE at all - it should work, right? you only should need that shopt thing in bash because it breaks with the spec regarding alias expansion in scripts.
        – mikeserv
        Nov 28 '15 at 19:42







      • 1




        @mikeserv I'm sure cuonglm is well capable of tweaking this approach to his needs and preferences. ;-)
        – PSkocik
        Nov 28 '15 at 19:46










      • ... and security considerations.
        – PSkocik
        Nov 28 '15 at 19:47










      • At no point does the above code eval user provided text. It's no less secure than a function. I've never seen you fussing about making functions read-only, but OK, I can mark the variable readonly.
        – PSkocik
        Nov 28 '15 at 20:06













      5












      5








      5






      So you effectively want just the middle part of declare -p without the junk around it?



      You could write a macro such as:



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


      so that you can do:



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


      (A mere function won't do if you'll want to use this on function-local variables).




      With aliases



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

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





      share|improve this answer














      So you effectively want just the middle part of declare -p without the junk around it?



      You could write a macro such as:



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


      so that you can do:



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


      (A mere function won't do if you'll want to use this on function-local variables).




      With aliases



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

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






      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 28 '15 at 20:06

























      answered Nov 28 '15 at 11:47









      PSkocik

      17.8k44994




      17.8k44994











      • @mikeserv Good point. Aliases make it look prettier. +1
        – PSkocik
        Nov 28 '15 at 19:41











      • i meant - alias vartype="$VARTYPE"... or just not defining the $VARTYPE at all - it should work, right? you only should need that shopt thing in bash because it breaks with the spec regarding alias expansion in scripts.
        – mikeserv
        Nov 28 '15 at 19:42







      • 1




        @mikeserv I'm sure cuonglm is well capable of tweaking this approach to his needs and preferences. ;-)
        – PSkocik
        Nov 28 '15 at 19:46










      • ... and security considerations.
        – PSkocik
        Nov 28 '15 at 19:47










      • At no point does the above code eval user provided text. It's no less secure than a function. I've never seen you fussing about making functions read-only, but OK, I can mark the variable readonly.
        – PSkocik
        Nov 28 '15 at 20:06
















      • @mikeserv Good point. Aliases make it look prettier. +1
        – PSkocik
        Nov 28 '15 at 19:41











      • i meant - alias vartype="$VARTYPE"... or just not defining the $VARTYPE at all - it should work, right? you only should need that shopt thing in bash because it breaks with the spec regarding alias expansion in scripts.
        – mikeserv
        Nov 28 '15 at 19:42







      • 1




        @mikeserv I'm sure cuonglm is well capable of tweaking this approach to his needs and preferences. ;-)
        – PSkocik
        Nov 28 '15 at 19:46










      • ... and security considerations.
        – PSkocik
        Nov 28 '15 at 19:47










      • At no point does the above code eval user provided text. It's no less secure than a function. I've never seen you fussing about making functions read-only, but OK, I can mark the variable readonly.
        – PSkocik
        Nov 28 '15 at 20:06















      @mikeserv Good point. Aliases make it look prettier. +1
      – PSkocik
      Nov 28 '15 at 19:41





      @mikeserv Good point. Aliases make it look prettier. +1
      – PSkocik
      Nov 28 '15 at 19:41













      i meant - alias vartype="$VARTYPE"... or just not defining the $VARTYPE at all - it should work, right? you only should need that shopt thing in bash because it breaks with the spec regarding alias expansion in scripts.
      – mikeserv
      Nov 28 '15 at 19:42





      i meant - alias vartype="$VARTYPE"... or just not defining the $VARTYPE at all - it should work, right? you only should need that shopt thing in bash because it breaks with the spec regarding alias expansion in scripts.
      – mikeserv
      Nov 28 '15 at 19:42





      1




      1




      @mikeserv I'm sure cuonglm is well capable of tweaking this approach to his needs and preferences. ;-)
      – PSkocik
      Nov 28 '15 at 19:46




      @mikeserv I'm sure cuonglm is well capable of tweaking this approach to his needs and preferences. ;-)
      – PSkocik
      Nov 28 '15 at 19:46












      ... and security considerations.
      – PSkocik
      Nov 28 '15 at 19:47




      ... and security considerations.
      – PSkocik
      Nov 28 '15 at 19:47












      At no point does the above code eval user provided text. It's no less secure than a function. I've never seen you fussing about making functions read-only, but OK, I can mark the variable readonly.
      – PSkocik
      Nov 28 '15 at 20:06




      At no point does the above code eval user provided text. It's no less secure than a function. I've never seen you fussing about making functions read-only, but OK, I can mark the variable readonly.
      – PSkocik
      Nov 28 '15 at 20:06











      5














      In zsh



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





      share|improve this answer




















      • Maybe echo $(t)var is simpler. Thanks for this.
        – user79743
        Nov 28 '15 at 21:00















      5














      In zsh



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





      share|improve this answer




















      • Maybe echo $(t)var is simpler. Thanks for this.
        – user79743
        Nov 28 '15 at 21:00













      5












      5








      5






      In zsh



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





      share|improve this answer












      In zsh



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






      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Nov 28 '15 at 20:16









      llua

      4,6991420




      4,6991420











      • Maybe echo $(t)var is simpler. Thanks for this.
        – user79743
        Nov 28 '15 at 21:00
















      • Maybe echo $(t)var is simpler. Thanks for this.
        – user79743
        Nov 28 '15 at 21:00















      Maybe echo $(t)var is simpler. Thanks for this.
      – user79743
      Nov 28 '15 at 21:00




      Maybe echo $(t)var is simpler. Thanks for this.
      – user79743
      Nov 28 '15 at 21:00











      3














      To test variable var, with



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


      It is possible to test if there are more than one array index:



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


      If the first index value is not zero:



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


      The only hard confusion is when there is only one index value and that value is zero (or one).



      For that condition, it is possible to use a side effect of trying to remove an array element from a variable that is not an array:



      **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


      This works correctly for bash:



      # 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."


      For zsh the index may need to be 1 (unless a compatible mode is active).



      The sub-shell is needed to avoid the side effect of erasing index 0 of var.



      I have found no way to make it work in ksh.



      Edit 1



      This function only works in bash4.2+



      getVarType() $varname=*) echo scalar; ;;
      esac;


      var=( foo bar ); getVarType var


      Edit 2



      This also work only for bash4.2+



       typeset -p var && echo "var is an array"


      Note: This will give false positives if var contains the tested strings.






      share|improve this answer






















      • How about array with zero elements?
        – cuonglm
        Nov 28 '15 at 8:32






      • 1




        Dat edit, tho. Looks very original. :D
        – PSkocik
        Nov 28 '15 at 20:30










      • @cuonglm The check ( unset "var[0]" 2>/dev/null; ) && echo "var is an array." correctly reports var is an array when var has been set to var=() an array with zero elements. It acts exactly equal to declare.
        – user79743
        Nov 29 '15 at 12:53










      • The test for scalar won't work if the scalar is exported or marked integer/lowercase/readonly... You can probably safely make it that any other non-empty output means scalar variable. I'd use grep -E instead of grep -P to avoid the dependency on GNU grep.
        – Stéphane Chazelas
        Nov 30 '15 at 9:30










      • @StéphaneChazelas The test (in bash) for scalar with integer and/or lowercase and /or readonly always start with -a, like this: declare -airl var='()'. Therefore the grep test will work.
        – user79743
        Nov 30 '15 at 17:32















      3














      To test variable var, with



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


      It is possible to test if there are more than one array index:



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


      If the first index value is not zero:



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


      The only hard confusion is when there is only one index value and that value is zero (or one).



      For that condition, it is possible to use a side effect of trying to remove an array element from a variable that is not an array:



      **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


      This works correctly for bash:



      # 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."


      For zsh the index may need to be 1 (unless a compatible mode is active).



      The sub-shell is needed to avoid the side effect of erasing index 0 of var.



      I have found no way to make it work in ksh.



      Edit 1



      This function only works in bash4.2+



      getVarType() $varname=*) echo scalar; ;;
      esac;


      var=( foo bar ); getVarType var


      Edit 2



      This also work only for bash4.2+



       typeset -p var && echo "var is an array"


      Note: This will give false positives if var contains the tested strings.






      share|improve this answer






















      • How about array with zero elements?
        – cuonglm
        Nov 28 '15 at 8:32






      • 1




        Dat edit, tho. Looks very original. :D
        – PSkocik
        Nov 28 '15 at 20:30










      • @cuonglm The check ( unset "var[0]" 2>/dev/null; ) && echo "var is an array." correctly reports var is an array when var has been set to var=() an array with zero elements. It acts exactly equal to declare.
        – user79743
        Nov 29 '15 at 12:53










      • The test for scalar won't work if the scalar is exported or marked integer/lowercase/readonly... You can probably safely make it that any other non-empty output means scalar variable. I'd use grep -E instead of grep -P to avoid the dependency on GNU grep.
        – Stéphane Chazelas
        Nov 30 '15 at 9:30










      • @StéphaneChazelas The test (in bash) for scalar with integer and/or lowercase and /or readonly always start with -a, like this: declare -airl var='()'. Therefore the grep test will work.
        – user79743
        Nov 30 '15 at 17:32













      3












      3








      3






      To test variable var, with



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


      It is possible to test if there are more than one array index:



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


      If the first index value is not zero:



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


      The only hard confusion is when there is only one index value and that value is zero (or one).



      For that condition, it is possible to use a side effect of trying to remove an array element from a variable that is not an array:



      **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


      This works correctly for bash:



      # 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."


      For zsh the index may need to be 1 (unless a compatible mode is active).



      The sub-shell is needed to avoid the side effect of erasing index 0 of var.



      I have found no way to make it work in ksh.



      Edit 1



      This function only works in bash4.2+



      getVarType() $varname=*) echo scalar; ;;
      esac;


      var=( foo bar ); getVarType var


      Edit 2



      This also work only for bash4.2+



       typeset -p var && echo "var is an array"


      Note: This will give false positives if var contains the tested strings.






      share|improve this answer














      To test variable var, with



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


      It is possible to test if there are more than one array index:



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


      If the first index value is not zero:



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


      The only hard confusion is when there is only one index value and that value is zero (or one).



      For that condition, it is possible to use a side effect of trying to remove an array element from a variable that is not an array:



      **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


      This works correctly for bash:



      # 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."


      For zsh the index may need to be 1 (unless a compatible mode is active).



      The sub-shell is needed to avoid the side effect of erasing index 0 of var.



      I have found no way to make it work in ksh.



      Edit 1



      This function only works in bash4.2+



      getVarType() $varname=*) echo scalar; ;;
      esac;


      var=( foo bar ); getVarType var


      Edit 2



      This also work only for bash4.2+



       typeset -p var && echo "var is an array"


      Note: This will give false positives if var contains the tested strings.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 29 '15 at 23:08

























      answered Nov 28 '15 at 8:30







      user79743


















      • How about array with zero elements?
        – cuonglm
        Nov 28 '15 at 8:32






      • 1




        Dat edit, tho. Looks very original. :D
        – PSkocik
        Nov 28 '15 at 20:30










      • @cuonglm The check ( unset "var[0]" 2>/dev/null; ) && echo "var is an array." correctly reports var is an array when var has been set to var=() an array with zero elements. It acts exactly equal to declare.
        – user79743
        Nov 29 '15 at 12:53










      • The test for scalar won't work if the scalar is exported or marked integer/lowercase/readonly... You can probably safely make it that any other non-empty output means scalar variable. I'd use grep -E instead of grep -P to avoid the dependency on GNU grep.
        – Stéphane Chazelas
        Nov 30 '15 at 9:30










      • @StéphaneChazelas The test (in bash) for scalar with integer and/or lowercase and /or readonly always start with -a, like this: declare -airl var='()'. Therefore the grep test will work.
        – user79743
        Nov 30 '15 at 17:32
















      • How about array with zero elements?
        – cuonglm
        Nov 28 '15 at 8:32






      • 1




        Dat edit, tho. Looks very original. :D
        – PSkocik
        Nov 28 '15 at 20:30










      • @cuonglm The check ( unset "var[0]" 2>/dev/null; ) && echo "var is an array." correctly reports var is an array when var has been set to var=() an array with zero elements. It acts exactly equal to declare.
        – user79743
        Nov 29 '15 at 12:53










      • The test for scalar won't work if the scalar is exported or marked integer/lowercase/readonly... You can probably safely make it that any other non-empty output means scalar variable. I'd use grep -E instead of grep -P to avoid the dependency on GNU grep.
        – Stéphane Chazelas
        Nov 30 '15 at 9:30










      • @StéphaneChazelas The test (in bash) for scalar with integer and/or lowercase and /or readonly always start with -a, like this: declare -airl var='()'. Therefore the grep test will work.
        – user79743
        Nov 30 '15 at 17:32















      How about array with zero elements?
      – cuonglm
      Nov 28 '15 at 8:32




      How about array with zero elements?
      – cuonglm
      Nov 28 '15 at 8:32




      1




      1




      Dat edit, tho. Looks very original. :D
      – PSkocik
      Nov 28 '15 at 20:30




      Dat edit, tho. Looks very original. :D
      – PSkocik
      Nov 28 '15 at 20:30












      @cuonglm The check ( unset "var[0]" 2>/dev/null; ) && echo "var is an array." correctly reports var is an array when var has been set to var=() an array with zero elements. It acts exactly equal to declare.
      – user79743
      Nov 29 '15 at 12:53




      @cuonglm The check ( unset "var[0]" 2>/dev/null; ) && echo "var is an array." correctly reports var is an array when var has been set to var=() an array with zero elements. It acts exactly equal to declare.
      – user79743
      Nov 29 '15 at 12:53












      The test for scalar won't work if the scalar is exported or marked integer/lowercase/readonly... You can probably safely make it that any other non-empty output means scalar variable. I'd use grep -E instead of grep -P to avoid the dependency on GNU grep.
      – Stéphane Chazelas
      Nov 30 '15 at 9:30




      The test for scalar won't work if the scalar is exported or marked integer/lowercase/readonly... You can probably safely make it that any other non-empty output means scalar variable. I'd use grep -E instead of grep -P to avoid the dependency on GNU grep.
      – Stéphane Chazelas
      Nov 30 '15 at 9:30












      @StéphaneChazelas The test (in bash) for scalar with integer and/or lowercase and /or readonly always start with -a, like this: declare -airl var='()'. Therefore the grep test will work.
      – user79743
      Nov 30 '15 at 17:32




      @StéphaneChazelas The test (in bash) for scalar with integer and/or lowercase and /or readonly always start with -a, like this: declare -airl var='()'. Therefore the grep test will work.
      – user79743
      Nov 30 '15 at 17:32











      3














      For bash, it's a little bit of a hack (albeit documented): attempt to use typeset to remove the "array" attribute:



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


      (You cannot do this in zsh, it allows you to convert an array to a scalar, in bash it's explicitly forbidden.)



      So:



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


      Or in a function, noting the caveats at the end:



      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



      Note the use of typeset -g (bash-4.2 or later), this is required within a function so that typeset (syn. declare) doesn't work like local and clobber the value you are trying to inspect. This also does not handle function "variable" types, you can add another branch test using typeset -f if needed.




      Another (nearly complete) option is to use this:



       $!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.


      There's one slight problem though, an array with a single subscript of 0 matches two of the above conditions. This is something that mikeserv also references, bash really doesn't have a hard distinction, and some of this (if you check the Changelog) can be blamed on ksh and compatibilty with how $name[*] or $name[@] behave on a non-array.



      So a partial solution is:



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


      I have used in the past a variation on this:



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


      this too needs a subshell though.



      One more possibly useful technique is compgen:



      compgen -A arrayvar


      This will list all indexed arrays, however associative arrays are not handled specially (up to bash-4.4) and appear as regular variables (compgen -A variable)






      share|improve this answer






















      • The typeset +a also reports an error in ksh. Not in zsh, though.
        – user79743
        Nov 28 '15 at 20:38















      3














      For bash, it's a little bit of a hack (albeit documented): attempt to use typeset to remove the "array" attribute:



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


      (You cannot do this in zsh, it allows you to convert an array to a scalar, in bash it's explicitly forbidden.)



      So:



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


      Or in a function, noting the caveats at the end:



      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



      Note the use of typeset -g (bash-4.2 or later), this is required within a function so that typeset (syn. declare) doesn't work like local and clobber the value you are trying to inspect. This also does not handle function "variable" types, you can add another branch test using typeset -f if needed.




      Another (nearly complete) option is to use this:



       $!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.


      There's one slight problem though, an array with a single subscript of 0 matches two of the above conditions. This is something that mikeserv also references, bash really doesn't have a hard distinction, and some of this (if you check the Changelog) can be blamed on ksh and compatibilty with how $name[*] or $name[@] behave on a non-array.



      So a partial solution is:



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


      I have used in the past a variation on this:



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


      this too needs a subshell though.



      One more possibly useful technique is compgen:



      compgen -A arrayvar


      This will list all indexed arrays, however associative arrays are not handled specially (up to bash-4.4) and appear as regular variables (compgen -A variable)






      share|improve this answer






















      • The typeset +a also reports an error in ksh. Not in zsh, though.
        – user79743
        Nov 28 '15 at 20:38













      3












      3








      3






      For bash, it's a little bit of a hack (albeit documented): attempt to use typeset to remove the "array" attribute:



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


      (You cannot do this in zsh, it allows you to convert an array to a scalar, in bash it's explicitly forbidden.)



      So:



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


      Or in a function, noting the caveats at the end:



      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



      Note the use of typeset -g (bash-4.2 or later), this is required within a function so that typeset (syn. declare) doesn't work like local and clobber the value you are trying to inspect. This also does not handle function "variable" types, you can add another branch test using typeset -f if needed.




      Another (nearly complete) option is to use this:



       $!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.


      There's one slight problem though, an array with a single subscript of 0 matches two of the above conditions. This is something that mikeserv also references, bash really doesn't have a hard distinction, and some of this (if you check the Changelog) can be blamed on ksh and compatibilty with how $name[*] or $name[@] behave on a non-array.



      So a partial solution is:



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


      I have used in the past a variation on this:



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


      this too needs a subshell though.



      One more possibly useful technique is compgen:



      compgen -A arrayvar


      This will list all indexed arrays, however associative arrays are not handled specially (up to bash-4.4) and appear as regular variables (compgen -A variable)






      share|improve this answer














      For bash, it's a little bit of a hack (albeit documented): attempt to use typeset to remove the "array" attribute:



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


      (You cannot do this in zsh, it allows you to convert an array to a scalar, in bash it's explicitly forbidden.)



      So:



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


      Or in a function, noting the caveats at the end:



      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



      Note the use of typeset -g (bash-4.2 or later), this is required within a function so that typeset (syn. declare) doesn't work like local and clobber the value you are trying to inspect. This also does not handle function "variable" types, you can add another branch test using typeset -f if needed.




      Another (nearly complete) option is to use this:



       $!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.


      There's one slight problem though, an array with a single subscript of 0 matches two of the above conditions. This is something that mikeserv also references, bash really doesn't have a hard distinction, and some of this (if you check the Changelog) can be blamed on ksh and compatibilty with how $name[*] or $name[@] behave on a non-array.



      So a partial solution is:



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


      I have used in the past a variation on this:



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


      this too needs a subshell though.



      One more possibly useful technique is compgen:



      compgen -A arrayvar


      This will list all indexed arrays, however associative arrays are not handled specially (up to bash-4.4) and appear as regular variables (compgen -A variable)







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Oct 5 '17 at 12:19

























      answered Nov 28 '15 at 16:39









      mr.spuratic

      6,7761028




      6,7761028











      • The typeset +a also reports an error in ksh. Not in zsh, though.
        – user79743
        Nov 28 '15 at 20:38
















      • The typeset +a also reports an error in ksh. Not in zsh, though.
        – user79743
        Nov 28 '15 at 20:38















      The typeset +a also reports an error in ksh. Not in zsh, though.
      – user79743
      Nov 28 '15 at 20:38




      The typeset +a also reports an error in ksh. Not in zsh, though.
      – user79743
      Nov 28 '15 at 20:38











      1














      Short answer:



      For the two shells that introduced this notation (bash and ksh93) a scalar variable is just an array with a single element.



      Neither needs a special declaration to create an array. Just the assignment is enough, and a plain assignment var=value is identical to var[0]=value.






      share|improve this answer




















      • Try: bash -c 'unset var; var=foo; typeset -p var'. Do bash answer report an array (needs an -a)?. Now compare with: bash -c 'unset var; var[12]=foo; typeset -p var'. Why is there a difference?. A: The shell maintains (for good or for bad) a notion of which vars are scalars or arrays. The shell ksh do mix both concepts into one.
        – user79743
        Nov 29 '15 at 23:23















      1














      Short answer:



      For the two shells that introduced this notation (bash and ksh93) a scalar variable is just an array with a single element.



      Neither needs a special declaration to create an array. Just the assignment is enough, and a plain assignment var=value is identical to var[0]=value.






      share|improve this answer




















      • Try: bash -c 'unset var; var=foo; typeset -p var'. Do bash answer report an array (needs an -a)?. Now compare with: bash -c 'unset var; var[12]=foo; typeset -p var'. Why is there a difference?. A: The shell maintains (for good or for bad) a notion of which vars are scalars or arrays. The shell ksh do mix both concepts into one.
        – user79743
        Nov 29 '15 at 23:23













      1












      1








      1






      Short answer:



      For the two shells that introduced this notation (bash and ksh93) a scalar variable is just an array with a single element.



      Neither needs a special declaration to create an array. Just the assignment is enough, and a plain assignment var=value is identical to var[0]=value.






      share|improve this answer












      Short answer:



      For the two shells that introduced this notation (bash and ksh93) a scalar variable is just an array with a single element.



      Neither needs a special declaration to create an array. Just the assignment is enough, and a plain assignment var=value is identical to var[0]=value.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Nov 29 '15 at 22:41









      Henk Langeveld

      567213




      567213











      • Try: bash -c 'unset var; var=foo; typeset -p var'. Do bash answer report an array (needs an -a)?. Now compare with: bash -c 'unset var; var[12]=foo; typeset -p var'. Why is there a difference?. A: The shell maintains (for good or for bad) a notion of which vars are scalars or arrays. The shell ksh do mix both concepts into one.
        – user79743
        Nov 29 '15 at 23:23
















      • Try: bash -c 'unset var; var=foo; typeset -p var'. Do bash answer report an array (needs an -a)?. Now compare with: bash -c 'unset var; var[12]=foo; typeset -p var'. Why is there a difference?. A: The shell maintains (for good or for bad) a notion of which vars are scalars or arrays. The shell ksh do mix both concepts into one.
        – user79743
        Nov 29 '15 at 23:23















      Try: bash -c 'unset var; var=foo; typeset -p var'. Do bash answer report an array (needs an -a)?. Now compare with: bash -c 'unset var; var[12]=foo; typeset -p var'. Why is there a difference?. A: The shell maintains (for good or for bad) a notion of which vars are scalars or arrays. The shell ksh do mix both concepts into one.
      – user79743
      Nov 29 '15 at 23:23




      Try: bash -c 'unset var; var=foo; typeset -p var'. Do bash answer report an array (needs an -a)?. Now compare with: bash -c 'unset var; var[12]=foo; typeset -p var'. Why is there a difference?. A: The shell maintains (for good or for bad) a notion of which vars are scalars or arrays. The shell ksh do mix both concepts into one.
      – user79743
      Nov 29 '15 at 23:23











      1














      yash's array builtin has some options that only work with array variables. Example: the -d option will report an error on non-array variable:



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


      So we can do something like this:



      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



      This approach won't work if array variable is readonly. Trying to modify a readonly variable leading to an error:



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





      share|improve this answer



























        1














        yash's array builtin has some options that only work with array variables. Example: the -d option will report an error on non-array variable:



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


        So we can do something like this:



        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



        This approach won't work if array variable is readonly. Trying to modify a readonly variable leading to an error:



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





        share|improve this answer

























          1












          1








          1






          yash's array builtin has some options that only work with array variables. Example: the -d option will report an error on non-array variable:



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


          So we can do something like this:



          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



          This approach won't work if array variable is readonly. Trying to modify a readonly variable leading to an error:



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





          share|improve this answer














          yash's array builtin has some options that only work with array variables. Example: the -d option will report an error on non-array variable:



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


          So we can do something like this:



          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



          This approach won't work if array variable is readonly. Trying to modify a readonly variable leading to an error:



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






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 30 '15 at 18:19

























          answered Nov 30 '15 at 7:19









          cuonglm

          102k23201301




          102k23201301





















              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





              share|improve this answer

























                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





                share|improve this answer























                  0












                  0








                  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





                  share|improve this answer












                  #!/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






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Dec 20 '18 at 9:56









                  Fólkvangr

                  32912




                  32912



























                      draft saved

                      draft discarded
















































                      Thanks for contributing an answer to Unix & Linux Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f246026%2fcheck-variable-is-an-array-in-bourne-like-shell%23new-answer', 'question_page');

                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown






                      Popular posts from this blog

                      How to check contact read email or not when send email to Individual?

                      Bahrain

                      Postfix configuration issue with fips on centos 7; mailgun relay