Zsh, Indirect array variable assignment without using eval

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











up vote
2
down vote

favorite












I have a variable VARNAME which contains a name of another variable. I'd like to assign to this another variable without using eval. How can I do that?



Th reason I don't want to use eval is the following. Assume first a function for prepending foo variable:



% prepend_foo() foo=("$@" $foo) 
% foo=(1 2)
% print -l $foo
1
2
% prepend_foo x 'a b c' y
% print -l $foo
x
a b c
y
1
2
%


Now consider a generalized function for appending to any variable:



% prepend() var=$1; shift; eval "$var=($@ $(P)var)" 
% foo=(1 2)
% print -l $foo
1
2
% prepend foo x 'a b c' y
% print -l $foo
x
a
b
c
y
1
2
%


As you can see, variables with spaces are split into several array items. I couldn't properly combine quote to achieve desired thing.




On IRC, someone suggested using $name::=word, but that doesn't work well with arrays:



21:23 < someone> > b=(bar baz); a=b; : $(P)a::=(foo $(P)a); typeset -p b
21:23 < machabot> someone: typeset b='(foo bar baz)'
21:23 < someone> dammit that's a string






share|improve this question























    up vote
    2
    down vote

    favorite












    I have a variable VARNAME which contains a name of another variable. I'd like to assign to this another variable without using eval. How can I do that?



    Th reason I don't want to use eval is the following. Assume first a function for prepending foo variable:



    % prepend_foo() foo=("$@" $foo) 
    % foo=(1 2)
    % print -l $foo
    1
    2
    % prepend_foo x 'a b c' y
    % print -l $foo
    x
    a b c
    y
    1
    2
    %


    Now consider a generalized function for appending to any variable:



    % prepend() var=$1; shift; eval "$var=($@ $(P)var)" 
    % foo=(1 2)
    % print -l $foo
    1
    2
    % prepend foo x 'a b c' y
    % print -l $foo
    x
    a
    b
    c
    y
    1
    2
    %


    As you can see, variables with spaces are split into several array items. I couldn't properly combine quote to achieve desired thing.




    On IRC, someone suggested using $name::=word, but that doesn't work well with arrays:



    21:23 < someone> > b=(bar baz); a=b; : $(P)a::=(foo $(P)a); typeset -p b
    21:23 < machabot> someone: typeset b='(foo bar baz)'
    21:23 < someone> dammit that's a string






    share|improve this question





















      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      I have a variable VARNAME which contains a name of another variable. I'd like to assign to this another variable without using eval. How can I do that?



      Th reason I don't want to use eval is the following. Assume first a function for prepending foo variable:



      % prepend_foo() foo=("$@" $foo) 
      % foo=(1 2)
      % print -l $foo
      1
      2
      % prepend_foo x 'a b c' y
      % print -l $foo
      x
      a b c
      y
      1
      2
      %


      Now consider a generalized function for appending to any variable:



      % prepend() var=$1; shift; eval "$var=($@ $(P)var)" 
      % foo=(1 2)
      % print -l $foo
      1
      2
      % prepend foo x 'a b c' y
      % print -l $foo
      x
      a
      b
      c
      y
      1
      2
      %


      As you can see, variables with spaces are split into several array items. I couldn't properly combine quote to achieve desired thing.




      On IRC, someone suggested using $name::=word, but that doesn't work well with arrays:



      21:23 < someone> > b=(bar baz); a=b; : $(P)a::=(foo $(P)a); typeset -p b
      21:23 < machabot> someone: typeset b='(foo bar baz)'
      21:23 < someone> dammit that's a string






      share|improve this question











      I have a variable VARNAME which contains a name of another variable. I'd like to assign to this another variable without using eval. How can I do that?



      Th reason I don't want to use eval is the following. Assume first a function for prepending foo variable:



      % prepend_foo() foo=("$@" $foo) 
      % foo=(1 2)
      % print -l $foo
      1
      2
      % prepend_foo x 'a b c' y
      % print -l $foo
      x
      a b c
      y
      1
      2
      %


      Now consider a generalized function for appending to any variable:



      % prepend() var=$1; shift; eval "$var=($@ $(P)var)" 
      % foo=(1 2)
      % print -l $foo
      1
      2
      % prepend foo x 'a b c' y
      % print -l $foo
      x
      a
      b
      c
      y
      1
      2
      %


      As you can see, variables with spaces are split into several array items. I couldn't properly combine quote to achieve desired thing.




      On IRC, someone suggested using $name::=word, but that doesn't work well with arrays:



      21:23 < someone> > b=(bar baz); a=b; : $(P)a::=(foo $(P)a); typeset -p b
      21:23 < machabot> someone: typeset b='(foo bar baz)'
      21:23 < someone> dammit that's a string








      share|improve this question










      share|improve this question




      share|improve this question









      asked May 18 at 19:39









      woky

      22216




      22216




















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          3
          down vote













          To prepend elements to an array, you can just do:



          a[1,0]=(more elements)


          Or you can do:



          a=(more elements "$a[@]")


          Note that doing:



          a=(more elements $a)


          would remove the empty elements in $a.



          Now, to make a function for that, that's what eval is for, but you have to get the syntax right:



          prepend() 
          eval "$1[1,0]"='("$@[2,-1]")'



          See how the ("$@[2,-1]") is single-quoted so it is passed literally to eval.



          Or the longer way, but wouldn't work for prepend var more elements (when the variable name is var):



          prepend() 
          local var=$1; shift
          eval "$var"='("$@" "$'"$var"'[@]")'



          The code you want to eval evaluate upon prepend varname ... is:



           varname=("$@" "${varname[@]")


          You don't want "$@" to be expanded before being passed to eval. Only $var needs to be expanded there.



          Note that the P variable expansion flag is as dangerous as eval when used with unsanitized data.



          var='x[$(uname>&2)0]'
          echo "$(P)var"


          Will run that uname command.






          share|improve this answer






























            up vote
            0
            down vote













            Your prepend function can be written without eval, if you loop over the elements to unshift them one at a time.



            $ prepend () 
            then> local i=$#*
            then> while [[ $i > 1 ]]; do
            then> typeset -g "$1[1,0]=$*[$i]"
            then> i=$((i - 1))
            then> done
            then>
            $ foo=(a 'b c' d)
            $ prepend foo m 'n o p' q r
            $ print -l $foo
            m
            n o p
            q
            r
            a
            b c
            d
            $


            Note that -g is needed for typeset to accept setting a non-local array element, but doesn't mean that the array referenced by $1 can't be local to the calling function. It just means "it's OK to look up the function call stack to find this array".






            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',
              convertImagesToLinks: false,
              noModals: false,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              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%2f444690%2fzsh-indirect-array-variable-assignment-without-using-eval%23new-answer', 'question_page');

              );

              Post as a guest






























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes








              up vote
              3
              down vote













              To prepend elements to an array, you can just do:



              a[1,0]=(more elements)


              Or you can do:



              a=(more elements "$a[@]")


              Note that doing:



              a=(more elements $a)


              would remove the empty elements in $a.



              Now, to make a function for that, that's what eval is for, but you have to get the syntax right:



              prepend() 
              eval "$1[1,0]"='("$@[2,-1]")'



              See how the ("$@[2,-1]") is single-quoted so it is passed literally to eval.



              Or the longer way, but wouldn't work for prepend var more elements (when the variable name is var):



              prepend() 
              local var=$1; shift
              eval "$var"='("$@" "$'"$var"'[@]")'



              The code you want to eval evaluate upon prepend varname ... is:



               varname=("$@" "${varname[@]")


              You don't want "$@" to be expanded before being passed to eval. Only $var needs to be expanded there.



              Note that the P variable expansion flag is as dangerous as eval when used with unsanitized data.



              var='x[$(uname>&2)0]'
              echo "$(P)var"


              Will run that uname command.






              share|improve this answer



























                up vote
                3
                down vote













                To prepend elements to an array, you can just do:



                a[1,0]=(more elements)


                Or you can do:



                a=(more elements "$a[@]")


                Note that doing:



                a=(more elements $a)


                would remove the empty elements in $a.



                Now, to make a function for that, that's what eval is for, but you have to get the syntax right:



                prepend() 
                eval "$1[1,0]"='("$@[2,-1]")'



                See how the ("$@[2,-1]") is single-quoted so it is passed literally to eval.



                Or the longer way, but wouldn't work for prepend var more elements (when the variable name is var):



                prepend() 
                local var=$1; shift
                eval "$var"='("$@" "$'"$var"'[@]")'



                The code you want to eval evaluate upon prepend varname ... is:



                 varname=("$@" "${varname[@]")


                You don't want "$@" to be expanded before being passed to eval. Only $var needs to be expanded there.



                Note that the P variable expansion flag is as dangerous as eval when used with unsanitized data.



                var='x[$(uname>&2)0]'
                echo "$(P)var"


                Will run that uname command.






                share|improve this answer

























                  up vote
                  3
                  down vote










                  up vote
                  3
                  down vote









                  To prepend elements to an array, you can just do:



                  a[1,0]=(more elements)


                  Or you can do:



                  a=(more elements "$a[@]")


                  Note that doing:



                  a=(more elements $a)


                  would remove the empty elements in $a.



                  Now, to make a function for that, that's what eval is for, but you have to get the syntax right:



                  prepend() 
                  eval "$1[1,0]"='("$@[2,-1]")'



                  See how the ("$@[2,-1]") is single-quoted so it is passed literally to eval.



                  Or the longer way, but wouldn't work for prepend var more elements (when the variable name is var):



                  prepend() 
                  local var=$1; shift
                  eval "$var"='("$@" "$'"$var"'[@]")'



                  The code you want to eval evaluate upon prepend varname ... is:



                   varname=("$@" "${varname[@]")


                  You don't want "$@" to be expanded before being passed to eval. Only $var needs to be expanded there.



                  Note that the P variable expansion flag is as dangerous as eval when used with unsanitized data.



                  var='x[$(uname>&2)0]'
                  echo "$(P)var"


                  Will run that uname command.






                  share|improve this answer















                  To prepend elements to an array, you can just do:



                  a[1,0]=(more elements)


                  Or you can do:



                  a=(more elements "$a[@]")


                  Note that doing:



                  a=(more elements $a)


                  would remove the empty elements in $a.



                  Now, to make a function for that, that's what eval is for, but you have to get the syntax right:



                  prepend() 
                  eval "$1[1,0]"='("$@[2,-1]")'



                  See how the ("$@[2,-1]") is single-quoted so it is passed literally to eval.



                  Or the longer way, but wouldn't work for prepend var more elements (when the variable name is var):



                  prepend() 
                  local var=$1; shift
                  eval "$var"='("$@" "$'"$var"'[@]")'



                  The code you want to eval evaluate upon prepend varname ... is:



                   varname=("$@" "${varname[@]")


                  You don't want "$@" to be expanded before being passed to eval. Only $var needs to be expanded there.



                  Note that the P variable expansion flag is as dangerous as eval when used with unsanitized data.



                  var='x[$(uname>&2)0]'
                  echo "$(P)var"


                  Will run that uname command.







                  share|improve this answer















                  share|improve this answer



                  share|improve this answer








                  edited May 18 at 20:40


























                  answered May 18 at 20:32









                  Stéphane Chazelas

                  279k53513845




                  279k53513845






















                      up vote
                      0
                      down vote













                      Your prepend function can be written without eval, if you loop over the elements to unshift them one at a time.



                      $ prepend () 
                      then> local i=$#*
                      then> while [[ $i > 1 ]]; do
                      then> typeset -g "$1[1,0]=$*[$i]"
                      then> i=$((i - 1))
                      then> done
                      then>
                      $ foo=(a 'b c' d)
                      $ prepend foo m 'n o p' q r
                      $ print -l $foo
                      m
                      n o p
                      q
                      r
                      a
                      b c
                      d
                      $


                      Note that -g is needed for typeset to accept setting a non-local array element, but doesn't mean that the array referenced by $1 can't be local to the calling function. It just means "it's OK to look up the function call stack to find this array".






                      share|improve this answer

























                        up vote
                        0
                        down vote













                        Your prepend function can be written without eval, if you loop over the elements to unshift them one at a time.



                        $ prepend () 
                        then> local i=$#*
                        then> while [[ $i > 1 ]]; do
                        then> typeset -g "$1[1,0]=$*[$i]"
                        then> i=$((i - 1))
                        then> done
                        then>
                        $ foo=(a 'b c' d)
                        $ prepend foo m 'n o p' q r
                        $ print -l $foo
                        m
                        n o p
                        q
                        r
                        a
                        b c
                        d
                        $


                        Note that -g is needed for typeset to accept setting a non-local array element, but doesn't mean that the array referenced by $1 can't be local to the calling function. It just means "it's OK to look up the function call stack to find this array".






                        share|improve this answer























                          up vote
                          0
                          down vote










                          up vote
                          0
                          down vote









                          Your prepend function can be written without eval, if you loop over the elements to unshift them one at a time.



                          $ prepend () 
                          then> local i=$#*
                          then> while [[ $i > 1 ]]; do
                          then> typeset -g "$1[1,0]=$*[$i]"
                          then> i=$((i - 1))
                          then> done
                          then>
                          $ foo=(a 'b c' d)
                          $ prepend foo m 'n o p' q r
                          $ print -l $foo
                          m
                          n o p
                          q
                          r
                          a
                          b c
                          d
                          $


                          Note that -g is needed for typeset to accept setting a non-local array element, but doesn't mean that the array referenced by $1 can't be local to the calling function. It just means "it's OK to look up the function call stack to find this array".






                          share|improve this answer













                          Your prepend function can be written without eval, if you loop over the elements to unshift them one at a time.



                          $ prepend () 
                          then> local i=$#*
                          then> while [[ $i > 1 ]]; do
                          then> typeset -g "$1[1,0]=$*[$i]"
                          then> i=$((i - 1))
                          then> done
                          then>
                          $ foo=(a 'b c' d)
                          $ prepend foo m 'n o p' q r
                          $ print -l $foo
                          m
                          n o p
                          q
                          r
                          a
                          b c
                          d
                          $


                          Note that -g is needed for typeset to accept setting a non-local array element, but doesn't mean that the array referenced by $1 can't be local to the calling function. It just means "it's OK to look up the function call stack to find this array".







                          share|improve this answer













                          share|improve this answer



                          share|improve this answer











                          answered Jun 29 at 0:02









                          Tim Smith

                          1414




                          1414






















                               

                              draft saved


                              draft discarded


























                               


                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function ()
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f444690%2fzsh-indirect-array-variable-assignment-without-using-eval%23new-answer', 'question_page');

                              );

                              Post as a guest













































































                              Popular posts from this blog

                              Peggy Mitchell

                              Palaiologos

                              The Forum (Inglewood, California)