18

Sorry if this is a stupid question, but I searched about it without success.

What does exactly the second line do ?:

#!/bin/sh
cd ${0%/*} || exit 1

I know the first is the shebang, the second tries to change directory but the confusing part is ${0%/*}.

Can you please explain me that second line?

  • 2
    As explained by Andrea in an answer, ${0%/*} translates to the directory path prefixed to the script name while invoking it. An alternative is to use the built-in `dirname` command like `$(dirname $0)` – alwayslearning Jan 07 '18 at 05:45
  • @alwayslearning: What shell and shell version are you using that `dirname` is built in? It certainly isn't in Bash v4.3.11 which is the default shell in Ubuntu Trusty. – David Foerster Jan 07 '18 at 21:15
  • My apologies for the confusion, just checked that `dirname` is not a shell built-in. – alwayslearning Jan 08 '18 at 02:03

1 Answers1

30

${0} is the first argument of the script, i.e. the script name or path. If you launch your script as path/to/script.sh, then ${0} will be exactly that string: path/to/script.sh.

The %/* part modifies the value of ${0}. It means: take all characters until / followed by a file name. In the example above, ${0%/*} will be path/to.

You can see it in action on your shell:

$ x=path/to/script.sh
$ echo "${x%/*}"
path/to

Sh supports many other kinds of "parameter substitution". Here is, for example, how to take the file name instead of the path:

$ echo "${x##*/}"
script.sh

In general, % and %% strip suffixes, while # and ## strip prefixes. You can read more about parameter substitution.

Andrea Corbellini
  • 15,616
  • 2
  • 64
  • 81
  • 2
    This "|| exit 1" seems to be needless: if it will be noway to change dir $? equals 1. – Josef Klimuk Jan 07 '18 at 06:02
  • 3
    It'd be nice to mention the construct is documented as "Remove matching prefix/suffix pattern" in the manual. Also a nice way to remember which is which is that # is shift 3 (*left* of $), % is shift 5 (*right* of $), at least on the US keyboard. – chexum Jan 07 '18 at 08:46
  • 1
    @JosefKlimuk: the `|| exit 1` _might_ be needed because `cd` might exit with status 2, not 1, on error. However I agree that it's not very useful (usually programs don't care about specific exit statuses). Probably this is part of a larger script? – Andrea Corbellini Jan 07 '18 at 10:12
  • 1
    @AndreaCorbellini Surely right. Pretty much the only way it can use useful at all is as part of a larger script. A script which just changes the current directory has no effect, because it only affects the shell running that one script. The parent process never sees it. – hvd Jan 07 '18 at 11:02