Aims of this section:
Understand the difference between absolute and relative paths
Understand what the working directory is
Know how to deal with references to other files in simple and not–so–simple cases
A path starting with “/” is called “absolute.” It refers to one particular file in any given context, independent of current working directory. So there can be only one /dev/hostname within the current context. Of course, the particular path may not exist, or the file contents and metadata may change at any time. There are also many ways to change which file an absolute path refers to:
mounting over the path or any ancestor path or
entering a chroot jail, container, VM or other host.
If your script needs to change such a context it is important that you are clear about which code runs within each context. The simplest way to clarify this is usually to put any code you need to run within a different context into a separate script, even if it’s just a single line. That way a context change in your code is represented by a context change in your editor. Another advantage of this approach is that you will never have to escape code within other code, which gets really complicated really fast.
A path starting with any other character is relative to the current working directory. It may be confusing that this is not the directory your script is in! If your shell’s working directory is /home/user and you call
./scripts/pdf2png.bash ./report.pdf the working directory of the script is /home/user. This is handy in most cases, because it means that pdf2png.bash will look for /home/user/report.pdf rather than /home/user/scripts/report.pdf.
Paths starting with “.” are “explicitly relative.” Most of the time adding
./ to the start of a path makes no difference, but they are easier to deal with than the alternative. Consider for example the perfectly valid filename “#tags.txt”. If you pass that as is, unquoted, to a command it will be treated as a comment! Try for example
cat #tags.txt – it will do nothing, because
cat has been called without an argument and its behavior in that case is to print anything you enter on its standard input. You’ll have to press Ctrl–c to cancel it or Ctrl–d to indicate end of file.
cat '#tags.txt' or
cat ./#tags.txt will do what you expect. This gets even more complex if the filename starts with a hyphen – what happens now depends on the argument handling of the application. For example:
Quoting doesn’t help here, because the issue is with the application rather than the shell:
This finally brings us to actually including files. As you can see from the above, referring to absolute paths in both arguments and within the code makes things simpler – the working directory doesn’t matter. But two desirable features mean that we have to deal with relative paths:
We want to be able to relocate the script and its dependencies. Whether the files are in /home/user/scripts or /usr/bin should not affect the functionality.
We want to be able to pass relative paths as arguments for convenience.
readlinkis often used to convert relative paths to absolute ones, but it doesn’t help with this problem: if the relative path isn’t correct,
readlinkisn’t going to find the correct file — classic garbage in, garbage out. It’s just going to make debugging harder by obfuscating the input.
To deal with files located relative to the script we need to know the directory of the script itself. The simplest case should work if you completely control the environment the script runs in, and nobody is doing anything weird or actively trying to break things: