Tjelvar Olsson     About     Posts     Feed     Newsletter

Using relative paths in Linux scripts

In the preivous post I discussed the difference between absolute and relative paths.

So what is better absolute or relative paths? Which one should be used when one needs to refer to a file in a script?

Let me prefix my answer with the caveat that all paths are a pain. However, absolute paths are more of a pain than relative paths. This is because absolute paths make it difficult to restructure the way that directories are organised on your computer. They also make it difficult for you to share your scripts with collaborators because they would need their computer to be structured in exactly the same way as yours. It is possible to get around some of these issues by using relative paths.

This post will show you how create scripts that have a clear separation between raw and derived data using relative paths. As a bonus the scripts will also be more portable and less fragile with respect to reorganisations of your directory structure.

This post makes use of some more advanced Linux skills including the use of environment variables, the creation of a Bash script and adding execute permissions to the script. You don’t need too worry too much about these details if they are new to you. An environment variable is a means to store a piece of information for use later, a Bash script is a text file with commands to run, and execute permissions allows the script to be run by referencing its path. If you would like to learn more about these topics please let me know.

First of all let us recap to get setup to the where the previous post ended. We need two subdirectories raw_data and scripts. The mkdir command below will create these if they do not already exist (the -p flag means that no errors are generated if the directories already exist).

$ mkdir -p raw_data scripts

We also need a file with raw data. The command below creates this file if it does not exist and overwrites it if it already exists.

$ echo "Raw data isn't baked data" > raw_data/raw_data.txt

To illustrate the use of relative paths in scripts create a file named analysis.sh in your scripts directory, i.e. with the relative path ./scripts/analysis.sh, and copy and paste the code below into it.

#!/bin/bash

# Save the current working directory in an environment variable.
INITIAL_WORKING_DIRECTORY=$(pwd)

# This line changes to current working directory to where
# the analysis.sh file is.
cd "$(dirname "$0")"

# Create an environment variable with the relative path to the
# derived data directory.
DERIVED_DATA_DIRECTORY=../derived_data

# Create the derived data  directory if it does not already exist.
mkdir -p $DERIVED_DATA_DIRECTORY

# This code streams the content of the raw data file into the sed
# stream editor. The sed stream editor is used to edit the content
# of the stream. Finally, the output of sed is redirected to a
# derived_data.txt file in the derived data directory.
cat ../raw_data/raw_data.txt  \
        | sed -e "s/Raw/Fudged/"  \
        | sed -e "s/isn't/is/"  \
        > $DERIVED_DATA_DIRECTORY/derived_data.txt

# Go back to where we were before changing into the
# scripts directory.
cd $INITIAL_WORKING_DIRECTORY

The code above works with relative paths. The paths are relative to the scripts directory. That means that the outcome of the script will be independent of the directory one is in when running the script, i.e. no nasty side effects of input files not being found or output files being written to the wrong directory.

To achieve this the script first makes a note of the directory you are currently in and stores it in the INITIAL_WORKING_DIRECTORY environment variable. The script then changes the working directory to be that of the analysis.sh script. At this point the script can start working with paths relative to the scripts directory.

The details of the analysis in this script do not really matter. It creates a directory for derived data (../derived_data) if it does not already exist. It then takes as input the raw data, transforms it before writing it to a file in the derived data directory.

Finally, and importantly, the script changes the working directory back to whatever it was before the script was invoked.

To test the script script ensure that you are not in the scripts directory. In the command below I change into my home directory.

cd /home/olssont

In the above I’m using an absolute path to make it clear which directory I am referring to. Depending on your setup this path may be different. For clarity, I am referring to the directory in which you created the raw_data and scripts directories.

Before we run the script we need to use the chmod command to make it executable.

chmod +x ./scripts/analysis.sh

We can then run the script by calling its path.

./scripts/analysis.sh

This will have created a directory called dervied_data at the same level as the scripts directory.

$ ls
derived_data  raw_data  scripts

Let us also use the cat command to look at the content of the ./derived_data/derived_data.txt file.

$ cat ./derived_data/derived_data.txt
Fudged data is baked data

In a previous post about data management I talked about the need to keep raw data separate from derived data. In this post I have given you some tips on how you can accomplish this. Setting up scripts in the fashion outlined above also has the benefit that it is easier to rename and reorganise directories without your scripts breaking. Furthermore, it will make it easier for you to share your scripts with collaborators.