1

I write a lot of software tools to simplify tasks. Some of these follow a workflow like this:

  1. In the terminal I type the command to invoke the tool
  2. The tool reads some configuration file from disk either from the current workdir or from some provided path.
  3. The tool operates using the config it found. Part of this config could be paths to other files.

Every time I implement something like this I am wondering about the best answer to the following question: How should the tools interpret relative paths in the settings? The reason is that there are two quite obvious choices for this:

(1) Relative to the current workdir where the terminal is operating from or

(2) relative to the file that contained the settings.

From a developer's standpoint both of these seem to be reasonable choices at first and are easy to implement. However, implementing both of these can become increasingly complicated since, to allow for maximum "freedom" for the user, it would be necessary to allow each path stored to be either interpreted as in (1) or in (2).

What would be the user-friendliest way of implementing this? By "user" I am referring to anyone that might want to use the tool I wrote that is not a developer themself.

Edit

Maybe I should have added some detail. This settings file is not a "global config" for the tool, but rather something local. For example, it could tell the tool how to construct some files in some directory.

In other words, the user/terminal probably was in this directory anyways. The settings file is in some directory and from there, the tool operates. Let's look at an example. Instead of an actual tool, I will take python for this.

The "tool":

import os
from os import path
import sys

cwd = os.getcwd()
# Argument passed is the path to the settings file;
# since it is from the command line, always relative
# to the workdir
arg = sys.argv[1]
settings_file = path.join(cwd, arg) if not path.isabs(arg) else arg
# Let's assume the settings file contains a single line, 
# a path to a file we want to create
with open(settings_file, 'r') as file:
    p = file.readline()
if not path.isabs(p):
    # How to handle this then?
    # Relative to cwd
    p = path.join(cwd, p)
    # vs
    # Relative to the settings file (directory)
    p = path.join(path.dirname(settings_file), p)
#
create_file(p)

2 Answers 2

1

Caveat: I'm a developer, not a designer.

Unless you have a helper tool to help the user manage the config file, then IMO the config file needs to contain unambiguous file paths:

  • use an absolute path
  • some documented location, e.g. $HOME or an XDG location
  • some documented location relative to the location of the tool.
    • e.g. /opt/tools/bin/useful_tool => /opt/tools/conf/useful_tool.cnf

The key is documented.

  1. "Relative to the current workdir where the terminal is operating from" -- I don't see how this can work unless you enforce the user to cd to a particular directory prior to invoking the tool.
2
  • I have added some details to the question Commented Jul 14, 2022 at 9:51
  • 1
    The tool is invoked like tool --conf ./path/to/config and then other files are produced. How often do you expect your users to edit the config file to change the other-file paths? Where should these other-files expect to reside? I dunno, this scenario is too generic for me to really make any conclusions about. Commented Jul 14, 2022 at 13:25
1

We have exactly this problem: a command line tool that takes a path to a config file and acts upon certain files referenced in that file.

Our current approach is to treat relative paths as being relative to the config file. This allows users to place a config file and all associated data in a single directory that can be moved or copied around or accessed via different mount points or symlinks. No files need editing. The user only needs to worry about the path to the config file.

Not all our users work like this but it doesn't invalidate this approach. Some like to keep each config file with its data, as described above, but others prefer to keep all their config files in one directory and all their data files in another directory. Neither of these are served by making the paths relative to the current working directory so taking them relative to the config directory is still the preferred option of the two.

There's nothing preventing users from providing absolute paths to avoid ambiguity. Requiring all paths to be absolute has this advantage but requires the config to be edited in the scenario where data and configuration can be moved around.

Note that if you allow settings from the configuration file to be overridden by command line arguments, you should probably treat the paths provided via the command line as being relative to the working directory. It's more complex to implement but more intuitive for the user and in line with all command line tools that I know of.

Glenn's answer may apply better to your situation if you have a clear and consistent "data directory" to serve as the base path or if data and configuration files are created and never moved.

You could make this behaviour configurable via an extra command line argument, e.g. --user-data-dir /mnt/data_repository or --user-data-dir ./ or --base-path-current or --base-path-config. It might be helpful to also control this behaviour via an environment variable or a centralised config file so that you can easily change according to individual user preferences or evolving requirements.

If you're still in doubt, maybe you could impose a process on the users: data files go here, config files go there, data files are referenced in such and such a way, and then support that process via the software. That would, of course, need to be managed tactfully. Sometimes a clear process being handed down is welcomed but at other times users already have their own ways of working and expect the tooling to follow existing practices (for better or for worse).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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