Will $0 always include the path to the script?

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












9















I want to grep the current script so I can print out help and version information from the comments section at the top.



I was thinking of something like this:



grep '^#h ' -- "$0" | sed -e 's/#h //'


But then I wondered what would happen if the script was located in a directory that was in PATH and called without explicitly specifying the directory.



I searched for an explanation of the special variables and found the following descriptions of $0:



  • name of the current shell or program


  • filename of the current script


  • name of the script itself


  • command as it was run


None of these make it clear whether or not the value of $0 would include the directory if the script was invoked without it. The last one actually implies to me that it wouldn't.



Testing on My System (Bash 4.1)



I created an executable file in /usr/local/bin called scriptname with one line echo $0 and invoked it from different locations.



These are my results:



> cd /usr/local/bin/test
> ../scriptname
../scriptname

> cd /usr/local/bin
> ./scriptname
./scriptname

> cd /usr/local
> bin/scriptname
bin/scriptname

> cd /tmp
> /usr/local/bin/scriptname
/usr/local/bin/scriptname

> scriptname
/usr/local/bin/scriptname


In these tests, the value of $0 is always exactly how the script was invoked, except if it is invoked without any path component. In that case, the value of $0 is the absolute path. So that looks like it would be safe to pass to another command.



But then I came across a comment on Stack Overflow that confused me. The answer suggests using $(dirname $0) to get the directory of the current script. The comment (upvoted 7 times) says "that will not work if the script is in your path".



Questions



  • Is that comment correct?

  • Is the behavior different on other systems?

  • Are there situations where $0 would not include the directory?









share|improve this question
























  • People have answered about situations where $0 is something other than the script, which does answer the question title. However, I'm also interested in situations where $0 is the script itself, but does not include the directory. In particular, I'm trying to understand the comment made on the SO answer.

    – toxalot
    Mar 16 '14 at 11:21















9















I want to grep the current script so I can print out help and version information from the comments section at the top.



I was thinking of something like this:



grep '^#h ' -- "$0" | sed -e 's/#h //'


But then I wondered what would happen if the script was located in a directory that was in PATH and called without explicitly specifying the directory.



I searched for an explanation of the special variables and found the following descriptions of $0:



  • name of the current shell or program


  • filename of the current script


  • name of the script itself


  • command as it was run


None of these make it clear whether or not the value of $0 would include the directory if the script was invoked without it. The last one actually implies to me that it wouldn't.



Testing on My System (Bash 4.1)



I created an executable file in /usr/local/bin called scriptname with one line echo $0 and invoked it from different locations.



These are my results:



> cd /usr/local/bin/test
> ../scriptname
../scriptname

> cd /usr/local/bin
> ./scriptname
./scriptname

> cd /usr/local
> bin/scriptname
bin/scriptname

> cd /tmp
> /usr/local/bin/scriptname
/usr/local/bin/scriptname

> scriptname
/usr/local/bin/scriptname


In these tests, the value of $0 is always exactly how the script was invoked, except if it is invoked without any path component. In that case, the value of $0 is the absolute path. So that looks like it would be safe to pass to another command.



But then I came across a comment on Stack Overflow that confused me. The answer suggests using $(dirname $0) to get the directory of the current script. The comment (upvoted 7 times) says "that will not work if the script is in your path".



Questions



  • Is that comment correct?

  • Is the behavior different on other systems?

  • Are there situations where $0 would not include the directory?









share|improve this question
























  • People have answered about situations where $0 is something other than the script, which does answer the question title. However, I'm also interested in situations where $0 is the script itself, but does not include the directory. In particular, I'm trying to understand the comment made on the SO answer.

    – toxalot
    Mar 16 '14 at 11:21













9












9








9


6






I want to grep the current script so I can print out help and version information from the comments section at the top.



I was thinking of something like this:



grep '^#h ' -- "$0" | sed -e 's/#h //'


But then I wondered what would happen if the script was located in a directory that was in PATH and called without explicitly specifying the directory.



I searched for an explanation of the special variables and found the following descriptions of $0:



  • name of the current shell or program


  • filename of the current script


  • name of the script itself


  • command as it was run


None of these make it clear whether or not the value of $0 would include the directory if the script was invoked without it. The last one actually implies to me that it wouldn't.



Testing on My System (Bash 4.1)



I created an executable file in /usr/local/bin called scriptname with one line echo $0 and invoked it from different locations.



These are my results:



> cd /usr/local/bin/test
> ../scriptname
../scriptname

> cd /usr/local/bin
> ./scriptname
./scriptname

> cd /usr/local
> bin/scriptname
bin/scriptname

> cd /tmp
> /usr/local/bin/scriptname
/usr/local/bin/scriptname

> scriptname
/usr/local/bin/scriptname


In these tests, the value of $0 is always exactly how the script was invoked, except if it is invoked without any path component. In that case, the value of $0 is the absolute path. So that looks like it would be safe to pass to another command.



But then I came across a comment on Stack Overflow that confused me. The answer suggests using $(dirname $0) to get the directory of the current script. The comment (upvoted 7 times) says "that will not work if the script is in your path".



Questions



  • Is that comment correct?

  • Is the behavior different on other systems?

  • Are there situations where $0 would not include the directory?









share|improve this question
















I want to grep the current script so I can print out help and version information from the comments section at the top.



I was thinking of something like this:



grep '^#h ' -- "$0" | sed -e 's/#h //'


But then I wondered what would happen if the script was located in a directory that was in PATH and called without explicitly specifying the directory.



I searched for an explanation of the special variables and found the following descriptions of $0:



  • name of the current shell or program


  • filename of the current script


  • name of the script itself


  • command as it was run


None of these make it clear whether or not the value of $0 would include the directory if the script was invoked without it. The last one actually implies to me that it wouldn't.



Testing on My System (Bash 4.1)



I created an executable file in /usr/local/bin called scriptname with one line echo $0 and invoked it from different locations.



These are my results:



> cd /usr/local/bin/test
> ../scriptname
../scriptname

> cd /usr/local/bin
> ./scriptname
./scriptname

> cd /usr/local
> bin/scriptname
bin/scriptname

> cd /tmp
> /usr/local/bin/scriptname
/usr/local/bin/scriptname

> scriptname
/usr/local/bin/scriptname


In these tests, the value of $0 is always exactly how the script was invoked, except if it is invoked without any path component. In that case, the value of $0 is the absolute path. So that looks like it would be safe to pass to another command.



But then I came across a comment on Stack Overflow that confused me. The answer suggests using $(dirname $0) to get the directory of the current script. The comment (upvoted 7 times) says "that will not work if the script is in your path".



Questions



  • Is that comment correct?

  • Is the behavior different on other systems?

  • Are there situations where $0 would not include the directory?






shell-script path






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited May 23 '17 at 12:40









Community

1




1










asked Mar 16 '14 at 8:58









toxalottoxalot

5901618




5901618












  • People have answered about situations where $0 is something other than the script, which does answer the question title. However, I'm also interested in situations where $0 is the script itself, but does not include the directory. In particular, I'm trying to understand the comment made on the SO answer.

    – toxalot
    Mar 16 '14 at 11:21

















  • People have answered about situations where $0 is something other than the script, which does answer the question title. However, I'm also interested in situations where $0 is the script itself, but does not include the directory. In particular, I'm trying to understand the comment made on the SO answer.

    – toxalot
    Mar 16 '14 at 11:21
















People have answered about situations where $0 is something other than the script, which does answer the question title. However, I'm also interested in situations where $0 is the script itself, but does not include the directory. In particular, I'm trying to understand the comment made on the SO answer.

– toxalot
Mar 16 '14 at 11:21





People have answered about situations where $0 is something other than the script, which does answer the question title. However, I'm also interested in situations where $0 is the script itself, but does not include the directory. In particular, I'm trying to understand the comment made on the SO answer.

– toxalot
Mar 16 '14 at 11:21










7 Answers
7






active

oldest

votes


















17














In the most common cases, $0 will contain a path, absolute or relative to the script, so



script_path=$(readlink -e -- "$0")


(assuming there's a readlink command and it supports -e) generally is a good enough way to obtain the canonical absolute path to the script.



$0 is assigned from the argument specifying the script as passed to the interpreter.



For example, in:



the-shell -shell-options the/script its args


$0 gets the/script.



When you run:



the/script its args


Your shell will do a:



exec("the/script", ["the/script", "its", "args"])


If the script contains a #! /bin/sh - she-bang for instance, the system will transform that to:



exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])


(if it doesn't contain a she-bang, or more generally if the system returns a ENOEXEC error, then its your shell that will do the same thing)



There's an exception for setuid/setgid scripts on some systems, where the system will open the script on some fd x and run instead:



exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])


to avoid race conditions (in which case $0 will contain /dev/fd/x).



Now, you may argue that /dev/fd/x is a path to that script. Note however that if you read from $0, you'll break the script as you consume the input.



Now, there's a difference if the script command name as invoked doesn't contain a slash. In:



the-script its args


Your shell will look up the-script in $PATH. $PATH may contain absolute or relative (including the empty string) paths to some directories. For instance, if $PATH contains /bin:/usr/bin: and the-script is found in the current directory, the shell will do a:



