I have a bash script that works with relative paths. It needs to have a working directory that is equal to the directory the script is stored in. This works fine as long as I start it from the prompt, because I can cd to the script's directory first. However, when I create a symlink to the script in /etc/cron.hourly, the script breaks.

I need a way to make the bash script change directory to the directory the script is stored in. However, so far I am unsuccessful. Is there an easy way to do this, even if the script is called from cron via a symlink?

This is precisely why a script can not and should not rely on "its location". Bash FAQ 28 has some more explanation of why scripts don't have any such thing as "a location". If you require a specific working directory, you can: (1) hard-code that path in the script itself; (2) read it from a config file at a known location; or (3) pass it in as an argument or environment variable.
–
jw013Oct 9 '12 at 13:59

jw013: The reason I want to do it this way is because the entire directory hierarchy is backed up on multiple systems. So I use relative paths within the script, because it may be located in /home/data in one case or /raid/archive/data in another case.
–
MagnusOct 9 '12 at 14:06

1

@Magnus so use option (2) or (3) - get the root portion (/home or /raid/archive) from a config file or pass it in as an argument. There are other options too - for example, you could put the cd right in your crontab.
–
jw013Oct 9 '12 at 14:13

What kind of ln does what you mentioned in the 3. point? My ln on Linux from the coreutils package not. It displays an error message : “ln: failed to create symbolic link './bar': File exists”. (Where “bar” is symlink to “foo”, either regular file or directory.)
–
manatworkOct 9 '12 at 14:14

I don't recall where I got this, so I can properly attribute, but I've used this for a long time to get the "canonical" path to the script I'm executing. By canonical, I mean absolute path with no "." or ".." in it.

Yes, $0can be problematic, if used in a general situation. I think that using it for ones own work in a particular situation where the set up is clearly defined is fine.

In particular, the script should not use its directory for work anyways, should rely on an environmental variable or a config file, so why care about details like "someone might have moved the script in the time since you have started it" or how ksh will interpret it (given that we know that this is a bash script).

The shell always knows the path to the script under normal circumstances, because it's had to read it. Note that there are circumstances where this is not true, for example with setuid scripts on the OSes that support them (not Linux) — but setuid shell scripts are a bad idea anyway. Most importantly, the script may have been moved between the time the shell opened it and the time you try to do anything with it. So only use the path to the script when there is no security or reliability concern in doing so. A software package with a known structure is a valid use case.

The path to the script that was passed to the shell interpreter is available in the special parameter $0. This may be a relative path, so if you're going to use it, do that before changing to another directory. You can use it if you know that the script won't have been moved in the meantime.

Since $0 may be a symbolic link, you'll need to get its target. There's no POSIX way to do this, but on Linux (GNU coreutils or BusyBox) and recent BSD you can use readlink -f to get a canonical absolute path.

Note that this restricts the ways in which you can structure your package. For example, you can't put the script in an architecture-independent directory tree, and link to it inside every architecture-specific directory tree.