Will $0 always include the path to the script?
Clash Royale CLAN TAG#URR8PPP
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
add a comment |
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
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
add a comment |
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
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
shell-script path
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
add a comment |
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
add a comment |
7 Answers
7
active
oldest
votes
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.
Stéphane, I don't understand your use of"-"
in the above examples. In my experience,exec("the-script", ["the-script", "its", "args"])
becomesexec("/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 usecmd -- something
if you can't guarantee thatsomething
won't start with-
" good practice adage here applied to/bin/sh
(where-
as the end-of-option marker is more portable than--
) withsomething
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 twoexecve()
s though (in between theexecve("the-script")
which elevates privileges and the subsequentexecve("interpreter", "thescript")
whereinterpreter
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 aexecve("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
|
show 3 more comments
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.
add a comment |
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.
I think this works because -c 'command' are operands, andargv0
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
add a comment |
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.
add a comment |
My man
page says:
$0
: expands to thename
ofshell
orshell 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.
On my system, if it's sourced (. ./myscript.sh
orsource ./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 notexec
it and instead source it, but with it, it shouldexec
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
|
show 2 more comments
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.
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
add a comment |
For something similar I use:
rPath="$(dirname $(realpath $0))"
echo $rPath
rPath=$(dirname $(readlink -e -- "$0"))
echo $rPath
rPath
always has the same value.
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Stéphane, I don't understand your use of"-"
in the above examples. In my experience,exec("the-script", ["the-script", "its", "args"])
becomesexec("/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 usecmd -- something
if you can't guarantee thatsomething
won't start with-
" good practice adage here applied to/bin/sh
(where-
as the end-of-option marker is more portable than--
) withsomething
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 twoexecve()
s though (in between theexecve("the-script")
which elevates privileges and the subsequentexecve("interpreter", "thescript")
whereinterpreter
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 aexecve("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
|
show 3 more comments
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.
Stéphane, I don't understand your use of"-"
in the above examples. In my experience,exec("the-script", ["the-script", "its", "args"])
becomesexec("/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 usecmd -- something
if you can't guarantee thatsomething
won't start with-
" good practice adage here applied to/bin/sh
(where-
as the end-of-option marker is more portable than--
) withsomething
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 twoexecve()
s though (in between theexecve("the-script")
which elevates privileges and the subsequentexecve("interpreter", "thescript")
whereinterpreter
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 aexecve("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
|
show 3 more comments
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.
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.
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"])
becomesexec("/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 usecmd -- something
if you can't guarantee thatsomething
won't start with-
" good practice adage here applied to/bin/sh
(where-
as the end-of-option marker is more portable than--
) withsomething
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 twoexecve()
s though (in between theexecve("the-script")
which elevates privileges and the subsequentexecve("interpreter", "thescript")
whereinterpreter
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 aexecve("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
|
show 3 more comments
Stéphane, I don't understand your use of"-"
in the above examples. In my experience,exec("the-script", ["the-script", "its", "args"])
becomesexec("/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 usecmd -- something
if you can't guarantee thatsomething
won't start with-
" good practice adage here applied to/bin/sh
(where-
as the end-of-option marker is more portable than--
) withsomething
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 twoexecve()
s though (in between theexecve("the-script")
which elevates privileges and the subsequentexecve("interpreter", "thescript")
whereinterpreter
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 aexecve("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
|
show 3 more comments
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.
add a comment |
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.
add a comment |
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.
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.
answered Mar 16 '14 at 8:58
toxalottoxalot
5901618
5901618
add a comment |
add a comment |
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.
I think this works because -c 'command' are operands, andargv0
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
add a comment |
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.
I think this works because -c 'command' are operands, andargv0
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
add a comment |
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.
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.
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, andargv0
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
add a comment |
I think this works because -c 'command' are operands, andargv0
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
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Mar 16 '14 at 14:06
slm♦slm
248k66519679
248k66519679
add a comment |
add a comment |
My man
page says:
$0
: expands to thename
ofshell
orshell 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.
On my system, if it's sourced (. ./myscript.sh
orsource ./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 notexec
it and instead source it, but with it, it shouldexec
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
|
show 2 more comments
My man
page says:
$0
: expands to thename
ofshell
orshell 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.
On my system, if it's sourced (. ./myscript.sh
orsource ./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 notexec
it and instead source it, but with it, it shouldexec
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
|
show 2 more comments
My man
page says:
$0
: expands to thename
ofshell
orshell 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.
My man
page says:
$0
: expands to thename
ofshell
orshell 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.
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
orsource ./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 notexec
it and instead source it, but with it, it shouldexec
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
|
show 2 more comments
On my system, if it's sourced (. ./myscript.sh
orsource ./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 notexec
it and instead source it, but with it, it shouldexec
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
|
show 2 more comments
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.
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
add a comment |
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.
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
add a comment |
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.
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.
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
add a comment |
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
add a comment |
For something similar I use:
rPath="$(dirname $(realpath $0))"
echo $rPath
rPath=$(dirname $(readlink -e -- "$0"))
echo $rPath
rPath
always has the same value.
add a comment |
For something similar I use:
rPath="$(dirname $(realpath $0))"
echo $rPath
rPath=$(dirname $(readlink -e -- "$0"))
echo $rPath
rPath
always has the same value.
add a comment |
For something similar I use:
rPath="$(dirname $(realpath $0))"
echo $rPath
rPath=$(dirname $(readlink -e -- "$0"))
echo $rPath
rPath
always has the same value.
For something similar I use:
rPath="$(dirname $(realpath $0))"
echo $rPath
rPath=$(dirname $(readlink -e -- "$0"))
echo $rPath
rPath
always has the same value.
edited Oct 4 '15 at 6:24
answered Sep 27 '15 at 2:05
AnonimousAnonimous
11
11
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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