10

The $0 variable contains the path info of the script.

  • How can I change the path info to absolute path? I mean how to process ~, ., .. or similar?
  • How can I split the path info into directory and file name?

I could use python/perl for this, but I want to use bash if possible.

  • what are you trying to achieve? btw. calling a script with ~/foo.sh will expand ~ into my home directory for $0 with my bash version (GNU bash, version 4.1.5(2)-release-(x86_64-unknown-linux-gnu)) – echox Sep 7 '10 at 21:19
17

You don't need to process things like ~, the shell does it for you. That's why you can pass ~/filename to any script or program and it works -- all those programs don't handle ~ themselves, your shell converts the argument to /home/username/filename and passes that to the program instead:

$ echo ~/filename
/home/mrozekma/filename

If you need a canonical filename (one that doesn't include things like ..), use realpath (thanks Neil):

$ realpath ~/../filename
/home/filename

As for splitting the path into directory name and filename, use dirname and basename:

$ dirname /foo/bar/baz
/foo/bar

$ basename /foo/bar/baz
baz
  • I think you mean realpath, not readlink. readlink won't work in the common situation, ie when the link uses a relative path and you are not in the same dir as the link. – Neil Mayhew Sep 10 '10 at 23:03
  • @Neil realpath is better, but readlink seems to work when I try it; do you know of an example where it fails? – Michael Mrozek Sep 11 '10 at 8:44
  • 1
    cd /tmp; touch foo; ln -s foo bar; cd; readlink /tmp/bar. This returns foo and realpath returns /tmp/foo, which is what you want. – Neil Mayhew Sep 11 '10 at 23:47
  • 1
    @Neil, @Michael: readlink -f (note -f) is almost equivalent to realpath, and it's more portable: readlink -f in GNU coreutils and exists on a few other systems as well; realpath is packaged separately and may not be installed (e.g. only 5 not-that-common packages depend on it in Ubuntu 10.04 or Debian lenny). – Gilles 'SO- stop being evil' Nov 13 '10 at 11:33
  • @Gilles: good point. Thanks for the reminder about realpath -f. – Neil Mayhew Nov 15 '10 at 22:36
5

Using dirname and basename like mentioned by Michael should be the safest way to get what you want.

Anyway if you really want to do this with "bash only tools" you could use parameter substitution:

echo `basename $PWD`        # Basename of current working directory.
echo "${PWD##*/}"           # Basename of current working directory.
echo
echo `basename $0`          # Name of script.
echo $0                     # Name of script.
echo "${0##*/}"             # Name of script.
echo
filename=test.data
echo "${filename##*.}"      # data
                            # Extension of filename.

This example is directly taken from the Advanced Bash Scripting Guide which is worth a look.

The explanation is pretty simple:

${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.

Look at the pattern like some regex and the # or ## as some kind of greedy/non-greedy modifier.

This might become useful if you will have to do some more complicated extractions of a paths part.

  • 5
    Beware when using ${...##...} and friends rather than dirname and basename that they don't work in all cases. For example both ${0##*/} and ${0%/*} expand to the same as $0 if $0 doesn't contain a /. – Gilles 'SO- stop being evil' Sep 7 '10 at 22:11
  • @Gilles I understand why ${0%/*} is not a good alternative to dirname. However, what's wrong with using ${0##*/} instead of basename? – toxalot Mar 16 '14 at 9:24
  • @toxalot For this one, the only problem is when $0 is a directory name with a trailing /. But I regret my advice, because dirname doesn't do any better. – Gilles 'SO- stop being evil' Mar 16 '14 at 14:18
2

realpath is a command which tells you the real path (removes .. and symbolic links etc.)

It is standard with FreeBSD. According to this discussion it's also available for Linux:

http://www.unix.com/shell-programming-scripting/89294-geting-real-path.html

That discussion also offers a bash solution:

$ bash -c "cd /foo/../bar/ ; pwd"
  • 1
    Exactly, something like this usually works: MYDIR="$(cd $(dirname "$0"); pwd)" – Kevin Cantu Sep 8 '10 at 22:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.