zsh script that takes a variable number of arguments

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











up vote
3
down vote

favorite












I have just (belatedly) discovered that I can ssh -t from one machine to another and run a command there.



The thought then took me that I should be able to come up with a script that tailed a log file (from a pair) on a machine by doing something like



ssh -t $1 ssh -t $2 tailf /pathtofile/$3/log.log


and indeed if I put in 1,2 and 3 by hand it works just fine.



My problem is that I may have 1,2 or 3 hosts to jump between to make this work.



How would I do this in zsh? I've done some digging and it looks as though shift may be my friend, but I'm a bit lost.










share|improve this question

























    up vote
    3
    down vote

    favorite












    I have just (belatedly) discovered that I can ssh -t from one machine to another and run a command there.



    The thought then took me that I should be able to come up with a script that tailed a log file (from a pair) on a machine by doing something like



    ssh -t $1 ssh -t $2 tailf /pathtofile/$3/log.log


    and indeed if I put in 1,2 and 3 by hand it works just fine.



    My problem is that I may have 1,2 or 3 hosts to jump between to make this work.



    How would I do this in zsh? I've done some digging and it looks as though shift may be my friend, but I'm a bit lost.










    share|improve this question























      up vote
      3
      down vote

      favorite









      up vote
      3
      down vote

      favorite











      I have just (belatedly) discovered that I can ssh -t from one machine to another and run a command there.



      The thought then took me that I should be able to come up with a script that tailed a log file (from a pair) on a machine by doing something like



      ssh -t $1 ssh -t $2 tailf /pathtofile/$3/log.log


      and indeed if I put in 1,2 and 3 by hand it works just fine.



      My problem is that I may have 1,2 or 3 hosts to jump between to make this work.



      How would I do this in zsh? I've done some digging and it looks as though shift may be my friend, but I'm a bit lost.










      share|improve this question













      I have just (belatedly) discovered that I can ssh -t from one machine to another and run a command there.



      The thought then took me that I should be able to come up with a script that tailed a log file (from a pair) on a machine by doing something like



      ssh -t $1 ssh -t $2 tailf /pathtofile/$3/log.log


      and indeed if I put in 1,2 and 3 by hand it works just fine.



      My problem is that I may have 1,2 or 3 hosts to jump between to make this work.



      How would I do this in zsh? I've done some digging and it looks as though shift may be my friend, but I'm a bit lost.







      shell-script ssh zsh






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Sep 5 at 12:46









      Peter NUnn

      1184




      1184




















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          2
          down vote



          accepted










          Since I'm not really a zsh user, I will write a sh solution (zsh should be able to execute it too).



          The general gist is to create a list with one ssh -t hostname bit for each command line option except for the last one, and then to execute that list as a command.



          #!/bin/sh

          argn=$#
          i=0

          for arg do
          shift
          i=$(( i + 1 ))

          if [ "$i" -lt "$argn" ]; then
          set -- "$@" ssh -t "$arg"
          else
          set -- "$@" "/pathtofile/$arg/log.log"
          fi
          done

          command "$@"


          Running this as



          sh script.sh alpha beta gamma zeta


          it would create the list



          ssh -t alpha ssh -t beta ssh -t gamma /pathtofile/zeta/log.log


          The last command in the script would execute this as a command.



          The list is built in $@, the list of positional parameters, which at the start of the script contains the command line parameters of the script. The loop keeps track of when the last element of that original list is encountered and treats it specially.



          The set command, when used as above, will append to the list, and shift will remove the first element of the list (the one we are currently processing).



          I'm sure this could be made shorter and more elegant with zsh-specific syntax.






          share|improve this answer






















          • I learned this just yesterday! zsh -c 'set -- alpha beta gamma zeta; print -- "-t "$^@'
            – Jeff Schaller
            Sep 5 at 13:19






          • 1




            @JeffSchaller, except that it would become like "-t $1" "-t $2".... Using o=-t; print -- "$(@)o:^^argv" would make it "-t" "$1" "-t" "$2", but here you want "ssh" "-t" "$1" "ssh" "-t" "$2", and actually even rather ssh -t "$1" "ssh -t quoted-$2 "...
            – Stéphane Chazelas
            Sep 5 at 21:11











          • Where do you find a reference to the arcane incantations such as "$0" etc for these scripts?
            – Peter NUnn
            Sep 6 at 22:23










          • @PeterNUnn I don't think I used $0 in this script. Are you asking where to find more information about the sh shell? In that case, I tend to read the POSIX standard (specifically the "Shell & Utilities" section).
            – Kusalananda
            Sep 7 at 5:45

















          up vote
          2
          down vote













          I don't think you can easily get away without a loop:



          cmd=()
          for i ($argv[1,-2]) cmd+=(ssh -t $i)
          $cmd tailf /pathtofile/$argv[-1]/log.log


          Stritly speaking, since it's a shell command line that you pass to ssh, if you wanted to be able to support arbitrary values for those $1, $2, you'd need quote those arguments and add an extra level of quoting for each ssh.



          Here assuming that the login shell of the user on all the hosts is Bourne-like:



          cmd=(tailf /pathtofile/$argv[-1]/log.log)
          argv[-1]=()
          while (($#argv))
          cmd=(ssh -t "$argv[-1]" $(j: :)$(qq)cmd)
          argv[-1]=()



          That should make it work when called as



          that-script host1 host2 'dir with spaces and other nasty characters'





          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%2f467011%2fzsh-script-that-takes-a-variable-number-of-arguments%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
            2
            down vote



            accepted










            Since I'm not really a zsh user, I will write a sh solution (zsh should be able to execute it too).



            The general gist is to create a list with one ssh -t hostname bit for each command line option except for the last one, and then to execute that list as a command.



            #!/bin/sh

            argn=$#
            i=0

            for arg do
            shift
            i=$(( i + 1 ))

            if [ "$i" -lt "$argn" ]; then
            set -- "$@" ssh -t "$arg"
            else
            set -- "$@" "/pathtofile/$arg/log.log"
            fi
            done

            command "$@"


            Running this as



            sh script.sh alpha beta gamma zeta


            it would create the list



            ssh -t alpha ssh -t beta ssh -t gamma /pathtofile/zeta/log.log


            The last command in the script would execute this as a command.



            The list is built in $@, the list of positional parameters, which at the start of the script contains the command line parameters of the script. The loop keeps track of when the last element of that original list is encountered and treats it specially.



            The set command, when used as above, will append to the list, and shift will remove the first element of the list (the one we are currently processing).



            I'm sure this could be made shorter and more elegant with zsh-specific syntax.






            share|improve this answer






















            • I learned this just yesterday! zsh -c 'set -- alpha beta gamma zeta; print -- "-t "$^@'
              – Jeff Schaller
              Sep 5 at 13:19






            • 1




              @JeffSchaller, except that it would become like "-t $1" "-t $2".... Using o=-t; print -- "$(@)o:^^argv" would make it "-t" "$1" "-t" "$2", but here you want "ssh" "-t" "$1" "ssh" "-t" "$2", and actually even rather ssh -t "$1" "ssh -t quoted-$2 "...
              – Stéphane Chazelas
              Sep 5 at 21:11











            • Where do you find a reference to the arcane incantations such as "$0" etc for these scripts?
              – Peter NUnn
              Sep 6 at 22:23










            • @PeterNUnn I don't think I used $0 in this script. Are you asking where to find more information about the sh shell? In that case, I tend to read the POSIX standard (specifically the "Shell & Utilities" section).
              – Kusalananda
              Sep 7 at 5:45














            up vote
            2
            down vote



            accepted










            Since I'm not really a zsh user, I will write a sh solution (zsh should be able to execute it too).



            The general gist is to create a list with one ssh -t hostname bit for each command line option except for the last one, and then to execute that list as a command.



            #!/bin/sh

            argn=$#
            i=0

            for arg do
            shift
            i=$(( i + 1 ))

            if [ "$i" -lt "$argn" ]; then
            set -- "$@" ssh -t "$arg"
            else
            set -- "$@" "/pathtofile/$arg/log.log"
            fi
            done

            command "$@"


            Running this as



            sh script.sh alpha beta gamma zeta


            it would create the list



            ssh -t alpha ssh -t beta ssh -t gamma /pathtofile/zeta/log.log


            The last command in the script would execute this as a command.



            The list is built in $@, the list of positional parameters, which at the start of the script contains the command line parameters of the script. The loop keeps track of when the last element of that original list is encountered and treats it specially.



            The set command, when used as above, will append to the list, and shift will remove the first element of the list (the one we are currently processing).



            I'm sure this could be made shorter and more elegant with zsh-specific syntax.






            share|improve this answer






















            • I learned this just yesterday! zsh -c 'set -- alpha beta gamma zeta; print -- "-t "$^@'
              – Jeff Schaller
              Sep 5 at 13:19






            • 1




              @JeffSchaller, except that it would become like "-t $1" "-t $2".... Using o=-t; print -- "$(@)o:^^argv" would make it "-t" "$1" "-t" "$2", but here you want "ssh" "-t" "$1" "ssh" "-t" "$2", and actually even rather ssh -t "$1" "ssh -t quoted-$2 "...
              – Stéphane Chazelas
              Sep 5 at 21:11











            • Where do you find a reference to the arcane incantations such as "$0" etc for these scripts?
              – Peter NUnn
              Sep 6 at 22:23










            • @PeterNUnn I don't think I used $0 in this script. Are you asking where to find more information about the sh shell? In that case, I tend to read the POSIX standard (specifically the "Shell & Utilities" section).
              – Kusalananda
              Sep 7 at 5:45












            up vote
            2
            down vote



            accepted







            up vote
            2
            down vote



            accepted






            Since I'm not really a zsh user, I will write a sh solution (zsh should be able to execute it too).



            The general gist is to create a list with one ssh -t hostname bit for each command line option except for the last one, and then to execute that list as a command.



            #!/bin/sh

            argn=$#
            i=0

            for arg do
            shift
            i=$(( i + 1 ))

            if [ "$i" -lt "$argn" ]; then
            set -- "$@" ssh -t "$arg"
            else
            set -- "$@" "/pathtofile/$arg/log.log"
            fi
            done

            command "$@"


            Running this as



            sh script.sh alpha beta gamma zeta


            it would create the list



            ssh -t alpha ssh -t beta ssh -t gamma /pathtofile/zeta/log.log


            The last command in the script would execute this as a command.



            The list is built in $@, the list of positional parameters, which at the start of the script contains the command line parameters of the script. The loop keeps track of when the last element of that original list is encountered and treats it specially.



            The set command, when used as above, will append to the list, and shift will remove the first element of the list (the one we are currently processing).



            I'm sure this could be made shorter and more elegant with zsh-specific syntax.






            share|improve this answer














            Since I'm not really a zsh user, I will write a sh solution (zsh should be able to execute it too).



            The general gist is to create a list with one ssh -t hostname bit for each command line option except for the last one, and then to execute that list as a command.



            #!/bin/sh

            argn=$#
            i=0

            for arg do
            shift
            i=$(( i + 1 ))

            if [ "$i" -lt "$argn" ]; then
            set -- "$@" ssh -t "$arg"
            else
            set -- "$@" "/pathtofile/$arg/log.log"
            fi
            done

            command "$@"


            Running this as



            sh script.sh alpha beta gamma zeta


            it would create the list



            ssh -t alpha ssh -t beta ssh -t gamma /pathtofile/zeta/log.log


            The last command in the script would execute this as a command.



            The list is built in $@, the list of positional parameters, which at the start of the script contains the command line parameters of the script. The loop keeps track of when the last element of that original list is encountered and treats it specially.



            The set command, when used as above, will append to the list, and shift will remove the first element of the list (the one we are currently processing).



            I'm sure this could be made shorter and more elegant with zsh-specific syntax.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Sep 5 at 13:23

























            answered Sep 5 at 13:09









            Kusalananda

            107k14209331




            107k14209331











            • I learned this just yesterday! zsh -c 'set -- alpha beta gamma zeta; print -- "-t "$^@'
              – Jeff Schaller
              Sep 5 at 13:19






            • 1




              @JeffSchaller, except that it would become like "-t $1" "-t $2".... Using o=-t; print -- "$(@)o:^^argv" would make it "-t" "$1" "-t" "$2", but here you want "ssh" "-t" "$1" "ssh" "-t" "$2", and actually even rather ssh -t "$1" "ssh -t quoted-$2 "...
              – Stéphane Chazelas
              Sep 5 at 21:11











            • Where do you find a reference to the arcane incantations such as "$0" etc for these scripts?
              – Peter NUnn
              Sep 6 at 22:23










            • @PeterNUnn I don't think I used $0 in this script. Are you asking where to find more information about the sh shell? In that case, I tend to read the POSIX standard (specifically the "Shell & Utilities" section).
              – Kusalananda
              Sep 7 at 5:45
















            • I learned this just yesterday! zsh -c 'set -- alpha beta gamma zeta; print -- "-t "$^@'
              – Jeff Schaller
              Sep 5 at 13:19






            • 1




              @JeffSchaller, except that it would become like "-t $1" "-t $2".... Using o=-t; print -- "$(@)o:^^argv" would make it "-t" "$1" "-t" "$2", but here you want "ssh" "-t" "$1" "ssh" "-t" "$2", and actually even rather ssh -t "$1" "ssh -t quoted-$2 "...
              – Stéphane Chazelas
              Sep 5 at 21:11











            • Where do you find a reference to the arcane incantations such as "$0" etc for these scripts?
              – Peter NUnn
              Sep 6 at 22:23










            • @PeterNUnn I don't think I used $0 in this script. Are you asking where to find more information about the sh shell? In that case, I tend to read the POSIX standard (specifically the "Shell & Utilities" section).
              – Kusalananda
              Sep 7 at 5:45















            I learned this just yesterday! zsh -c 'set -- alpha beta gamma zeta; print -- "-t "$^@'
            – Jeff Schaller
            Sep 5 at 13:19




            I learned this just yesterday! zsh -c 'set -- alpha beta gamma zeta; print -- "-t "$^@'
            – Jeff Schaller
            Sep 5 at 13:19




            1




            1




            @JeffSchaller, except that it would become like "-t $1" "-t $2".... Using o=-t; print -- "$(@)o:^^argv" would make it "-t" "$1" "-t" "$2", but here you want "ssh" "-t" "$1" "ssh" "-t" "$2", and actually even rather ssh -t "$1" "ssh -t quoted-$2 "...
            – Stéphane Chazelas
            Sep 5 at 21:11





            @JeffSchaller, except that it would become like "-t $1" "-t $2".... Using o=-t; print -- "$(@)o:^^argv" would make it "-t" "$1" "-t" "$2", but here you want "ssh" "-t" "$1" "ssh" "-t" "$2", and actually even rather ssh -t "$1" "ssh -t quoted-$2 "...
            – Stéphane Chazelas
            Sep 5 at 21:11













            Where do you find a reference to the arcane incantations such as "$0" etc for these scripts?
            – Peter NUnn
            Sep 6 at 22:23




            Where do you find a reference to the arcane incantations such as "$0" etc for these scripts?
            – Peter NUnn
            Sep 6 at 22:23












            @PeterNUnn I don't think I used $0 in this script. Are you asking where to find more information about the sh shell? In that case, I tend to read the POSIX standard (specifically the "Shell & Utilities" section).
            – Kusalananda
            Sep 7 at 5:45




            @PeterNUnn I don't think I used $0 in this script. Are you asking where to find more information about the sh shell? In that case, I tend to read the POSIX standard (specifically the "Shell & Utilities" section).
            – Kusalananda
            Sep 7 at 5:45












            up vote
            2
            down vote













            I don't think you can easily get away without a loop:



            cmd=()
            for i ($argv[1,-2]) cmd+=(ssh -t $i)
            $cmd tailf /pathtofile/$argv[-1]/log.log


            Stritly speaking, since it's a shell command line that you pass to ssh, if you wanted to be able to support arbitrary values for those $1, $2, you'd need quote those arguments and add an extra level of quoting for each ssh.



            Here assuming that the login shell of the user on all the hosts is Bourne-like:



            cmd=(tailf /pathtofile/$argv[-1]/log.log)
            argv[-1]=()
            while (($#argv))
            cmd=(ssh -t "$argv[-1]" $(j: :)$(qq)cmd)
            argv[-1]=()



            That should make it work when called as



            that-script host1 host2 'dir with spaces and other nasty characters'





            share|improve this answer
























              up vote
              2
              down vote













              I don't think you can easily get away without a loop:



              cmd=()
              for i ($argv[1,-2]) cmd+=(ssh -t $i)
              $cmd tailf /pathtofile/$argv[-1]/log.log


              Stritly speaking, since it's a shell command line that you pass to ssh, if you wanted to be able to support arbitrary values for those $1, $2, you'd need quote those arguments and add an extra level of quoting for each ssh.



              Here assuming that the login shell of the user on all the hosts is Bourne-like:



              cmd=(tailf /pathtofile/$argv[-1]/log.log)
              argv[-1]=()
              while (($#argv))
              cmd=(ssh -t "$argv[-1]" $(j: :)$(qq)cmd)
              argv[-1]=()



              That should make it work when called as



              that-script host1 host2 'dir with spaces and other nasty characters'





              share|improve this answer






















                up vote
                2
                down vote










                up vote
                2
                down vote









                I don't think you can easily get away without a loop:



                cmd=()
                for i ($argv[1,-2]) cmd+=(ssh -t $i)
                $cmd tailf /pathtofile/$argv[-1]/log.log


                Stritly speaking, since it's a shell command line that you pass to ssh, if you wanted to be able to support arbitrary values for those $1, $2, you'd need quote those arguments and add an extra level of quoting for each ssh.



                Here assuming that the login shell of the user on all the hosts is Bourne-like:



                cmd=(tailf /pathtofile/$argv[-1]/log.log)
                argv[-1]=()
                while (($#argv))
                cmd=(ssh -t "$argv[-1]" $(j: :)$(qq)cmd)
                argv[-1]=()



                That should make it work when called as



                that-script host1 host2 'dir with spaces and other nasty characters'





                share|improve this answer












                I don't think you can easily get away without a loop:



                cmd=()
                for i ($argv[1,-2]) cmd+=(ssh -t $i)
                $cmd tailf /pathtofile/$argv[-1]/log.log


                Stritly speaking, since it's a shell command line that you pass to ssh, if you wanted to be able to support arbitrary values for those $1, $2, you'd need quote those arguments and add an extra level of quoting for each ssh.



                Here assuming that the login shell of the user on all the hosts is Bourne-like:



                cmd=(tailf /pathtofile/$argv[-1]/log.log)
                argv[-1]=()
                while (($#argv))
                cmd=(ssh -t "$argv[-1]" $(j: :)$(qq)cmd)
                argv[-1]=()



                That should make it work when called as



                that-script host1 host2 'dir with spaces and other nasty characters'






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Sep 5 at 21:04









                Stéphane Chazelas

                286k53528866




                286k53528866



























                     

                    draft saved


                    draft discarded















































                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f467011%2fzsh-script-that-takes-a-variable-number-of-arguments%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    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