Multiple arguments in shebang












21















I am wondering whether there is a general way of passing multiple options to an executable via the shebang line (#!).



I use NixOS, and the first part of the shebang in any script I write is usually /usr/bin/env. The problem I encounter then is that everything that comes after is interpreted as a single file or directory by the system.



Suppose, for example, that I want to write a script to be executed by bash in posix mode. The naive way of writing the shebang would be:



#!/usr/bin/env bash --posix


but trying to execute the resulting script produces the following error:



/usr/bin/env: ‘bash --posix’: No such file or directory


I am aware of this post, but I was wondering whether there was a more general and cleaner solution.





EDIT: I know that for Guile scripts, there is a way to achieve what I want, documented in Section 4.3.4 of the manual:



 #!/usr/bin/env sh
exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
!#


The trick, here, is that the second line (starting with exec) is interpreted as code by sh but, being in the #! ... !# block, as a comment, and thus ignored, by the Guile interpreter.



Would it not be possible to generalize this method to any interpreter?





Second EDIT: After playing around a little bit, it seems that, for interpreters that can read their input from stdin, the following method would work:



#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;


It's probably not optimal, though, as the sh process lives until the interpreter has finished its job. Any feedback or suggestion would be appreciated.










share|improve this question




















  • 1





    Related: unix.stackexchange.com/questions/63979/…

    – Kusalananda
    Oct 22 '17 at 11:50






  • 1





    Also relevant: How to pass arguments to the shebang interpreter when executing a script?.

    – Stephen Kitt
    Oct 22 '17 at 11:54
















21















I am wondering whether there is a general way of passing multiple options to an executable via the shebang line (#!).



I use NixOS, and the first part of the shebang in any script I write is usually /usr/bin/env. The problem I encounter then is that everything that comes after is interpreted as a single file or directory by the system.



Suppose, for example, that I want to write a script to be executed by bash in posix mode. The naive way of writing the shebang would be:



#!/usr/bin/env bash --posix


but trying to execute the resulting script produces the following error:



/usr/bin/env: ‘bash --posix’: No such file or directory


I am aware of this post, but I was wondering whether there was a more general and cleaner solution.





EDIT: I know that for Guile scripts, there is a way to achieve what I want, documented in Section 4.3.4 of the manual:



 #!/usr/bin/env sh
exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
!#


The trick, here, is that the second line (starting with exec) is interpreted as code by sh but, being in the #! ... !# block, as a comment, and thus ignored, by the Guile interpreter.



Would it not be possible to generalize this method to any interpreter?





Second EDIT: After playing around a little bit, it seems that, for interpreters that can read their input from stdin, the following method would work:



#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;


It's probably not optimal, though, as the sh process lives until the interpreter has finished its job. Any feedback or suggestion would be appreciated.










share|improve this question




















  • 1





    Related: unix.stackexchange.com/questions/63979/…

    – Kusalananda
    Oct 22 '17 at 11:50






  • 1





    Also relevant: How to pass arguments to the shebang interpreter when executing a script?.

    – Stephen Kitt
    Oct 22 '17 at 11:54














21












21








21


4






I am wondering whether there is a general way of passing multiple options to an executable via the shebang line (#!).



I use NixOS, and the first part of the shebang in any script I write is usually /usr/bin/env. The problem I encounter then is that everything that comes after is interpreted as a single file or directory by the system.



Suppose, for example, that I want to write a script to be executed by bash in posix mode. The naive way of writing the shebang would be:



#!/usr/bin/env bash --posix


but trying to execute the resulting script produces the following error:



/usr/bin/env: ‘bash --posix’: No such file or directory


I am aware of this post, but I was wondering whether there was a more general and cleaner solution.





EDIT: I know that for Guile scripts, there is a way to achieve what I want, documented in Section 4.3.4 of the manual:



 #!/usr/bin/env sh
exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
!#


The trick, here, is that the second line (starting with exec) is interpreted as code by sh but, being in the #! ... !# block, as a comment, and thus ignored, by the Guile interpreter.



Would it not be possible to generalize this method to any interpreter?





Second EDIT: After playing around a little bit, it seems that, for interpreters that can read their input from stdin, the following method would work:



#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;


It's probably not optimal, though, as the sh process lives until the interpreter has finished its job. Any feedback or suggestion would be appreciated.










share|improve this question
















I am wondering whether there is a general way of passing multiple options to an executable via the shebang line (#!).



I use NixOS, and the first part of the shebang in any script I write is usually /usr/bin/env. The problem I encounter then is that everything that comes after is interpreted as a single file or directory by the system.



Suppose, for example, that I want to write a script to be executed by bash in posix mode. The naive way of writing the shebang would be:



#!/usr/bin/env bash --posix


but trying to execute the resulting script produces the following error:



/usr/bin/env: ‘bash --posix’: No such file or directory


I am aware of this post, but I was wondering whether there was a more general and cleaner solution.





EDIT: I know that for Guile scripts, there is a way to achieve what I want, documented in Section 4.3.4 of the manual:



 #!/usr/bin/env sh
exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
!#


The trick, here, is that the second line (starting with exec) is interpreted as code by sh but, being in the #! ... !# block, as a comment, and thus ignored, by the Guile interpreter.



Would it not be possible to generalize this method to any interpreter?





Second EDIT: After playing around a little bit, it seems that, for interpreters that can read their input from stdin, the following method would work:



#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;


It's probably not optimal, though, as the sh process lives until the interpreter has finished its job. Any feedback or suggestion would be appreciated.







scripting environment-variables posix arguments shebang






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jun 21 '18 at 11:13







Rastapopoulos

















asked Oct 22 '17 at 11:23









RastapopoulosRastapopoulos

578313




578313








  • 1





    Related: unix.stackexchange.com/questions/63979/…

    – Kusalananda
    Oct 22 '17 at 11:50






  • 1





    Also relevant: How to pass arguments to the shebang interpreter when executing a script?.

    – Stephen Kitt
    Oct 22 '17 at 11:54














  • 1





    Related: unix.stackexchange.com/questions/63979/…

    – Kusalananda
    Oct 22 '17 at 11:50






  • 1





    Also relevant: How to pass arguments to the shebang interpreter when executing a script?.

    – Stephen Kitt
    Oct 22 '17 at 11:54








1




1





Related: unix.stackexchange.com/questions/63979/…

– Kusalananda
Oct 22 '17 at 11:50





Related: unix.stackexchange.com/questions/63979/…

– Kusalananda
Oct 22 '17 at 11:50




1




1





Also relevant: How to pass arguments to the shebang interpreter when executing a script?.

– Stephen Kitt
Oct 22 '17 at 11:54





Also relevant: How to pass arguments to the shebang interpreter when executing a script?.

– Stephen Kitt
Oct 22 '17 at 11:54










5 Answers
5






active

oldest

votes


















20














There is no general solution, at least not if you need to support Linux, because the Linux kernel treats everything following the first “word” in the shebang line as a single argument.



I’m not sure what NixOS’s constraints are, but typically I would just write your shebang as



#!/bin/bash --posix


or, where possible, set options in the script:



set -o posix


Alternatively, you can have the script restart itself with the appropriate shell invocation:



#!/bin/sh -

if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

shift

# Processing continues


This approach can be generalised to other languages, as long as you find a way for the first couple of lines (which are interpreted by the shell) to be ignored by the target language.






share|improve this answer

































    7














    The shebang is described in execve(2) man page as follow:



    #! interpreter [optional-arg]


    Two spaces are accepted in this syntax:




    1. One space before the interpreter path, but this space is optional.

    2. One space separating the the interpreter path and its optional argument.


    Note that I didn't used the plural when talking of an optional argument, neither does the syntax above uses [optional-arg ...], as you can provide at most one single argument.



    As far as shell scripting is concerned, you can use the set built-in command near the beginning of your script which will allow to set interpreters parameters, providing the same result as if you used command-line arguments.



    In your case:



    set -o posix


    From a Bash prompt, check the output of help set to get all available options.






    share|improve this answer



















    • 1





      You’re allowed to have more than two spaces, they’re just considered to be part of the optional argument.

      – Stephen Kitt
      Oct 22 '17 at 11:56











    • @StephenKitt: Indeed, white space here is to be taken more as a category than the actual space char. I suppose that other white spaces such as tabs should also be widely accepted.

      – WhiteWinterWolf
      Oct 22 '17 at 12:14





















    7














    The POSIX standard is very terse on describing #!:



    From the rationale section of the documentation of the exec() family of system interfaces:




    Another way that some historical implementations handle shell scripts is by recognizing the first two bytes of the file as the character string #! and using the remainder of the first line of the file as the name of the command interpreter to execute.




    From the Shell Introduction section:




    The shell reads its input from a file (see sh), from the -c option or from the system() and popen() functions defined in the System Interfaces volume of POSIX.1-2008. If the first line of a file of shell commands starts with the characters #!, the results are unspecified.




    This basically means that any implementation (the Unix you are using) is free to do the specifics of the parsing of the shebang line as it wants.



    Some Unices, like macOS (can't test ATM), will split the arguments given to the interpreter on the shebang line into separate arguments, while Linux and most other Unices will give the arguments as a single option to the interpreter.



    It is thus unwise to rely on the shebang line being able to take more than a single argument.



    See also the Portability section of the Shebang article on Wikipedia.





    One easy solution, which is generalizable to any utility or language, is to make a wrapper script that executes the real script with the appropriate command line arguments:



    #!/bin/sh
    exec /bin/bash --posix /some/path/realscript "$@"


    I don't think I would personally try to make it re-execute itself as that feels somewhat fragile.






    share|improve this answer

































      4














      Although not exactly portable, starting with coreutils 8.30 and according to its documentation you will be able to use:



      #!/usr/bin/env -S command arg1 arg2 ...


      So given:



      $ cat test.sh
      #!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too


      you will get:



      % ./test.sh 
      $0 is '/usr/local/bin/showargs'
      $1 is 'here'
      $2 is 'is another'
      $3 is 'long'
      $4 is 'arg'
      $5 is '-e'
      $6 is 'this and that '
      $7 is 'too'
      $8 is './test.sh'


      and in case you are curious showargs is:



      #!/usr/bin/env sh
      echo "$0 is '$0'"

      i=1
      for arg in "$@"; do
      echo "$$i is '$arg'"
      i=$((i+1))
      done





      share|improve this answer
























      • This is very good to know for future reference.

        – John McGehee
        Feb 4 at 21:39











      • That option was copied from FreeBSD's env where -S was added in 2005. See lists.gnu.org/r/coreutils/2018-04/msg00011.html

        – Stéphane Chazelas
        Feb 6 at 7:19



















      3














      On Linux, the shebang isn't very flexible; according to multiple answers (Stephen Kitt's answer and Jörg W Mittag's), there is no designated way to pass multiple arguments in a shebang line.



      I'm not sure if it will be of use to anyone, but I've written a short script to implement the lacking feature. See https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa.



      It is also possible to write embedded workarounds. Bellow, I present four language-agnostic workarounds applied to the same test script and the result each prints. I suppose that the script is executable and is in /tmp/shebang.





      Wrapping your script in a bash heredoc inside process substitution



      As far as I know, this is the most reliable language-agnostic way of doing it. It allows passing arguments and preserves stdin. The drawback is that the interpreter doesn't know the (real) location of the file it reads.



      #!/bin/bash
      exec python3 -O <(cat << 'EOWRAPPER'
      print("PYTHON_SCRIPT_BEGINNING")

      from sys import argv
      try:
      print("input() 0 ::", input())
      print("input() 1 ::", input())
      except EOFError:
      print("input() caused EOFError")
      print("argv[0] ::", argv[0])
      print("argv[1:] ::", argv[1:])
      print("__debug__ ::", __debug__)
      # The -O option changes __debug__ to False

      print("PYTHON_SCRIPT_END")
      EOWRAPPER
      ) "$@"


      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



      PYTHON_SCRIPT_BEGINNING
      input() 0 :: aa
      input() 1 :: bb
      argv[0] :: /dev/fd/62
      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
      __debug__ :: False
      PYTHON_SCRIPT_END


      Note that process substitution produces a special file. This may not suit all executables. For instance, #!/usr/bin/less complains: /dev/fd/63 is not a regular file (use -f to see it)



      I don't know if it is possible to have heredoc inside process substitution in dash.





      Wrapping your script in a simple heredoc



      Shorter and simpler, but you won't be able to access stdin from your script and it requires the interpreter to be able to read and execute a script from stdin.



      #!/bin/sh
      exec python3 - "$@" << 'EOWRAPPER'
      print("PYTHON_SCRIPT_BEGINNING")

      from sys import argv

      try:
      print("input() 0 ::", input())
      print("input() 1 ::", input())
      except EOFError:
      print("input() caused EOFError")
      print("argv[0] ::", argv[0])
      print("argv[1:] ::", argv[1:])
      print("__debug__ ::", __debug__)
      # The -O option changes __debug__ to False

      print("PYTHON_SCRIPT_END")
      EOWRAPPER


      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



      PYTHON_SCRIPT_BEGINNING
      input() caused EOFError
      argv[0] :: -
      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
      __debug__ :: True
      PYTHON_SCRIPT_END




      Use awk system() call but without arguments



      Correctly passes the name of the executed file, but your script won't receive the arguments you give it.
      Note that awk is the only language I know whose interpreter both is installed on linux by default and reads its instructions from the command line by default.



      #!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
      print("PYTHON_SCRIPT_BEGINNING")

      from sys import argv

      print("input() 0 ::", input())
      print("input() 1 ::", input())
      print("argv[0] ::", argv[0])
      print("argv[1:] ::", argv[1:])
      print("__debug__ ::", __debug__)
      # The -O option changes __debug__ to False

      print("PYTHON_SCRIPT_END")


      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



      PYTHON_SCRIPT_BEGINNING
      input() 0 :: aa
      input() 1 :: bb
      argv[0] :: /tmp/shebang
      argv[1:] ::
      __debug__ :: False
      PYTHON_SCRIPT_END




      Use awk 4.1+ system() call, provided your arguments do not contain spaces



      Nice, but only if you are sure your script won't be called with arguments containing spaces. As you can see, your arguments containing spaces would be split, unless the spaces are escaped.



      #!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
      print("PYTHON_SCRIPT_BEGINNING")

      from sys import argv

      print("input() 0 ::", input())
      print("input() 1 ::", input())
      print("argv[0] ::", argv[0])
      print("argv[1:] ::", argv[1:])
      print("__debug__ ::", __debug__)
      # The -O option changes __debug__ to False

      print("PYTHON_SCRIPT_END")


      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



      PYTHON_SCRIPT_BEGINNING
      input() 0 :: aa
      input() 1 :: bb
      argv[0] :: /tmp/shebang
      argv[1:] :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \escapes\']
      __debug__ :: False
      PYTHON_SCRIPT_END


      For awk versions below 4.1, you will have to use string concatenation inside a for loop, see example function https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .






      share|improve this answer





















      • 1





        Quote the here document terminator to inhibit $variable or `command` substitution: exec python3 -O <(cat <<'EOWRAPPER'

        – John McGehee
        Feb 4 at 22:28








      • 1





        @JohnMcGehee Thanks. Done.

        – loxaxs
        Feb 6 at 6:37











      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%2f399690%2fmultiple-arguments-in-shebang%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      5 Answers
      5






      active

      oldest

      votes








      5 Answers
      5






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      20














      There is no general solution, at least not if you need to support Linux, because the Linux kernel treats everything following the first “word” in the shebang line as a single argument.



      I’m not sure what NixOS’s constraints are, but typically I would just write your shebang as



      #!/bin/bash --posix


      or, where possible, set options in the script:



      set -o posix


      Alternatively, you can have the script restart itself with the appropriate shell invocation:



      #!/bin/sh -

      if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

      shift

      # Processing continues


      This approach can be generalised to other languages, as long as you find a way for the first couple of lines (which are interpreted by the shell) to be ignored by the target language.






      share|improve this answer






























        20














        There is no general solution, at least not if you need to support Linux, because the Linux kernel treats everything following the first “word” in the shebang line as a single argument.



        I’m not sure what NixOS’s constraints are, but typically I would just write your shebang as



        #!/bin/bash --posix


        or, where possible, set options in the script:



        set -o posix


        Alternatively, you can have the script restart itself with the appropriate shell invocation:



        #!/bin/sh -

        if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

        shift

        # Processing continues


        This approach can be generalised to other languages, as long as you find a way for the first couple of lines (which are interpreted by the shell) to be ignored by the target language.






        share|improve this answer




























          20












          20








          20







          There is no general solution, at least not if you need to support Linux, because the Linux kernel treats everything following the first “word” in the shebang line as a single argument.



          I’m not sure what NixOS’s constraints are, but typically I would just write your shebang as



          #!/bin/bash --posix


          or, where possible, set options in the script:



          set -o posix


          Alternatively, you can have the script restart itself with the appropriate shell invocation:



          #!/bin/sh -

          if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

          shift

          # Processing continues


          This approach can be generalised to other languages, as long as you find a way for the first couple of lines (which are interpreted by the shell) to be ignored by the target language.






          share|improve this answer















          There is no general solution, at least not if you need to support Linux, because the Linux kernel treats everything following the first “word” in the shebang line as a single argument.



          I’m not sure what NixOS’s constraints are, but typically I would just write your shebang as



          #!/bin/bash --posix


          or, where possible, set options in the script:



          set -o posix


          Alternatively, you can have the script restart itself with the appropriate shell invocation:



          #!/bin/sh -

          if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

          shift

          # Processing continues


          This approach can be generalised to other languages, as long as you find a way for the first couple of lines (which are interpreted by the shell) to be ignored by the target language.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jun 21 '18 at 11:46









          Stéphane Chazelas

          306k57577931




          306k57577931










          answered Oct 22 '17 at 11:55









          Stephen KittStephen Kitt

          172k24386465




          172k24386465

























              7














              The shebang is described in execve(2) man page as follow:



              #! interpreter [optional-arg]


              Two spaces are accepted in this syntax:




              1. One space before the interpreter path, but this space is optional.

              2. One space separating the the interpreter path and its optional argument.


              Note that I didn't used the plural when talking of an optional argument, neither does the syntax above uses [optional-arg ...], as you can provide at most one single argument.



              As far as shell scripting is concerned, you can use the set built-in command near the beginning of your script which will allow to set interpreters parameters, providing the same result as if you used command-line arguments.



              In your case:



              set -o posix


              From a Bash prompt, check the output of help set to get all available options.






              share|improve this answer



















              • 1





                You’re allowed to have more than two spaces, they’re just considered to be part of the optional argument.

                – Stephen Kitt
                Oct 22 '17 at 11:56











              • @StephenKitt: Indeed, white space here is to be taken more as a category than the actual space char. I suppose that other white spaces such as tabs should also be widely accepted.

                – WhiteWinterWolf
                Oct 22 '17 at 12:14


















              7














              The shebang is described in execve(2) man page as follow:



              #! interpreter [optional-arg]


              Two spaces are accepted in this syntax:




              1. One space before the interpreter path, but this space is optional.

              2. One space separating the the interpreter path and its optional argument.


              Note that I didn't used the plural when talking of an optional argument, neither does the syntax above uses [optional-arg ...], as you can provide at most one single argument.



              As far as shell scripting is concerned, you can use the set built-in command near the beginning of your script which will allow to set interpreters parameters, providing the same result as if you used command-line arguments.



              In your case:



              set -o posix


              From a Bash prompt, check the output of help set to get all available options.






              share|improve this answer



















              • 1





                You’re allowed to have more than two spaces, they’re just considered to be part of the optional argument.

                – Stephen Kitt
                Oct 22 '17 at 11:56











              • @StephenKitt: Indeed, white space here is to be taken more as a category than the actual space char. I suppose that other white spaces such as tabs should also be widely accepted.

                – WhiteWinterWolf
                Oct 22 '17 at 12:14
















              7












              7








              7







              The shebang is described in execve(2) man page as follow:



              #! interpreter [optional-arg]


              Two spaces are accepted in this syntax:




              1. One space before the interpreter path, but this space is optional.

              2. One space separating the the interpreter path and its optional argument.


              Note that I didn't used the plural when talking of an optional argument, neither does the syntax above uses [optional-arg ...], as you can provide at most one single argument.



              As far as shell scripting is concerned, you can use the set built-in command near the beginning of your script which will allow to set interpreters parameters, providing the same result as if you used command-line arguments.



              In your case:



              set -o posix


              From a Bash prompt, check the output of help set to get all available options.






              share|improve this answer













              The shebang is described in execve(2) man page as follow:



              #! interpreter [optional-arg]


              Two spaces are accepted in this syntax:




              1. One space before the interpreter path, but this space is optional.

              2. One space separating the the interpreter path and its optional argument.


              Note that I didn't used the plural when talking of an optional argument, neither does the syntax above uses [optional-arg ...], as you can provide at most one single argument.



              As far as shell scripting is concerned, you can use the set built-in command near the beginning of your script which will allow to set interpreters parameters, providing the same result as if you used command-line arguments.



              In your case:



              set -o posix


              From a Bash prompt, check the output of help set to get all available options.







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Oct 22 '17 at 11:51









              WhiteWinterWolfWhiteWinterWolf

              1,666930




              1,666930








              • 1





                You’re allowed to have more than two spaces, they’re just considered to be part of the optional argument.

                – Stephen Kitt
                Oct 22 '17 at 11:56











              • @StephenKitt: Indeed, white space here is to be taken more as a category than the actual space char. I suppose that other white spaces such as tabs should also be widely accepted.

                – WhiteWinterWolf
                Oct 22 '17 at 12:14
















              • 1





                You’re allowed to have more than two spaces, they’re just considered to be part of the optional argument.

                – Stephen Kitt
                Oct 22 '17 at 11:56











              • @StephenKitt: Indeed, white space here is to be taken more as a category than the actual space char. I suppose that other white spaces such as tabs should also be widely accepted.

                – WhiteWinterWolf
                Oct 22 '17 at 12:14










              1




              1





              You’re allowed to have more than two spaces, they’re just considered to be part of the optional argument.

              – Stephen Kitt
              Oct 22 '17 at 11:56





              You’re allowed to have more than two spaces, they’re just considered to be part of the optional argument.

              – Stephen Kitt
              Oct 22 '17 at 11:56













              @StephenKitt: Indeed, white space here is to be taken more as a category than the actual space char. I suppose that other white spaces such as tabs should also be widely accepted.

              – WhiteWinterWolf
              Oct 22 '17 at 12:14







              @StephenKitt: Indeed, white space here is to be taken more as a category than the actual space char. I suppose that other white spaces such as tabs should also be widely accepted.

              – WhiteWinterWolf
              Oct 22 '17 at 12:14













              7














              The POSIX standard is very terse on describing #!:



              From the rationale section of the documentation of the exec() family of system interfaces:




              Another way that some historical implementations handle shell scripts is by recognizing the first two bytes of the file as the character string #! and using the remainder of the first line of the file as the name of the command interpreter to execute.




              From the Shell Introduction section:




              The shell reads its input from a file (see sh), from the -c option or from the system() and popen() functions defined in the System Interfaces volume of POSIX.1-2008. If the first line of a file of shell commands starts with the characters #!, the results are unspecified.




              This basically means that any implementation (the Unix you are using) is free to do the specifics of the parsing of the shebang line as it wants.



              Some Unices, like macOS (can't test ATM), will split the arguments given to the interpreter on the shebang line into separate arguments, while Linux and most other Unices will give the arguments as a single option to the interpreter.



              It is thus unwise to rely on the shebang line being able to take more than a single argument.



              See also the Portability section of the Shebang article on Wikipedia.





              One easy solution, which is generalizable to any utility or language, is to make a wrapper script that executes the real script with the appropriate command line arguments:



              #!/bin/sh
              exec /bin/bash --posix /some/path/realscript "$@"


              I don't think I would personally try to make it re-execute itself as that feels somewhat fragile.






              share|improve this answer






























                7














                The POSIX standard is very terse on describing #!:



                From the rationale section of the documentation of the exec() family of system interfaces:




                Another way that some historical implementations handle shell scripts is by recognizing the first two bytes of the file as the character string #! and using the remainder of the first line of the file as the name of the command interpreter to execute.




                From the Shell Introduction section:




                The shell reads its input from a file (see sh), from the -c option or from the system() and popen() functions defined in the System Interfaces volume of POSIX.1-2008. If the first line of a file of shell commands starts with the characters #!, the results are unspecified.




                This basically means that any implementation (the Unix you are using) is free to do the specifics of the parsing of the shebang line as it wants.



                Some Unices, like macOS (can't test ATM), will split the arguments given to the interpreter on the shebang line into separate arguments, while Linux and most other Unices will give the arguments as a single option to the interpreter.



                It is thus unwise to rely on the shebang line being able to take more than a single argument.



                See also the Portability section of the Shebang article on Wikipedia.





                One easy solution, which is generalizable to any utility or language, is to make a wrapper script that executes the real script with the appropriate command line arguments:



                #!/bin/sh
                exec /bin/bash --posix /some/path/realscript "$@"


                I don't think I would personally try to make it re-execute itself as that feels somewhat fragile.






                share|improve this answer




























                  7












                  7








                  7







                  The POSIX standard is very terse on describing #!:



                  From the rationale section of the documentation of the exec() family of system interfaces:




                  Another way that some historical implementations handle shell scripts is by recognizing the first two bytes of the file as the character string #! and using the remainder of the first line of the file as the name of the command interpreter to execute.




                  From the Shell Introduction section:




                  The shell reads its input from a file (see sh), from the -c option or from the system() and popen() functions defined in the System Interfaces volume of POSIX.1-2008. If the first line of a file of shell commands starts with the characters #!, the results are unspecified.




                  This basically means that any implementation (the Unix you are using) is free to do the specifics of the parsing of the shebang line as it wants.



                  Some Unices, like macOS (can't test ATM), will split the arguments given to the interpreter on the shebang line into separate arguments, while Linux and most other Unices will give the arguments as a single option to the interpreter.



                  It is thus unwise to rely on the shebang line being able to take more than a single argument.



                  See also the Portability section of the Shebang article on Wikipedia.





                  One easy solution, which is generalizable to any utility or language, is to make a wrapper script that executes the real script with the appropriate command line arguments:



                  #!/bin/sh
                  exec /bin/bash --posix /some/path/realscript "$@"


                  I don't think I would personally try to make it re-execute itself as that feels somewhat fragile.






                  share|improve this answer















                  The POSIX standard is very terse on describing #!:



                  From the rationale section of the documentation of the exec() family of system interfaces:




                  Another way that some historical implementations handle shell scripts is by recognizing the first two bytes of the file as the character string #! and using the remainder of the first line of the file as the name of the command interpreter to execute.




                  From the Shell Introduction section:




                  The shell reads its input from a file (see sh), from the -c option or from the system() and popen() functions defined in the System Interfaces volume of POSIX.1-2008. If the first line of a file of shell commands starts with the characters #!, the results are unspecified.




                  This basically means that any implementation (the Unix you are using) is free to do the specifics of the parsing of the shebang line as it wants.



                  Some Unices, like macOS (can't test ATM), will split the arguments given to the interpreter on the shebang line into separate arguments, while Linux and most other Unices will give the arguments as a single option to the interpreter.



                  It is thus unwise to rely on the shebang line being able to take more than a single argument.



                  See also the Portability section of the Shebang article on Wikipedia.





                  One easy solution, which is generalizable to any utility or language, is to make a wrapper script that executes the real script with the appropriate command line arguments:



                  #!/bin/sh
                  exec /bin/bash --posix /some/path/realscript "$@"


                  I don't think I would personally try to make it re-execute itself as that feels somewhat fragile.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited May 27 '18 at 14:24

























                  answered Oct 22 '17 at 12:06









                  KusalanandaKusalananda

                  131k17249408




                  131k17249408























                      4














                      Although not exactly portable, starting with coreutils 8.30 and according to its documentation you will be able to use:



                      #!/usr/bin/env -S command arg1 arg2 ...


                      So given:



                      $ cat test.sh
                      #!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too


                      you will get:



                      % ./test.sh 
                      $0 is '/usr/local/bin/showargs'
                      $1 is 'here'
                      $2 is 'is another'
                      $3 is 'long'
                      $4 is 'arg'
                      $5 is '-e'
                      $6 is 'this and that '
                      $7 is 'too'
                      $8 is './test.sh'


                      and in case you are curious showargs is:



                      #!/usr/bin/env sh
                      echo "$0 is '$0'"

                      i=1
                      for arg in "$@"; do
                      echo "$$i is '$arg'"
                      i=$((i+1))
                      done





                      share|improve this answer
























                      • This is very good to know for future reference.

                        – John McGehee
                        Feb 4 at 21:39











                      • That option was copied from FreeBSD's env where -S was added in 2005. See lists.gnu.org/r/coreutils/2018-04/msg00011.html

                        – Stéphane Chazelas
                        Feb 6 at 7:19
















                      4














                      Although not exactly portable, starting with coreutils 8.30 and according to its documentation you will be able to use:



                      #!/usr/bin/env -S command arg1 arg2 ...


                      So given:



                      $ cat test.sh
                      #!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too


                      you will get:



                      % ./test.sh 
                      $0 is '/usr/local/bin/showargs'
                      $1 is 'here'
                      $2 is 'is another'
                      $3 is 'long'
                      $4 is 'arg'
                      $5 is '-e'
                      $6 is 'this and that '
                      $7 is 'too'
                      $8 is './test.sh'


                      and in case you are curious showargs is:



                      #!/usr/bin/env sh
                      echo "$0 is '$0'"

                      i=1
                      for arg in "$@"; do
                      echo "$$i is '$arg'"
                      i=$((i+1))
                      done





                      share|improve this answer
























                      • This is very good to know for future reference.

                        – John McGehee
                        Feb 4 at 21:39











                      • That option was copied from FreeBSD's env where -S was added in 2005. See lists.gnu.org/r/coreutils/2018-04/msg00011.html

                        – Stéphane Chazelas
                        Feb 6 at 7:19














                      4












                      4








                      4







                      Although not exactly portable, starting with coreutils 8.30 and according to its documentation you will be able to use:



                      #!/usr/bin/env -S command arg1 arg2 ...


                      So given:



                      $ cat test.sh
                      #!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too


                      you will get:



                      % ./test.sh 
                      $0 is '/usr/local/bin/showargs'
                      $1 is 'here'
                      $2 is 'is another'
                      $3 is 'long'
                      $4 is 'arg'
                      $5 is '-e'
                      $6 is 'this and that '
                      $7 is 'too'
                      $8 is './test.sh'


                      and in case you are curious showargs is:



                      #!/usr/bin/env sh
                      echo "$0 is '$0'"

                      i=1
                      for arg in "$@"; do
                      echo "$$i is '$arg'"
                      i=$((i+1))
                      done





                      share|improve this answer













                      Although not exactly portable, starting with coreutils 8.30 and according to its documentation you will be able to use:



                      #!/usr/bin/env -S command arg1 arg2 ...


                      So given:



                      $ cat test.sh
                      #!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too


                      you will get:



                      % ./test.sh 
                      $0 is '/usr/local/bin/showargs'
                      $1 is 'here'
                      $2 is 'is another'
                      $3 is 'long'
                      $4 is 'arg'
                      $5 is '-e'
                      $6 is 'this and that '
                      $7 is 'too'
                      $8 is './test.sh'


                      and in case you are curious showargs is:



                      #!/usr/bin/env sh
                      echo "$0 is '$0'"

                      i=1
                      for arg in "$@"; do
                      echo "$$i is '$arg'"
                      i=$((i+1))
                      done






                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Oct 25 '18 at 1:07









                      unodeunode

                      2951310




                      2951310













                      • This is very good to know for future reference.

                        – John McGehee
                        Feb 4 at 21:39











                      • That option was copied from FreeBSD's env where -S was added in 2005. See lists.gnu.org/r/coreutils/2018-04/msg00011.html

                        – Stéphane Chazelas
                        Feb 6 at 7:19



















                      • This is very good to know for future reference.

                        – John McGehee
                        Feb 4 at 21:39











                      • That option was copied from FreeBSD's env where -S was added in 2005. See lists.gnu.org/r/coreutils/2018-04/msg00011.html

                        – Stéphane Chazelas
                        Feb 6 at 7:19

















                      This is very good to know for future reference.

                      – John McGehee
                      Feb 4 at 21:39





                      This is very good to know for future reference.

                      – John McGehee
                      Feb 4 at 21:39













                      That option was copied from FreeBSD's env where -S was added in 2005. See lists.gnu.org/r/coreutils/2018-04/msg00011.html

                      – Stéphane Chazelas
                      Feb 6 at 7:19





                      That option was copied from FreeBSD's env where -S was added in 2005. See lists.gnu.org/r/coreutils/2018-04/msg00011.html

                      – Stéphane Chazelas
                      Feb 6 at 7:19











                      3














                      On Linux, the shebang isn't very flexible; according to multiple answers (Stephen Kitt's answer and Jörg W Mittag's), there is no designated way to pass multiple arguments in a shebang line.



                      I'm not sure if it will be of use to anyone, but I've written a short script to implement the lacking feature. See https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa.



                      It is also possible to write embedded workarounds. Bellow, I present four language-agnostic workarounds applied to the same test script and the result each prints. I suppose that the script is executable and is in /tmp/shebang.





                      Wrapping your script in a bash heredoc inside process substitution



                      As far as I know, this is the most reliable language-agnostic way of doing it. It allows passing arguments and preserves stdin. The drawback is that the interpreter doesn't know the (real) location of the file it reads.



                      #!/bin/bash
                      exec python3 -O <(cat << 'EOWRAPPER'
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv
                      try:
                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      except EOFError:
                      print("input() caused EOFError")
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")
                      EOWRAPPER
                      ) "$@"


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /dev/fd/62
                      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
                      __debug__ :: False
                      PYTHON_SCRIPT_END


                      Note that process substitution produces a special file. This may not suit all executables. For instance, #!/usr/bin/less complains: /dev/fd/63 is not a regular file (use -f to see it)



                      I don't know if it is possible to have heredoc inside process substitution in dash.





                      Wrapping your script in a simple heredoc



                      Shorter and simpler, but you won't be able to access stdin from your script and it requires the interpreter to be able to read and execute a script from stdin.



                      #!/bin/sh
                      exec python3 - "$@" << 'EOWRAPPER'
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      try:
                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      except EOFError:
                      print("input() caused EOFError")
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")
                      EOWRAPPER


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() caused EOFError
                      argv[0] :: -
                      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
                      __debug__ :: True
                      PYTHON_SCRIPT_END




                      Use awk system() call but without arguments



                      Correctly passes the name of the executed file, but your script won't receive the arguments you give it.
                      Note that awk is the only language I know whose interpreter both is installed on linux by default and reads its instructions from the command line by default.



                      #!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /tmp/shebang
                      argv[1:] ::
                      __debug__ :: False
                      PYTHON_SCRIPT_END




                      Use awk 4.1+ system() call, provided your arguments do not contain spaces



                      Nice, but only if you are sure your script won't be called with arguments containing spaces. As you can see, your arguments containing spaces would be split, unless the spaces are escaped.



                      #!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /tmp/shebang
                      argv[1:] :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \escapes\']
                      __debug__ :: False
                      PYTHON_SCRIPT_END


                      For awk versions below 4.1, you will have to use string concatenation inside a for loop, see example function https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .






                      share|improve this answer





















                      • 1





                        Quote the here document terminator to inhibit $variable or `command` substitution: exec python3 -O <(cat <<'EOWRAPPER'

                        – John McGehee
                        Feb 4 at 22:28








                      • 1





                        @JohnMcGehee Thanks. Done.

                        – loxaxs
                        Feb 6 at 6:37
















                      3














                      On Linux, the shebang isn't very flexible; according to multiple answers (Stephen Kitt's answer and Jörg W Mittag's), there is no designated way to pass multiple arguments in a shebang line.



                      I'm not sure if it will be of use to anyone, but I've written a short script to implement the lacking feature. See https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa.



                      It is also possible to write embedded workarounds. Bellow, I present four language-agnostic workarounds applied to the same test script and the result each prints. I suppose that the script is executable and is in /tmp/shebang.





                      Wrapping your script in a bash heredoc inside process substitution



                      As far as I know, this is the most reliable language-agnostic way of doing it. It allows passing arguments and preserves stdin. The drawback is that the interpreter doesn't know the (real) location of the file it reads.



                      #!/bin/bash
                      exec python3 -O <(cat << 'EOWRAPPER'
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv
                      try:
                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      except EOFError:
                      print("input() caused EOFError")
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")
                      EOWRAPPER
                      ) "$@"


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /dev/fd/62
                      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
                      __debug__ :: False
                      PYTHON_SCRIPT_END


                      Note that process substitution produces a special file. This may not suit all executables. For instance, #!/usr/bin/less complains: /dev/fd/63 is not a regular file (use -f to see it)



                      I don't know if it is possible to have heredoc inside process substitution in dash.





                      Wrapping your script in a simple heredoc



                      Shorter and simpler, but you won't be able to access stdin from your script and it requires the interpreter to be able to read and execute a script from stdin.



                      #!/bin/sh
                      exec python3 - "$@" << 'EOWRAPPER'
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      try:
                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      except EOFError:
                      print("input() caused EOFError")
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")
                      EOWRAPPER


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() caused EOFError
                      argv[0] :: -
                      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
                      __debug__ :: True
                      PYTHON_SCRIPT_END




                      Use awk system() call but without arguments



                      Correctly passes the name of the executed file, but your script won't receive the arguments you give it.
                      Note that awk is the only language I know whose interpreter both is installed on linux by default and reads its instructions from the command line by default.



                      #!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /tmp/shebang
                      argv[1:] ::
                      __debug__ :: False
                      PYTHON_SCRIPT_END




                      Use awk 4.1+ system() call, provided your arguments do not contain spaces



                      Nice, but only if you are sure your script won't be called with arguments containing spaces. As you can see, your arguments containing spaces would be split, unless the spaces are escaped.



                      #!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /tmp/shebang
                      argv[1:] :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \escapes\']
                      __debug__ :: False
                      PYTHON_SCRIPT_END


                      For awk versions below 4.1, you will have to use string concatenation inside a for loop, see example function https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .






                      share|improve this answer





















                      • 1





                        Quote the here document terminator to inhibit $variable or `command` substitution: exec python3 -O <(cat <<'EOWRAPPER'

                        – John McGehee
                        Feb 4 at 22:28








                      • 1





                        @JohnMcGehee Thanks. Done.

                        – loxaxs
                        Feb 6 at 6:37














                      3












                      3








                      3







                      On Linux, the shebang isn't very flexible; according to multiple answers (Stephen Kitt's answer and Jörg W Mittag's), there is no designated way to pass multiple arguments in a shebang line.



                      I'm not sure if it will be of use to anyone, but I've written a short script to implement the lacking feature. See https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa.



                      It is also possible to write embedded workarounds. Bellow, I present four language-agnostic workarounds applied to the same test script and the result each prints. I suppose that the script is executable and is in /tmp/shebang.





                      Wrapping your script in a bash heredoc inside process substitution



                      As far as I know, this is the most reliable language-agnostic way of doing it. It allows passing arguments and preserves stdin. The drawback is that the interpreter doesn't know the (real) location of the file it reads.



                      #!/bin/bash
                      exec python3 -O <(cat << 'EOWRAPPER'
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv
                      try:
                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      except EOFError:
                      print("input() caused EOFError")
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")
                      EOWRAPPER
                      ) "$@"


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /dev/fd/62
                      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
                      __debug__ :: False
                      PYTHON_SCRIPT_END


                      Note that process substitution produces a special file. This may not suit all executables. For instance, #!/usr/bin/less complains: /dev/fd/63 is not a regular file (use -f to see it)



                      I don't know if it is possible to have heredoc inside process substitution in dash.





                      Wrapping your script in a simple heredoc



                      Shorter and simpler, but you won't be able to access stdin from your script and it requires the interpreter to be able to read and execute a script from stdin.



                      #!/bin/sh
                      exec python3 - "$@" << 'EOWRAPPER'
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      try:
                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      except EOFError:
                      print("input() caused EOFError")
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")
                      EOWRAPPER


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() caused EOFError
                      argv[0] :: -
                      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
                      __debug__ :: True
                      PYTHON_SCRIPT_END




                      Use awk system() call but without arguments



                      Correctly passes the name of the executed file, but your script won't receive the arguments you give it.
                      Note that awk is the only language I know whose interpreter both is installed on linux by default and reads its instructions from the command line by default.



                      #!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /tmp/shebang
                      argv[1:] ::
                      __debug__ :: False
                      PYTHON_SCRIPT_END




                      Use awk 4.1+ system() call, provided your arguments do not contain spaces



                      Nice, but only if you are sure your script won't be called with arguments containing spaces. As you can see, your arguments containing spaces would be split, unless the spaces are escaped.



                      #!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /tmp/shebang
                      argv[1:] :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \escapes\']
                      __debug__ :: False
                      PYTHON_SCRIPT_END


                      For awk versions below 4.1, you will have to use string concatenation inside a for loop, see example function https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .






                      share|improve this answer















                      On Linux, the shebang isn't very flexible; according to multiple answers (Stephen Kitt's answer and Jörg W Mittag's), there is no designated way to pass multiple arguments in a shebang line.



                      I'm not sure if it will be of use to anyone, but I've written a short script to implement the lacking feature. See https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa.



                      It is also possible to write embedded workarounds. Bellow, I present four language-agnostic workarounds applied to the same test script and the result each prints. I suppose that the script is executable and is in /tmp/shebang.





                      Wrapping your script in a bash heredoc inside process substitution



                      As far as I know, this is the most reliable language-agnostic way of doing it. It allows passing arguments and preserves stdin. The drawback is that the interpreter doesn't know the (real) location of the file it reads.



                      #!/bin/bash
                      exec python3 -O <(cat << 'EOWRAPPER'
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv
                      try:
                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      except EOFError:
                      print("input() caused EOFError")
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")
                      EOWRAPPER
                      ) "$@"


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /dev/fd/62
                      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
                      __debug__ :: False
                      PYTHON_SCRIPT_END


                      Note that process substitution produces a special file. This may not suit all executables. For instance, #!/usr/bin/less complains: /dev/fd/63 is not a regular file (use -f to see it)



                      I don't know if it is possible to have heredoc inside process substitution in dash.





                      Wrapping your script in a simple heredoc



                      Shorter and simpler, but you won't be able to access stdin from your script and it requires the interpreter to be able to read and execute a script from stdin.



                      #!/bin/sh
                      exec python3 - "$@" << 'EOWRAPPER'
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      try:
                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      except EOFError:
                      print("input() caused EOFError")
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")
                      EOWRAPPER


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() caused EOFError
                      argv[0] :: -
                      argv[1:] :: ['arg1', 'arg2 contains spaces', 'arg3\ uses\ \\escapes\\']
                      __debug__ :: True
                      PYTHON_SCRIPT_END




                      Use awk system() call but without arguments



                      Correctly passes the name of the executed file, but your script won't receive the arguments you give it.
                      Note that awk is the only language I know whose interpreter both is installed on linux by default and reads its instructions from the command line by default.



                      #!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /tmp/shebang
                      argv[1:] ::
                      __debug__ :: False
                      PYTHON_SCRIPT_END




                      Use awk 4.1+ system() call, provided your arguments do not contain spaces



                      Nice, but only if you are sure your script won't be called with arguments containing spaces. As you can see, your arguments containing spaces would be split, unless the spaces are escaped.



                      #!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
                      print("PYTHON_SCRIPT_BEGINNING")

                      from sys import argv

                      print("input() 0 ::", input())
                      print("input() 1 ::", input())
                      print("argv[0] ::", argv[0])
                      print("argv[1:] ::", argv[1:])
                      print("__debug__ ::", __debug__)
                      # The -O option changes __debug__ to False

                      print("PYTHON_SCRIPT_END")


                      Calling echo -e 'aanbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3 uses \escapes\' prints:



                      PYTHON_SCRIPT_BEGINNING
                      input() 0 :: aa
                      input() 1 :: bb
                      argv[0] :: /tmp/shebang
                      argv[1:] :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \escapes\']
                      __debug__ :: False
                      PYTHON_SCRIPT_END


                      For awk versions below 4.1, you will have to use string concatenation inside a for loop, see example function https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Feb 6 at 6:55

























                      answered May 27 '18 at 1:05









                      loxaxsloxaxs

                      7291711




                      7291711








                      • 1





                        Quote the here document terminator to inhibit $variable or `command` substitution: exec python3 -O <(cat <<'EOWRAPPER'

                        – John McGehee
                        Feb 4 at 22:28








                      • 1





                        @JohnMcGehee Thanks. Done.

                        – loxaxs
                        Feb 6 at 6:37














                      • 1





                        Quote the here document terminator to inhibit $variable or `command` substitution: exec python3 -O <(cat <<'EOWRAPPER'

                        – John McGehee
                        Feb 4 at 22:28








                      • 1





                        @JohnMcGehee Thanks. Done.

                        – loxaxs
                        Feb 6 at 6:37








                      1




                      1





                      Quote the here document terminator to inhibit $variable or `command` substitution: exec python3 -O <(cat <<'EOWRAPPER'

                      – John McGehee
                      Feb 4 at 22:28







                      Quote the here document terminator to inhibit $variable or `command` substitution: exec python3 -O <(cat <<'EOWRAPPER'

                      – John McGehee
                      Feb 4 at 22:28






                      1




                      1





                      @JohnMcGehee Thanks. Done.

                      – loxaxs
                      Feb 6 at 6:37





                      @JohnMcGehee Thanks. Done.

                      – loxaxs
                      Feb 6 at 6:37


















                      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.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f399690%2fmultiple-arguments-in-shebang%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 reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

                      is 'sed' thread safe

                      How to make a Squid Proxy server?