exec("the-script", ["the-script", "its", "args"])


which will become:



exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]


Or if it's found in /usr/bin:



exec("/usr/bin/the-script", ["the-script", "its", "args"])
exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
"-", "/usr/bin/the-script", "its", "args")


In all those cases above except the setuid corner case, $0 will contain a path (absolute or relative) to the script.



Now, a script can also be called as:



the-interpreter the-script its args


When the-script as above doesn't contain slash characters, the behaviour varies slightly from shell to shell.



Old AT&T ksh implementations were actually looking up the-script unconditionally in $PATH (which was actually a bug and a security hole for setuid scripts), so $0 actually did not contain a path to the script unless the $PATH lookup actually happened to find the-script in the current directory.



Newer AT&T ksh would try and interpret the-script in the current directory if it's readable. If not it would lookup for a readable and executable the-script in $PATH.



For bash, it checks if the-script is in the current directory (and is not a broken symlink) and if not, lookup for a readable (not necessarily executable) the-script in $PATH.



zsh in sh emulation would do like bash except that if the-script is a broken symlink in the current directory, it would not search for a the-script in $PATH and would instead report an error.



All the other Bourne-like shells don't look the-script up in $PATH.



For all those shells anyway, if you find that $0 doesn't contain a / and is not readable, then it probably has been looked up in $PATH. Then, as files in $PATH are likely to be executable, it's probably a safe approximation to use command -v -- "$0" to find its path (though that wouldn't work if $0 happens to also be the name of a shell builtin or keyword (in most shells)).



So if you really want to cover for that case, you could write it:



progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in $PATH-$(getconf PATH)""; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%sn' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown


(the "" appended to $PATH is to preserve a trailing empty element with shells whose $IFS acts as delimiter instead of separator).



Now, there are more esoteric ways to invoke a script. One could do:



the-shell < the-script


Or:



cat the-script | the-shell


In that case, $0 will be the first argument (argv[0]) that the interpreter received (above the-shell, but that could be anything though generally either the basename or one path to that interpreter).



Detecting that you're in that situation based on the value of $0 is not reliable. You could look at the output of ps -o args= -p "$$" to get a clue. In the pipe case, there's no real way you can get back to a path to the script.



One could also do:



the-shell -c '. the-script' blah blih


Then, except in zsh (and some old implementation of the Bourne shell), $0 would be blah. Again, hard to get to the path of the script in those shells.



Or:



the-shell -c "$(cat the-script)" blah blih


etc.



To make sure you have the right $progname, you could search for a specific string in it like:



progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in $PATH-$(getconf PATH):; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%sn' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown

[ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
progname=unknown


But again I don't think it's worth the effort.






share|improve this answer

























  • Stéphane, I don't understand your use of "-" in the above examples. In my experience, exec("the-script", ["the-script", "its", "args"]) becomes exec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"]), with of course the possibility of an interpreter option.

    – jrw32982
    Aug 3 '16 at 4:28












  • @jrw32982, #! /bin/sh - is the "always use cmd -- something if you can't guarantee that something won't start with -" good practice adage here applied to /bin/sh (where - as the end-of-option marker is more portable than --) with something being the path/name of the script. If you don't use that for setuid scripts (on systems that support them but not with the /dev/fd/x method mentioned in the answer), then one can get a root shell by creating a symlink to your script called -i or -s for instance.

    – Stéphane Chazelas
    Aug 3 '16 at 9:00












  • Thanks, Stéphane. I had missed the trailing single hyphen in your example shebang line. I had to hunt for where it's documented that a single hyphen is equivalent to a double hyphen pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html.

    – jrw32982
    Aug 3 '16 at 20:24












  • It's too easy to forget the trailing single/double hyphen in the shebang line for a setuid script; somehow the system should take care of it for you. Disallow setuid scripts altogether or somehow /bin/sh should disable its own option processing if it can detect that it's running setuid. I don't see how using /dev/fd/x by itself fixes that. You still need the single/double hyphen, I think.

    – jrw32982
    Aug 3 '16 at 20:26











  • @jrw32982, /dev/fd/x starts with /, not -. The primary goal is to remove the race condition between the two execve()s though (in between the execve("the-script") which elevates privileges and the subsequent execve("interpreter", "thescript") where interpreter opens thescript later (which may very well have been replaces with a symlink to something else in the meantime). Systems that implement suid scripts properly do a execve("interpreter", "/dev/fd/n") instead where n has been opened as part of the first execve().

    – Stéphane Chazelas
    Aug 4 '16 at 6:41


















6














Here are two situations where the directory would not be included:



> bash scriptname
scriptname

> bash <scriptname
bash


In both cases, the current directory would have to be the directory where scriptname was located.



In the first case, the value of $0 could still be passed to grep since it assumes the FILE argument is relative to the current directory.



In the second case, if the help and version information is only printed in response to a specific command line option, it shouldn't be a problem. I'm not sure why anyone would invoke a script that way to print the help or version information.



Caveats



  • If the script changes the current directory, you wouldn't want to use relative paths.


  • If the script is sourced, the value of $0 will usually be the caller script rather than the sourced script.






share|improve this answer






























    3














    An arbitrary argument zero can be specified when using the -c option to most (all?) shells. Eg:



    sh -c 'echo $0' argv0


    From man bash (chosen purely because this has a better description than my man sh - usage is the same regardless):




    -c



    If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are
    assigned to the positional parameters, starting with $0.







    share|improve this answer

























    • I think this works because -c 'command' are operands, and argv0 is its first nonoperand commandline argument.

      – mikeserv
      Mar 16 '14 at 11:10











    • @mike correct, I have updated with a man snippet.

      – Graeme
      Mar 16 '14 at 11:19


















    3














    NOTE: Other's have already explained the mechanics of $0 so I'll skip all that.



    I generally side step this entire issue and simply use the command readlink -f $0. This will always give you back the full path of whatever you give to it as an argument.



    Examples



    Say I'm here to start with:



    $ pwd
    /home/saml/tst/119929/adir


    Make a directory + file:



    $ mkdir adir
    $ touch afile
    $ cd adir/


    Now start showing off readlink:



    $ readlink -f ../adir
    /home/saml/tst/119929/adir

    $ readlink -f ../
    /home/saml/tst/119929

    $ readlink -f ../afile
    /home/saml/tst/119929/afile

    $ readlink -f .
    /home/saml/tst/119929/adir


    Additional trickery



    Now with a consistent result being returned when we interrogate $0 via readlink we can use a simply dirname $(readlink -f $0) to get the absolute path to the script -or- basename $(readlink -f $0) to get the actual name of the script.






    share|improve this answer






























      0














      My man page says:





      $0 : expands to the name of shell or shell script.





      It seems this translates to argv[0] of the current shell - or the first nonoperand commandline argument the currently interpreting shell is fed when invoked. I previously stated that sh ./somescript would path its $0 $ENV variable to sh but this was incorrect because the shell is a new process of its own and invoked with a new $ENV.



      In this way sh ./somescript.sh differs from . ./somescript.sh which runs in the current environment and $0 is already set.



      You can check this by comparing $0 to /proc/$$/status.



      echo 'script="/proc/$$/status"
      echo $0
      cat "$script"'
      > ./script.sh
      sh ./script.sh ; . ./script.sh


      Thanks for the correction, @toxalot. I learned something.






      share|improve this answer

























      • On my system, if it's sourced (. ./myscript.sh or source ./myscript.sh), then $0 is the shell. But if it's passed as an argument to the shell (sh ./myscript.sh), then $0 is the path to the script. Of course, sh on my system is Bash. So I don't know if that makes a difference or not.

        – toxalot
        Mar 16 '14 at 10:32












      • Does it have a hashbang? I think that the difference matters - and I can add that - without it sh should not exec it and instead source it, but with it, it should exec it.

        – mikeserv
        Mar 16 '14 at 10:34











      • With or without hashbang, I get same results. With or without being executable, I get same results.

        – toxalot
        Mar 16 '14 at 10:37











      • Me too! I think it's because it's a shell builtin. I'm checking...

        – mikeserv
        Mar 16 '14 at 10:38











      • Bang lines are interpreted by the kernel. If you exec ./somescript with the bangline #!/bin/sh, it would be equivalent to running /bin/sh ./somescript. Otherwise they make no difference to the shell.

        – Graeme
        Mar 16 '14 at 11:36


















      0















      I want to grep the current script so I can print out help and version information from the comments section at the top.




      While $0 contains the script name it may contain a prefixed path based on the way the script is called, I have always used $0##*/ to print the script name in the help output which removes any leading path from $0.



      Taken from Advanced Bash Scripting guide - Section 10.2 parameter substitution




      $var#Pattern



      Remove from $var the shortest part of $Pattern that matches the front end of $var.



      $var##Pattern



      Remove from $var the longest part of $Pattern that matches the front end of $var.




      So the longest part of $0 that matches */ will be the entire path prefix, returning only the script name.






      share|improve this answer

























      • Yes, I do that too for the name of the script that is used within the help message. But I'm talking about grepping the script so I need to have at least a relative path. I want to put the help message at the top in the comments and not have to repeat the help message later when printing it.

        – toxalot
        Mar 16 '14 at 13:32


















      0














      For something similar I use:



      rPath="$(dirname $(realpath $0))"
      echo $rPath

      rPath=$(dirname $(readlink -e -- "$0"))
      echo $rPath


      rPath always has the same value.






      share|improve this answer
























        Your Answer








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

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

        else
        createEditor();

        );

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



        );













        draft saved

        draft discarded


















        StackExchange.ready(
        function ()
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f119929%2fwill-0-always-include-the-path-to-the-script%23new-answer', 'question_page');

        );

        Post as a guest















        Required, but never shown

























        7 Answers
        7






        active

        oldest

        votes








        7 Answers
        7






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        17














        In the most common cases, $0 will contain a path, absolute or relative to the script, so



        script_path=$(readlink -e -- "$0")


        (assuming there's a readlink command and it supports -e) generally is a good enough way to obtain the canonical absolute path to the script.



        $0 is assigned from the argument specifying the script as passed to the interpreter.



        For example, in:



        the-shell -shell-options the/script its args


        $0 gets the/script.



        When you run:



        the/script its args


        Your shell will do a:



        exec("the/script", ["the/script", "its", "args"])


        If the script contains a #! /bin/sh - she-bang for instance, the system will transform that to:



        exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])


        (if it doesn't contain a she-bang, or more generally if the system returns a ENOEXEC error, then its your shell that will do the same thing)



        There's an exception for setuid/setgid scripts on some systems, where the system will open the script on some fd x and run instead:



        exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])


        to avoid race conditions (in which case $0 will contain /dev/fd/x).



        Now, you may argue that /dev/fd/x is a path to that script. Note however that if you read from $0, you'll break the script as you consume the input.



        Now, there's a difference if the script command name as invoked doesn't contain a slash. In:



        the-script its args


        Your shell will look up the-script in $PATH. $PATH may contain absolute or relative (including the empty string) paths to some directories. For instance, if $PATH contains /bin:/usr/bin: and the-script is found in the current directory, the shell will do a:



        exec("the-script", ["the-script", "its", "args"])


        which will become:



        exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]


        Or if it's found in /usr/bin:



        exec("/usr/bin/the-script", ["the-script", "its", "args"])
        exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
        "-", "/usr/bin/the-script", "its", "args")


        In all those cases above except the setuid corner case, $0 will contain a path (absolute or relative) to the script.



        Now, a script can also be called as:



        the-interpreter the-script its args


        When the-script as above doesn't contain slash characters, the behaviour varies slightly from shell to shell.



        Old AT&T ksh implementations were actually looking up the-script unconditionally in $PATH (which was actually a bug and a security hole for setuid scripts), so $0 actually did not contain a path to the script unless the $PATH lookup actually happened to find the-script in the current directory.



        Newer AT&T ksh would try and interpret the-script in the current directory if it's readable. If not it would lookup for a readable and executable the-script in $PATH.



        For bash, it checks if the-script is in the current directory (and is not a broken symlink) and if not, lookup for a readable (not necessarily executable) the-script in $PATH.



        zsh in sh emulation would do like bash except that if the-script is a broken symlink in the current directory, it would not search for a the-script in $PATH and would instead report an error.



        All the other Bourne-like shells don't look the-script up in $PATH.



        For all those shells anyway, if you find that $0 doesn't contain a / and is not readable, then it probably has been looked up in $PATH. Then, as files in $PATH are likely to be executable, it's probably a safe approximation to use command -v -- "$0" to find its path (though that wouldn't work if $0 happens to also be the name of a shell builtin or keyword (in most shells)).



        So if you really want to cover for that case, you could write it:



        progname=$0
        [ -r "$progname" ] || progname=$(
        IFS=:; set -f
        for i in $PATH-$(getconf PATH)""; do
        case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
        esac
        [ -r "$p" ] && exec printf '%sn' "$p"
        done
        exit 1
        ) && progname=$(readlink -e -- "$progname") ||
        progname=unknown


        (the "" appended to $PATH is to preserve a trailing empty element with shells whose $IFS acts as delimiter instead of separator).



        Now, there are more esoteric ways to invoke a script. One could do:



        the-shell < the-script


        Or:



        cat the-script | the-shell


        In that case, $0 will be the first argument (argv[0]) that the interpreter received (above the-shell, but that could be anything though generally either the basename or one path to that interpreter).



        Detecting that you're in that situation based on the value of $0 is not reliable. You could look at the output of ps -o args= -p "$$" to get a clue. In the pipe case, there's no real way you can get back to a path to the script.



        One could also do:



        the-shell -c '. the-script' blah blih


        Then, except in zsh (and some old implementation of the Bourne shell), $0 would be blah. Again, hard to get to the path of the script in those shells.



        Or:



        the-shell -c "$(cat the-script)" blah blih


        etc.



        To make sure you have the right $progname, you could search for a specific string in it like:



        progname=$0
        [ -r "$progname" ] || progname=$(
        IFS=:; set -f
        for i in $PATH-$(getconf PATH):; do
        case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
        esac
        [ -r "$p" ] && exec printf '%sn' "$p"
        done
        exit 1
        ) && progname=$(readlink -e -- "$progname") ||
        progname=unknown

        [ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
        progname=unknown


        But again I don't think it's worth the effort.






        share|improve this answer

























        • Stéphane, I don't understand your use of "-" in the above examples. In my experience, exec("the-script", ["the-script", "its", "args"]) becomes exec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"]), with of course the possibility of an interpreter option.

          – jrw32982
          Aug 3 '16 at 4:28












        • @jrw32982, #! /bin/sh - is the "always use cmd -- something if you can't guarantee that something won't start with -" good practice adage here applied to /bin/sh (where - as the end-of-option marker is more portable than --) with something being the path/name of the script. If you don't use that for setuid scripts (on systems that support them but not with the /dev/fd/x method mentioned in the answer), then one can get a root shell by creating a symlink to your script called -i or -s for instance.

          – Stéphane Chazelas
          Aug 3 '16 at 9:00












        • Thanks, Stéphane. I had missed the trailing single hyphen in your example shebang line. I had to hunt for where it's documented that a single hyphen is equivalent to a double hyphen pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html.

          – jrw32982
          Aug 3 '16 at 20:24












        • It's too easy to forget the trailing single/double hyphen in the shebang line for a setuid script; somehow the system should take care of it for you. Disallow setuid scripts altogether or somehow /bin/sh should disable its own option processing if it can detect that it's running setuid. I don't see how using /dev/fd/x by itself fixes that. You still need the single/double hyphen, I think.

          – jrw32982
          Aug 3 '16 at 20:26











        • @jrw32982, /dev/fd/x starts with /, not -. The primary goal is to remove the race condition between the two execve()s though (in between the execve("the-script") which elevates privileges and the subsequent execve("interpreter", "thescript") where interpreter opens thescript later (which may very well have been replaces with a symlink to something else in the meantime). Systems that implement suid scripts properly do a execve("interpreter", "/dev/fd/n") instead where n has been opened as part of the first execve().

          – Stéphane Chazelas
          Aug 4 '16 at 6:41















        17














        In the most common cases, $0 will contain a path, absolute or relative to the script, so



        script_path=$(readlink -e -- "$0")


        (assuming there's a readlink command and it supports -e) generally is a good enough way to obtain the canonical absolute path to the script.



        $0 is assigned from the argument specifying the script as passed to the interpreter.



        For example, in:



        the-shell -shell-options the/script its args


        $0 gets the/script.



        When you run:



        the/script its args


        Your shell will do a:



        exec("the/script", ["the/script", "its", "args"])


        If the script contains a #! /bin/sh - she-bang for instance, the system will transform that to:



        exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])


        (if it doesn't contain a she-bang, or more generally if the system returns a ENOEXEC error, then its your shell that will do the same thing)



        There's an exception for setuid/setgid scripts on some systems, where the system will open the script on some fd x and run instead:



        exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])


        to avoid race conditions (in which case $0 will contain /dev/fd/x).



        Now, you may argue that /dev/fd/x is a path to that script. Note however that if you read from $0, you'll break the script as you consume the input.



        Now, there's a difference if the script command name as invoked doesn't contain a slash. In:



        the-script its args


        Your shell will look up the-script in $PATH. $PATH may contain absolute or relative (including the empty string) paths to some directories. For instance, if $PATH contains /bin:/usr/bin: and the-script is found in the current directory, the shell will do a:



        exec("the-script", ["the-script", "its", "args"])


        which will become:



        exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]


        Or if it's found in /usr/bin:



        exec("/usr/bin/the-script", ["the-script", "its", "args"])
        exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
        "-", "/usr/bin/the-script", "its", "args")


        In all those cases above except the setuid corner case, $0 will contain a path (absolute or relative) to the script.



        Now, a script can also be called as:



        the-interpreter the-script its args


        When the-script as above doesn't contain slash characters, the behaviour varies slightly from shell to shell.



        Old AT&T ksh implementations were actually looking up the-script unconditionally in $PATH (which was actually a bug and a security hole for setuid scripts), so $0 actually did not contain a path to the script unless the $PATH lookup actually happened to find the-script in the current directory.



        Newer AT&T ksh would try and interpret the-script in the current directory if it's readable. If not it would lookup for a readable and executable the-script in $PATH.



        For bash, it checks if the-script is in the current directory (and is not a broken symlink) and if not, lookup for a readable (not necessarily executable) the-script in $PATH.



        zsh in sh emulation would do like bash except that if the-script is a broken symlink in the current directory, it would not search for a the-script in $PATH and would instead report an error.



        All the other Bourne-like shells don't look the-script up in $PATH.



        For all those shells anyway, if you find that $0 doesn't contain a / and is not readable, then it probably has been looked up in $PATH. Then, as files in $PATH are likely to be executable, it's probably a safe approximation to use command -v -- "$0" to find its path (though that wouldn't work if $0 happens to also be the name of a shell builtin or keyword (in most shells)).



        So if you really want to cover for that case, you could write it:



        progname=$0
        [ -r "$progname" ] || progname=$(
        IFS=:; set -f
        for i in $PATH-$(getconf PATH)""; do
        case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
        esac
        [ -r "$p" ] && exec printf '%sn' "$p"
        done
        exit 1
        ) && progname=$(readlink -e -- "$progname") ||
        progname=unknown


        (the "" appended to $PATH is to preserve a trailing empty element with shells whose $IFS acts as delimiter instead of separator).



        Now, there are more esoteric ways to invoke a script. One could do:



        the-shell < the-script


        Or:



        cat the-script | the-shell


        In that case, $0 will be the first argument (argv[0]) that the interpreter received (above the-shell, but that could be anything though generally either the basename or one path to that interpreter).



        Detecting that you're in that situation based on the value of $0 is not reliable. You could look at the output of ps -o args= -p "$$" to get a clue. In the pipe case, there's no real way you can get back to a path to the script.



        One could also do:



        the-shell -c '. the-script' blah blih


        Then, except in zsh (and some old implementation of the Bourne shell), $0 would be blah. Again, hard to get to the path of the script in those shells.



        Or:



        the-shell -c "$(cat the-script)" blah blih


        etc.



        To make sure you have the right $progname, you could search for a specific string in it like:



        progname=$0
        [ -r "$progname" ] || progname=$(
        IFS=:; set -f
        for i in $PATH-$(getconf PATH):; do
        case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
        esac
        [ -r "$p" ] && exec printf '%sn' "$p"
        done
        exit 1
        ) && progname=$(readlink -e -- "$progname") ||
        progname=unknown

        [ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
        progname=unknown


        But again I don't think it's worth the effort.






        share|improve this answer

























        • Stéphane, I don't understand your use of "-" in the above examples. In my experience, exec("the-script", ["the-script", "its", "args"]) becomes exec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"]), with of course the possibility of an interpreter option.

          – jrw32982
          Aug 3 '16 at 4:28












        • @jrw32982, #! /bin/sh - is the "always use cmd -- something if you can't guarantee that something won't start with -" good practice adage here applied to /bin/sh (where - as the end-of-option marker is more portable than --) with something being the path/name of the script. If you don't use that for setuid scripts (on systems that support them but not with the /dev/fd/x method mentioned in the answer), then one can get a root shell by creating a symlink to your script called -i or -s for instance.

          – Stéphane Chazelas
          Aug 3 '16 at 9:00












        • Thanks, Stéphane. I had missed the trailing single hyphen in your example shebang line. I had to hunt for where it's documented that a single hyphen is equivalent to a double hyphen pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html.

          – jrw32982
          Aug 3 '16 at 20:24












        • It's too easy to forget the trailing single/double hyphen in the shebang line for a setuid script; somehow the system should take care of it for you. Disallow setuid scripts altogether or somehow /bin/sh should disable its own option processing if it can detect that it's running setuid. I don't see how using /dev/fd/x by itself fixes that. You still need the single/double hyphen, I think.

          – jrw32982
          Aug 3 '16 at 20:26











        • @jrw32982, /dev/fd/x starts with /, not -. The primary goal is to remove the race condition between the two execve()s though (in between the execve("the-script") which elevates privileges and the subsequent execve("interpreter", "thescript") where interpreter opens thescript later (which may very well have been replaces with a symlink to something else in the meantime). Systems that implement suid scripts properly do a execve("interpreter", "/dev/fd/n") instead where n has been opened as part of the first execve().

          – Stéphane Chazelas
          Aug 4 '16 at 6:41













        17












        17








        17







        In the most common cases, $0 will contain a path, absolute or relative to the script, so



        script_path=$(readlink -e -- "$0")


        (assuming there's a readlink command and it supports -e) generally is a good enough way to obtain the canonical absolute path to the script.



        $0 is assigned from the argument specifying the script as passed to the interpreter.



        For example, in:



        the-shell -shell-options the/script its args


        $0 gets the/script.



        When you run:



        the/script its args


        Your shell will do a:



        exec("the/script", ["the/script", "its", "args"])


        If the script contains a #! /bin/sh - she-bang for instance, the system will transform that to:



        exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])


        (if it doesn't contain a she-bang, or more generally if the system returns a ENOEXEC error, then its your shell that will do the same thing)



        There's an exception for setuid/setgid scripts on some systems, where the system will open the script on some fd x and run instead:



        exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])


        to avoid race conditions (in which case $0 will contain /dev/fd/x).



        Now, you may argue that /dev/fd/x is a path to that script. Note however that if you read from $0, you'll break the script as you consume the input.



        Now, there's a difference if the script command name as invoked doesn't contain a slash. In:



        the-script its args


        Your shell will look up the-script in $PATH. $PATH may contain absolute or relative (including the empty string) paths to some directories. For instance, if $PATH contains /bin:/usr/bin: and the-script is found in the current directory, the shell will do a:



        exec("the-script", ["the-script", "its", "args"])


        which will become:



        exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]


        Or if it's found in /usr/bin:



        exec("/usr/bin/the-script", ["the-script", "its", "args"])
        exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
        "-", "/usr/bin/the-script", "its", "args")


        In all those cases above except the setuid corner case, $0 will contain a path (absolute or relative) to the script.



        Now, a script can also be called as:



        the-interpreter the-script its args


        When the-script as above doesn't contain slash characters, the behaviour varies slightly from shell to shell.



        Old AT&T ksh implementations were actually looking up the-script unconditionally in $PATH (which was actually a bug and a security hole for setuid scripts), so $0 actually did not contain a path to the script unless the $PATH lookup actually happened to find the-script in the current directory.



        Newer AT&T ksh would try and interpret the-script in the current directory if it's readable. If not it would lookup for a readable and executable the-script in $PATH.



        For bash, it checks if the-script is in the current directory (and is not a broken symlink) and if not, lookup for a readable (not necessarily executable) the-script in $PATH.



        zsh in sh emulation would do like bash except that if the-script is a broken symlink in the current directory, it would not search for a the-script in $PATH and would instead report an error.



        All the other Bourne-like shells don't look the-script up in $PATH.



        For all those shells anyway, if you find that $0 doesn't contain a / and is not readable, then it probably has been looked up in $PATH. Then, as files in $PATH are likely to be executable, it's probably a safe approximation to use command -v -- "$0" to find its path (though that wouldn't work if $0 happens to also be the name of a shell builtin or keyword (in most shells)).



        So if you really want to cover for that case, you could write it:



        progname=$0
        [ -r "$progname" ] || progname=$(
        IFS=:; set -f
        for i in $PATH-$(getconf PATH)""; do
        case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
        esac
        [ -r "$p" ] && exec printf '%sn' "$p"
        done
        exit 1
        ) && progname=$(readlink -e -- "$progname") ||
        progname=unknown


        (the "" appended to $PATH is to preserve a trailing empty element with shells whose $IFS acts as delimiter instead of separator).



        Now, there are more esoteric ways to invoke a script. One could do:



        the-shell < the-script


        Or:



        cat the-script | the-shell


        In that case, $0 will be the first argument (argv[0]) that the interpreter received (above the-shell, but that could be anything though generally either the basename or one path to that interpreter).



        Detecting that you're in that situation based on the value of $0 is not reliable. You could look at the output of ps -o args= -p "$$" to get a clue. In the pipe case, there's no real way you can get back to a path to the script.



        One could also do:



        the-shell -c '. the-script' blah blih


        Then, except in zsh (and some old implementation of the Bourne shell), $0 would be blah. Again, hard to get to the path of the script in those shells.



        Or:



        the-shell -c "$(cat the-script)" blah blih


        etc.



        To make sure you have the right $progname, you could search for a specific string in it like:



        progname=$0
        [ -r "$progname" ] || progname=$(
        IFS=:; set -f
        for i in $PATH-$(getconf PATH):; do
        case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
        esac
        [ -r "$p" ] && exec printf '%sn' "$p"
        done
        exit 1
        ) && progname=$(readlink -e -- "$progname") ||
        progname=unknown

        [ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
        progname=unknown


        But again I don't think it's worth the effort.






        share|improve this answer















        In the most common cases, $0 will contain a path, absolute or relative to the script, so



        script_path=$(readlink -e -- "$0")


        (assuming there's a readlink command and it supports -e) generally is a good enough way to obtain the canonical absolute path to the script.



        $0 is assigned from the argument specifying the script as passed to the interpreter.



        For example, in:



        the-shell -shell-options the/script its args


        $0 gets the/script.



        When you run:



        the/script its args


        Your shell will do a:



        exec("the/script", ["the/script", "its", "args"])


        If the script contains a #! /bin/sh - she-bang for instance, the system will transform that to:



        exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])


        (if it doesn't contain a she-bang, or more generally if the system returns a ENOEXEC error, then its your shell that will do the same thing)



        There's an exception for setuid/setgid scripts on some systems, where the system will open the script on some fd x and run instead:



        exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])


        to avoid race conditions (in which case $0 will contain /dev/fd/x).



        Now, you may argue that /dev/fd/x is a path to that script. Note however that if you read from $0, you'll break the script as you consume the input.



        Now, there's a difference if the script command name as invoked doesn't contain a slash. In:



        the-script its args


        Your shell will look up the-script in $PATH. $PATH may contain absolute or relative (including the empty string) paths to some directories. For instance, if $PATH contains /bin:/usr/bin: and the-script is found in the current directory, the shell will do a:



        exec("the-script", ["the-script", "its", "args"])


        which will become:



        exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]


        Or if it's found in /usr/bin:



        exec("/usr/bin/the-script", ["the-script", "its", "args"])
        exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
        "-", "/usr/bin/the-script", "its", "args")


        In all those cases above except the setuid corner case, $0 will contain a path (absolute or relative) to the script.



        Now, a script can also be called as:



        the-interpreter the-script its args


        When the-script as above doesn't contain slash characters, the behaviour varies slightly from shell to shell.



        Old AT&T ksh implementations were actually looking up the-script unconditionally in $PATH (which was actually a bug and a security hole for setuid scripts), so $0 actually did not contain a path to the script unless the $PATH lookup actually happened to find the-script in the current directory.



        Newer AT&T ksh would try and interpret the-script in the current directory if it's readable. If not it would lookup for a readable and executable the-script in $PATH.



        For bash, it checks if the-script is in the current directory (and is not a broken symlink) and if not, lookup for a readable (not necessarily executable) the-script in $PATH.



        zsh in sh emulation would do like bash except that if the-script is a broken symlink in the current directory, it would not search for a the-script in $PATH and would instead report an error.



        All the other Bourne-like shells don't look the-script up in $PATH.



        For all those shells anyway, if you find that $0 doesn't contain a / and is not readable, then it probably has been looked up in $PATH. Then, as files in $PATH are likely to be executable, it's probably a safe approximation to use command -v -- "$0" to find its path (though that wouldn't work if $0 happens to also be the name of a shell builtin or keyword (in most shells)).



        So if you really want to cover for that case, you could write it:



        progname=$0
        [ -r "$progname" ] || progname=$(
        IFS=:; set -f
        for i in $PATH-$(getconf PATH)""; do
        case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
        esac
        [ -r "$p" ] && exec printf '%sn' "$p"
        done
        exit 1
        ) && progname=$(readlink -e -- "$progname") ||
        progname=unknown


        (the "" appended to $PATH is to preserve a trailing empty element with shells whose $IFS acts as delimiter instead of separator).



        Now, there are more esoteric ways to invoke a script. One could do:



        the-shell < the-script


        Or:



        cat the-script | the-shell


        In that case, $0 will be the first argument (argv[0]) that the interpreter received (above the-shell, but that could be anything though generally either the basename or one path to that interpreter).



        Detecting that you're in that situation based on the value of $0 is not reliable. You could look at the output of ps -o args= -p "$$" to get a clue. In the pipe case, there's no real way you can get back to a path to the script.



        One could also do:



        the-shell -c '. the-script' blah blih


        Then, except in zsh (and some old implementation of the Bourne shell), $0 would be blah. Again, hard to get to the path of the script in those shells.



        Or:



        the-shell -c "$(cat the-script)" blah blih


        etc.



        To make sure you have the right $progname, you could search for a specific string in it like:



        progname=$0
        [ -r "$progname" ] || progname=$(
        IFS=:; set -f
        for i in $PATH-$(getconf PATH):; do
        case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
        esac
        [ -r "$p" ] && exec printf '%sn' "$p"
        done
        exit 1
        ) && progname=$(readlink -e -- "$progname") ||
        progname=unknown

        [ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
        progname=unknown


        But again I don't think it's worth the effort.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jan 6 at 8:48

























        answered Mar 16 '14 at 20:38









        Stéphane ChazelasStéphane Chazelas

        301k55565917




        301k55565917












        • Stéphane, I don't understand your use of "-" in the above examples. In my experience, exec("the-script", ["the-script", "its", "args"]) becomes exec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"]), with of course the possibility of an interpreter option.

          – jrw32982
          Aug 3 '16 at 4:28












        • @jrw32982, #! /bin/sh - is the "always use cmd -- something if you can't guarantee that something won't start with -" good practice adage here applied to /bin/sh (where - as the end-of-option marker is more portable than --) with something being the path/name of the script. If you don't use that for setuid scripts (on systems that support them but not with the /dev/fd/x method mentioned in the answer), then one can get a root shell by creating a symlink to your script called -i or -s for instance.

          – Stéphane Chazelas
          Aug 3 '16 at 9:00












        • Thanks, Stéphane. I had missed the trailing single hyphen in your example shebang line. I had to hunt for where it's documented that a single hyphen is equivalent to a double hyphen pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html.

          – jrw32982
          Aug 3 '16 at 20:24












        • It's too easy to forget the trailing single/double hyphen in the shebang line for a setuid script; somehow the system should take care of it for you. Disallow setuid scripts altogether or somehow /bin/sh should disable its own option processing if it can detect that it's running setuid. I don't see how using /dev/fd/x by itself fixes that. You still need the single/double hyphen, I think.

          – jrw32982
          Aug 3 '16 at 20:26











        • @jrw32982, /dev/fd/x starts with /, not -. The primary goal is to remove the race condition between the two execve()s though (in between the execve("the-script") which elevates privileges and the subsequent execve("interpreter", "thescript") where interpreter opens thescript later (which may very well have been replaces with a symlink to something else in the meantime). Systems that implement suid scripts properly do a execve("interpreter", "/dev/fd/n") instead where n has been opened as part of the first execve().

          – Stéphane Chazelas
          Aug 4 '16 at 6:41

















        • Stéphane, I don't understand your use of "-" in the above examples. In my experience, exec("the-script", ["the-script", "its", "args"]) becomes exec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"]), with of course the possibility of an interpreter option.

          – jrw32982
          Aug 3 '16 at 4:28












        • @jrw32982, #! /bin/sh - is the "always use cmd -- something if you can't guarantee that something won't start with -" good practice adage here applied to /bin/sh (where - as the end-of-option marker is more portable than --) with something being the path/name of the script. If you don't use that for setuid scripts (on systems that support them but not with the /dev/fd/x method mentioned in the answer), then one can get a root shell by creating a symlink to your script called -i or -s for instance.

          – Stéphane Chazelas
          Aug 3 '16 at 9:00












        • Thanks, Stéphane. I had missed the trailing single hyphen in your example shebang line. I had to hunt for where it's documented that a single hyphen is equivalent to a double hyphen pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html.

          – jrw32982
          Aug 3 '16 at 20:24












        • It's too easy to forget the trailing single/double hyphen in the shebang line for a setuid script; somehow the system should take care of it for you. Disallow setuid scripts altogether or somehow /bin/sh should disable its own option processing if it can detect that it's running setuid. I don't see how using /dev/fd/x by itself fixes that. You still need the single/double hyphen, I think.

          – jrw32982
          Aug 3 '16 at 20:26











        • @jrw32982, /dev/fd/x starts with /, not -. The primary goal is to remove the race condition between the two execve()s though (in between the execve("the-script") which elevates privileges and the subsequent execve("interpreter", "thescript") where interpreter opens thescript later (which may very well have been replaces with a symlink to something else in the meantime). Systems that implement suid scripts properly do a execve("interpreter", "/dev/fd/n") instead where n has been opened as part of the first execve().

          – Stéphane Chazelas
          Aug 4 '16 at 6:41
















        Stéphane, I don't understand your use of "-" in the above examples. In my experience, exec("the-script", ["the-script", "its", "args"]) becomes exec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"]), with of course the possibility of an interpreter option.

        – jrw32982
        Aug 3 '16 at 4:28






        Stéphane, I don't understand your use of "-" in the above examples. In my experience, exec("the-script", ["the-script", "its", "args"]) becomes exec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"]), with of course the possibility of an interpreter option.

        – jrw32982
        Aug 3 '16 at 4:28














        @jrw32982, #! /bin/sh - is the "always use cmd -- something if you can't guarantee that something won't start with -" good practice adage here applied to /bin/sh (where - as the end-of-option marker is more portable than --) with something being the path/name of the script. If you don't use that for setuid scripts (on systems that support them but not with the /dev/fd/x method mentioned in the answer), then one can get a root shell by creating a symlink to your script called -i or -s for instance.

        – Stéphane Chazelas
        Aug 3 '16 at 9:00






        @jrw32982, #! /bin/sh - is the "always use cmd -- something if you can't guarantee that something won't start with -" good practice adage here applied to /bin/sh (where - as the end-of-option marker is more portable than --) with something being the path/name of the script. If you don't use that for setuid scripts (on systems that support them but not with the /dev/fd/x method mentioned in the answer), then one can get a root shell by creating a symlink to your script called -i or -s for instance.

        – Stéphane Chazelas
        Aug 3 '16 at 9:00














        Thanks, Stéphane. I had missed the trailing single hyphen in your example shebang line. I had to hunt for where it's documented that a single hyphen is equivalent to a double hyphen pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html.

        – jrw32982
        Aug 3 '16 at 20:24






        Thanks, Stéphane. I had missed the trailing single hyphen in your example shebang line. I had to hunt for where it's documented that a single hyphen is equivalent to a double hyphen pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html.

        – jrw32982
        Aug 3 '16 at 20:24














        It's too easy to forget the trailing single/double hyphen in the shebang line for a setuid script; somehow the system should take care of it for you. Disallow setuid scripts altogether or somehow /bin/sh should disable its own option processing if it can detect that it's running setuid. I don't see how using /dev/fd/x by itself fixes that. You still need the single/double hyphen, I think.

        – jrw32982
        Aug 3 '16 at 20:26





        It's too easy to forget the trailing single/double hyphen in the shebang line for a setuid script; somehow the system should take care of it for you. Disallow setuid scripts altogether or somehow /bin/sh should disable its own option processing if it can detect that it's running setuid. I don't see how using /dev/fd/x by itself fixes that. You still need the single/double hyphen, I think.

        – jrw32982
        Aug 3 '16 at 20:26













        @jrw32982, /dev/fd/x starts with /, not -. The primary goal is to remove the race condition between the two execve()s though (in between the execve("the-script") which elevates privileges and the subsequent execve("interpreter", "thescript") where interpreter opens thescript later (which may very well have been replaces with a symlink to something else in the meantime). Systems that implement suid scripts properly do a execve("interpreter", "/dev/fd/n") instead where n has been opened as part of the first execve().

        – Stéphane Chazelas
        Aug 4 '16 at 6:41





        @jrw32982, /dev/fd/x starts with /, not -. The primary goal is to remove the race condition between the two execve()s though (in between the execve("the-script") which elevates privileges and the subsequent execve("interpreter", "thescript") where interpreter opens thescript later (which may very well have been replaces with a symlink to something else in the meantime). Systems that implement suid scripts properly do a execve("interpreter", "/dev/fd/n") instead where n has been opened as part of the first execve().

        – Stéphane Chazelas
        Aug 4 '16 at 6:41













        6














        Here are two situations where the directory would not be included:



        > bash scriptname
        scriptname

        > bash <scriptname
        bash


        In both cases, the current directory would have to be the directory where scriptname was located.



        In the first case, the value of $0 could still be passed to grep since it assumes the FILE argument is relative to the current directory.



        In the second case, if the help and version information is only printed in response to a specific command line option, it shouldn't be a problem. I'm not sure why anyone would invoke a script that way to print the help or version information.



        Caveats



        • If the script changes the current directory, you wouldn't want to use relative paths.


        • If the script is sourced, the value of $0 will usually be the caller script rather than the sourced script.






        share|improve this answer



























          6














          Here are two situations where the directory would not be included:



          > bash scriptname
          scriptname

          > bash <scriptname
          bash


          In both cases, the current directory would have to be the directory where scriptname was located.



          In the first case, the value of $0 could still be passed to grep since it assumes the FILE argument is relative to the current directory.



          In the second case, if the help and version information is only printed in response to a specific command line option, it shouldn't be a problem. I'm not sure why anyone would invoke a script that way to print the help or version information.



          Caveats



          • If the script changes the current directory, you wouldn't want to use relative paths.


          • If the script is sourced, the value of $0 will usually be the caller script rather than the sourced script.






          share|improve this answer

























            6












            6








            6







            Here are two situations where the directory would not be included:



            > bash scriptname
            scriptname

            > bash <scriptname
            bash


            In both cases, the current directory would have to be the directory where scriptname was located.



            In the first case, the value of $0 could still be passed to grep since it assumes the FILE argument is relative to the current directory.



            In the second case, if the help and version information is only printed in response to a specific command line option, it shouldn't be a problem. I'm not sure why anyone would invoke a script that way to print the help or version information.



            Caveats



            • If the script changes the current directory, you wouldn't want to use relative paths.


            • If the script is sourced, the value of $0 will usually be the caller script rather than the sourced script.






            share|improve this answer













            Here are two situations where the directory would not be included:



            > bash scriptname
            scriptname

            > bash <scriptname
            bash


            In both cases, the current directory would have to be the directory where scriptname was located.



            In the first case, the value of $0 could still be passed to grep since it assumes the FILE argument is relative to the current directory.



            In the second case, if the help and version information is only printed in response to a specific command line option, it shouldn't be a problem. I'm not sure why anyone would invoke a script that way to print the help or version information.



            Caveats



            • If the script changes the current directory, you wouldn't want to use relative paths.


            • If the script is sourced, the value of $0 will usually be the caller script rather than the sourced script.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Mar 16 '14 at 8:58









            toxalottoxalot

            5901618




            5901618





















                3














                An arbitrary argument zero can be specified when using the -c option to most (all?) shells. Eg:



                sh -c 'echo $0' argv0


                From man bash (chosen purely because this has a better description than my man sh - usage is the same regardless):




                -c



                If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are
                assigned to the positional parameters, starting with $0.







                share|improve this answer

























                • I think this works because -c 'command' are operands, and argv0 is its first nonoperand commandline argument.

                  – mikeserv
                  Mar 16 '14 at 11:10











                • @mike correct, I have updated with a man snippet.

                  – Graeme
                  Mar 16 '14 at 11:19















                3














                An arbitrary argument zero can be specified when using the -c option to most (all?) shells. Eg:



                sh -c 'echo $0' argv0


                From man bash (chosen purely because this has a better description than my man sh - usage is the same regardless):




                -c



                If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are
                assigned to the positional parameters, starting with $0.







                share|improve this answer

























                • I think this works because -c 'command' are operands, and argv0 is its first nonoperand commandline argument.

                  – mikeserv
                  Mar 16 '14 at 11:10











                • @mike correct, I have updated with a man snippet.

                  – Graeme
                  Mar 16 '14 at 11:19













                3












                3








                3







                An arbitrary argument zero can be specified when using the -c option to most (all?) shells. Eg:



                sh -c 'echo $0' argv0


                From man bash (chosen purely because this has a better description than my man sh - usage is the same regardless):




                -c



                If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are
                assigned to the positional parameters, starting with $0.







                share|improve this answer















                An arbitrary argument zero can be specified when using the -c option to most (all?) shells. Eg:



                sh -c 'echo $0' argv0


                From man bash (chosen purely because this has a better description than my man sh - usage is the same regardless):




                -c



                If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are
                assigned to the positional parameters, starting with $0.








                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Mar 16 '14 at 11:18

























                answered Mar 16 '14 at 11:04









                GraemeGraeme

                25k46497




                25k46497












                • I think this works because -c 'command' are operands, and argv0 is its first nonoperand commandline argument.

                  – mikeserv
                  Mar 16 '14 at 11:10











                • @mike correct, I have updated with a man snippet.

                  – Graeme
                  Mar 16 '14 at 11:19

















                • I think this works because -c 'command' are operands, and argv0 is its first nonoperand commandline argument.

                  – mikeserv
                  Mar 16 '14 at 11:10











                • @mike correct, I have updated with a man snippet.

                  – Graeme
                  Mar 16 '14 at 11:19
















                I think this works because -c 'command' are operands, and argv0 is its first nonoperand commandline argument.

                – mikeserv
                Mar 16 '14 at 11:10





                I think this works because -c 'command' are operands, and argv0 is its first nonoperand commandline argument.

                – mikeserv
                Mar 16 '14 at 11:10













                @mike correct, I have updated with a man snippet.

                – Graeme
                Mar 16 '14 at 11:19





                @mike correct, I have updated with a man snippet.

                – Graeme
                Mar 16 '14 at 11:19











                3














                NOTE: Other's have already explained the mechanics of $0 so I'll skip all that.



                I generally side step this entire issue and simply use the command readlink -f $0. This will always give you back the full path of whatever you give to it as an argument.



                Examples



                Say I'm here to start with:



                $ pwd
                /home/saml/tst/119929/adir


                Make a directory + file:



                $ mkdir adir
                $ touch afile
                $ cd adir/


                Now start showing off readlink:



                $ readlink -f ../adir
                /home/saml/tst/119929/adir

                $ readlink -f ../
                /home/saml/tst/119929

                $ readlink -f ../afile
                /home/saml/tst/119929/afile

                $ readlink -f .
                /home/saml/tst/119929/adir


                Additional trickery



                Now with a consistent result being returned when we interrogate $0 via readlink we can use a simply dirname $(readlink -f $0) to get the absolute path to the script -or- basename $(readlink -f $0) to get the actual name of the script.






                share|improve this answer



























                  3














                  NOTE: Other's have already explained the mechanics of $0 so I'll skip all that.



                  I generally side step this entire issue and simply use the command readlink -f $0. This will always give you back the full path of whatever you give to it as an argument.



                  Examples



                  Say I'm here to start with:



                  $ pwd
                  /home/saml/tst/119929/adir


                  Make a directory + file:



                  $ mkdir adir
                  $ touch afile
                  $ cd adir/


                  Now start showing off readlink:



                  $ readlink -f ../adir
                  /home/saml/tst/119929/adir

                  $ readlink -f ../
                  /home/saml/tst/119929

                  $ readlink -f ../afile
                  /home/saml/tst/119929/afile

                  $ readlink -f .
                  /home/saml/tst/119929/adir


                  Additional trickery



                  Now with a consistent result being returned when we interrogate $0 via readlink we can use a simply dirname $(readlink -f $0) to get the absolute path to the script -or- basename $(readlink -f $0) to get the actual name of the script.






                  share|improve this answer

























                    3












                    3








                    3







                    NOTE: Other's have already explained the mechanics of $0 so I'll skip all that.



                    I generally side step this entire issue and simply use the command readlink -f $0. This will always give you back the full path of whatever you give to it as an argument.



                    Examples



                    Say I'm here to start with:



                    $ pwd
                    /home/saml/tst/119929/adir


                    Make a directory + file:



                    $ mkdir adir
                    $ touch afile
                    $ cd adir/


                    Now start showing off readlink:



                    $ readlink -f ../adir
                    /home/saml/tst/119929/adir

                    $ readlink -f ../
                    /home/saml/tst/119929

                    $ readlink -f ../afile
                    /home/saml/tst/119929/afile

                    $ readlink -f .
                    /home/saml/tst/119929/adir


                    Additional trickery



                    Now with a consistent result being returned when we interrogate $0 via readlink we can use a simply dirname $(readlink -f $0) to get the absolute path to the script -or- basename $(readlink -f $0) to get the actual name of the script.






                    share|improve this answer













                    NOTE: Other's have already explained the mechanics of $0 so I'll skip all that.



                    I generally side step this entire issue and simply use the command readlink -f $0. This will always give you back the full path of whatever you give to it as an argument.



                    Examples



                    Say I'm here to start with:



                    $ pwd
                    /home/saml/tst/119929/adir


                    Make a directory + file:



                    $ mkdir adir
                    $ touch afile
                    $ cd adir/


                    Now start showing off readlink:



                    $ readlink -f ../adir
                    /home/saml/tst/119929/adir

                    $ readlink -f ../
                    /home/saml/tst/119929

                    $ readlink -f ../afile
                    /home/saml/tst/119929/afile

                    $ readlink -f .
                    /home/saml/tst/119929/adir


                    Additional trickery



                    Now with a consistent result being returned when we interrogate $0 via readlink we can use a simply dirname $(readlink -f $0) to get the absolute path to the script -or- basename $(readlink -f $0) to get the actual name of the script.







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Mar 16 '14 at 14:06









                    slmslm

                    248k66519679




                    248k66519679





















                        0














                        My man page says:





                        $0 : expands to the name of shell or shell script.





                        It seems this translates to argv[0] of the current shell - or the first nonoperand commandline argument the currently interpreting shell is fed when invoked. I previously stated that sh ./somescript would path its $0 $ENV variable to sh but this was incorrect because the shell is a new process of its own and invoked with a new $ENV.



                        In this way sh ./somescript.sh differs from . ./somescript.sh which runs in the current environment and $0 is already set.



                        You can check this by comparing $0 to /proc/$$/status.



                        echo 'script="/proc/$$/status"
                        echo $0
                        cat "$script"'
                        > ./script.sh
                        sh ./script.sh ; . ./script.sh


                        Thanks for the correction, @toxalot. I learned something.






                        share|improve this answer

























                        • On my system, if it's sourced (. ./myscript.sh or source ./myscript.sh), then $0 is the shell. But if it's passed as an argument to the shell (sh ./myscript.sh), then $0 is the path to the script. Of course, sh on my system is Bash. So I don't know if that makes a difference or not.

                          – toxalot
                          Mar 16 '14 at 10:32












                        • Does it have a hashbang? I think that the difference matters - and I can add that - without it sh should not exec it and instead source it, but with it, it should exec it.

                          – mikeserv
                          Mar 16 '14 at 10:34











                        • With or without hashbang, I get same results. With or without being executable, I get same results.

                          – toxalot
                          Mar 16 '14 at 10:37











                        • Me too! I think it's because it's a shell builtin. I'm checking...

                          – mikeserv
                          Mar 16 '14 at 10:38











                        • Bang lines are interpreted by the kernel. If you exec ./somescript with the bangline #!/bin/sh, it would be equivalent to running /bin/sh ./somescript. Otherwise they make no difference to the shell.

                          – Graeme
                          Mar 16 '14 at 11:36















                        0














                        My man page says:





                        $0 : expands to the name of shell or shell script.





                        It seems this translates to argv[0] of the current shell - or the first nonoperand commandline argument the currently interpreting shell is fed when invoked. I previously stated that sh ./somescript would path its $0 $ENV variable to sh but this was incorrect because the shell is a new process of its own and invoked with a new $ENV.



                        In this way sh ./somescript.sh differs from . ./somescript.sh which runs in the current environment and $0 is already set.



                        You can check this by comparing $0 to /proc/$$/status.



                        echo 'script="/proc/$$/status"
                        echo $0
                        cat "$script"'
                        > ./script.sh
                        sh ./script.sh ; . ./script.sh


                        Thanks for the correction, @toxalot. I learned something.






                        share|improve this answer

























                        • On my system, if it's sourced (. ./myscript.sh or source ./myscript.sh), then $0 is the shell. But if it's passed as an argument to the shell (sh ./myscript.sh), then $0 is the path to the script. Of course, sh on my system is Bash. So I don't know if that makes a difference or not.

                          – toxalot
                          Mar 16 '14 at 10:32












                        • Does it have a hashbang? I think that the difference matters - and I can add that - without it sh should not exec it and instead source it, but with it, it should exec it.

                          – mikeserv
                          Mar 16 '14 at 10:34











                        • With or without hashbang, I get same results. With or without being executable, I get same results.

                          – toxalot
                          Mar 16 '14 at 10:37











                        • Me too! I think it's because it's a shell builtin. I'm checking...

                          – mikeserv
                          Mar 16 '14 at 10:38











                        • Bang lines are interpreted by the kernel. If you exec ./somescript with the bangline #!/bin/sh, it would be equivalent to running /bin/sh ./somescript. Otherwise they make no difference to the shell.

                          – Graeme
                          Mar 16 '14 at 11:36













                        0












                        0








                        0







                        My man page says:





                        $0 : expands to the name of shell or shell script.





                        It seems this translates to argv[0] of the current shell - or the first nonoperand commandline argument the currently interpreting shell is fed when invoked. I previously stated that sh ./somescript would path its $0 $ENV variable to sh but this was incorrect because the shell is a new process of its own and invoked with a new $ENV.



                        In this way sh ./somescript.sh differs from . ./somescript.sh which runs in the current environment and $0 is already set.



                        You can check this by comparing $0 to /proc/$$/status.



                        echo 'script="/proc/$$/status"
                        echo $0
                        cat "$script"'
                        > ./script.sh
                        sh ./script.sh ; . ./script.sh


                        Thanks for the correction, @toxalot. I learned something.






                        share|improve this answer















                        My man page says:





                        $0 : expands to the name of shell or shell script.





                        It seems this translates to argv[0] of the current shell - or the first nonoperand commandline argument the currently interpreting shell is fed when invoked. I previously stated that sh ./somescript would path its $0 $ENV variable to sh but this was incorrect because the shell is a new process of its own and invoked with a new $ENV.



                        In this way sh ./somescript.sh differs from . ./somescript.sh which runs in the current environment and $0 is already set.



                        You can check this by comparing $0 to /proc/$$/status.



                        echo 'script="/proc/$$/status"
                        echo $0
                        cat "$script"'
                        > ./script.sh
                        sh ./script.sh ; . ./script.sh


                        Thanks for the correction, @toxalot. I learned something.







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Mar 16 '14 at 11:12

























                        answered Mar 16 '14 at 10:13









                        mikeservmikeserv

                        45.5k668154




                        45.5k668154












                        • On my system, if it's sourced (. ./myscript.sh or source ./myscript.sh), then $0 is the shell. But if it's passed as an argument to the shell (sh ./myscript.sh), then $0 is the path to the script. Of course, sh on my system is Bash. So I don't know if that makes a difference or not.

                          – toxalot
                          Mar 16 '14 at 10:32












                        • Does it have a hashbang? I think that the difference matters - and I can add that - without it sh should not exec it and instead source it, but with it, it should exec it.

                          – mikeserv
                          Mar 16 '14 at 10:34











                        • With or without hashbang, I get same results. With or without being executable, I get same results.

                          – toxalot
                          Mar 16 '14 at 10:37











                        • Me too! I think it's because it's a shell builtin. I'm checking...

                          – mikeserv
                          Mar 16 '14 at 10:38











                        • Bang lines are interpreted by the kernel. If you exec ./somescript with the bangline #!/bin/sh, it would be equivalent to running /bin/sh ./somescript. Otherwise they make no difference to the shell.

                          – Graeme
                          Mar 16 '14 at 11:36

















                        • On my system, if it's sourced (. ./myscript.sh or source ./myscript.sh), then $0 is the shell. But if it's passed as an argument to the shell (sh ./myscript.sh), then $0 is the path to the script. Of course, sh on my system is Bash. So I don't know if that makes a difference or not.

                          – toxalot
                          Mar 16 '14 at 10:32












                        • Does it have a hashbang? I think that the difference matters - and I can add that - without it sh should not exec it and instead source it, but with it, it should exec it.

                          – mikeserv
                          Mar 16 '14 at 10:34











                        • With or without hashbang, I get same results. With or without being executable, I get same results.

                          – toxalot
                          Mar 16 '14 at 10:37











                        • Me too! I think it's because it's a shell builtin. I'm checking...

                          – mikeserv
                          Mar 16 '14 at 10:38











                        • Bang lines are interpreted by the kernel. If you exec ./somescript with the bangline #!/bin/sh, it would be equivalent to running /bin/sh ./somescript. Otherwise they make no difference to the shell.

                          – Graeme
                          Mar 16 '14 at 11:36
















                        On my system, if it's sourced (. ./myscript.sh or source ./myscript.sh), then $0 is the shell. But if it's passed as an argument to the shell (sh ./myscript.sh), then $0 is the path to the script. Of course, sh on my system is Bash. So I don't know if that makes a difference or not.

                        – toxalot
                        Mar 16 '14 at 10:32






                        On my system, if it's sourced (. ./myscript.sh or source ./myscript.sh), then $0 is the shell. But if it's passed as an argument to the shell (sh ./myscript.sh), then $0 is the path to the script. Of course, sh on my system is Bash. So I don't know if that makes a difference or not.

                        – toxalot
                        Mar 16 '14 at 10:32














                        Does it have a hashbang? I think that the difference matters - and I can add that - without it sh should not exec it and instead source it, but with it, it should exec it.

                        – mikeserv
                        Mar 16 '14 at 10:34





                        Does it have a hashbang? I think that the difference matters - and I can add that - without it sh should not exec it and instead source it, but with it, it should exec it.

                        – mikeserv
                        Mar 16 '14 at 10:34













                        With or without hashbang, I get same results. With or without being executable, I get same results.

                        – toxalot
                        Mar 16 '14 at 10:37





                        With or without hashbang, I get same results. With or without being executable, I get same results.

                        – toxalot
                        Mar 16 '14 at 10:37













                        Me too! I think it's because it's a shell builtin. I'm checking...

                        – mikeserv
                        Mar 16 '14 at 10:38





                        Me too! I think it's because it's a shell builtin. I'm checking...

                        – mikeserv
                        Mar 16 '14 at 10:38













                        Bang lines are interpreted by the kernel. If you exec ./somescript with the bangline #!/bin/sh, it would be equivalent to running /bin/sh ./somescript. Otherwise they make no difference to the shell.

                        – Graeme
                        Mar 16 '14 at 11:36





                        Bang lines are interpreted by the kernel. If you exec ./somescript with the bangline #!/bin/sh, it would be equivalent to running /bin/sh ./somescript. Otherwise they make no difference to the shell.

                        – Graeme
                        Mar 16 '14 at 11:36











                        0















                        I want to grep the current script so I can print out help and version information from the comments section at the top.




                        While $0 contains the script name it may contain a prefixed path based on the way the script is called, I have always used $0##*/ to print the script name in the help output which removes any leading path from $0.



                        Taken from Advanced Bash Scripting guide - Section 10.2 parameter substitution




                        $var#Pattern



                        Remove from $var the shortest part of $Pattern that matches the front end of $var.



                        $var##Pattern



                        Remove from $var the longest part of $Pattern that matches the front end of $var.




                        So the longest part of $0 that matches */ will be the entire path prefix, returning only the script name.






                        share|improve this answer

























                        • Yes, I do that too for the name of the script that is used within the help message. But I'm talking about grepping the script so I need to have at least a relative path. I want to put the help message at the top in the comments and not have to repeat the help message later when printing it.

                          – toxalot
                          Mar 16 '14 at 13:32















                        0















                        I want to grep the current script so I can print out help and version information from the comments section at the top.




                        While $0 contains the script name it may contain a prefixed path based on the way the script is called, I have always used $0##*/ to print the script name in the help output which removes any leading path from $0.



                        Taken from Advanced Bash Scripting guide - Section 10.2 parameter substitution




                        $var#Pattern



                        Remove from $var the shortest part of $Pattern that matches the front end of $var.



                        $var##Pattern



                        Remove from $var the longest part of $Pattern that matches the front end of $var.




                        So the longest part of $0 that matches */ will be the entire path prefix, returning only the script name.






                        share|improve this answer

























                        • Yes, I do that too for the name of the script that is used within the help message. But I'm talking about grepping the script so I need to have at least a relative path. I want to put the help message at the top in the comments and not have to repeat the help message later when printing it.

                          – toxalot
                          Mar 16 '14 at 13:32













                        0












                        0








                        0








                        I want to grep the current script so I can print out help and version information from the comments section at the top.




                        While $0 contains the script name it may contain a prefixed path based on the way the script is called, I have always used $0##*/ to print the script name in the help output which removes any leading path from $0.



                        Taken from Advanced Bash Scripting guide - Section 10.2 parameter substitution




                        $var#Pattern



                        Remove from $var the shortest part of $Pattern that matches the front end of $var.



                        $var##Pattern



                        Remove from $var the longest part of $Pattern that matches the front end of $var.




                        So the longest part of $0 that matches */ will be the entire path prefix, returning only the script name.






                        share|improve this answer
















                        I want to grep the current script so I can print out help and version information from the comments section at the top.




                        While $0 contains the script name it may contain a prefixed path based on the way the script is called, I have always used $0##*/ to print the script name in the help output which removes any leading path from $0.



                        Taken from Advanced Bash Scripting guide - Section 10.2 parameter substitution




                        $var#Pattern



                        Remove from $var the shortest part of $Pattern that matches the front end of $var.



                        $var##Pattern



                        Remove from $var the longest part of $Pattern that matches the front end of $var.




                        So the longest part of $0 that matches */ will be the entire path prefix, returning only the script name.







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Mar 16 '14 at 13:45









                        toxalot

                        5901618




                        5901618










                        answered Mar 16 '14 at 13:21









                        samblersambler

                        31014




                        31014












                        • Yes, I do that too for the name of the script that is used within the help message. But I'm talking about grepping the script so I need to have at least a relative path. I want to put the help message at the top in the comments and not have to repeat the help message later when printing it.

                          – toxalot
                          Mar 16 '14 at 13:32

















                        • Yes, I do that too for the name of the script that is used within the help message. But I'm talking about grepping the script so I need to have at least a relative path. I want to put the help message at the top in the comments and not have to repeat the help message later when printing it.

                          – toxalot
                          Mar 16 '14 at 13:32
















                        Yes, I do that too for the name of the script that is used within the help message. But I'm talking about grepping the script so I need to have at least a relative path. I want to put the help message at the top in the comments and not have to repeat the help message later when printing it.

                        – toxalot
                        Mar 16 '14 at 13:32





                        Yes, I do that too for the name of the script that is used within the help message. But I'm talking about grepping the script so I need to have at least a relative path. I want to put the help message at the top in the comments and not have to repeat the help message later when printing it.

                        – toxalot
                        Mar 16 '14 at 13:32











                        0














                        For something similar I use:



                        rPath="$(dirname $(realpath $0))"
                        echo $rPath

                        rPath=$(dirname $(readlink -e -- "$0"))
                        echo $rPath


                        rPath always has the same value.






                        share|improve this answer





























                          0














                          For something similar I use:



                          rPath="$(dirname $(realpath $0))"
                          echo $rPath

                          rPath=$(dirname $(readlink -e -- "$0"))
                          echo $rPath


                          rPath always has the same value.






                          share|improve this answer



























                            0












                            0








                            0







                            For something similar I use:



                            rPath="$(dirname $(realpath $0))"
                            echo $rPath

                            rPath=$(dirname $(readlink -e -- "$0"))
                            echo $rPath


                            rPath always has the same value.






                            share|improve this answer















                            For something similar I use:



                            rPath="$(dirname $(realpath $0))"
                            echo $rPath

                            rPath=$(dirname $(readlink -e -- "$0"))
                            echo $rPath


                            rPath always has the same value.







                            share|improve this answer














                            share|improve this answer



                            share|improve this answer








                            edited Oct 4 '15 at 6:24

























                            answered Sep 27 '15 at 2:05









                            AnonimousAnonimous

                            11




                            11



























                                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%2f119929%2fwill-0-always-include-the-path-to-the-script%23new-answer', 'question_page');

                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown






                                Popular posts from this blog

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

                                Displaying single band from multi-band raster using QGIS

                                How many registers does an x86_64 CPU actually have?