I am writing a shell script with a few variables that should be configured by the user. There will be an installer for downloading and configuring the script, possibly by asking a series of question. The script in question is aimed at other developers.
This can be implemented in a number of ways:
-
Use placeholders in the script itself and use
sed
to replace them during installation (something like this: https://stackoverflow.com/questions/415677/how-to-replace-placeholders-in-a-text-file)-
Pros: All variable definitions are contained within the script. It’s easy to download the script manually and configure the variables for users who prefer an editor over the installer.
-
Cons: It’s hard to reconfigure the variables through the installer once they are in place. Unless I create a more complex regexp which would be prone to errors.
-
-
Use a config file, basically another shell script with assignments, and use
source
to include it. (And probably place it in~/.scriptname
? The main script is copied to/usr/local/bin
)-
Pros: It’s easy to reconfigure the script. Could even add a parameter for doing so from the main script (Would probably work in the first solution as well, but editing a script from itself doesn’t sound like a very good idea)
-
Cons: The script is now dependent on two files and the user is required to run the installer for the configuration file to be created. This can be solved by auto generating a config file if none exists. But locating an external configuration file will still be more cumbersome for users who just want to download the script, edit it, and be done with it.
-
Also, a few options regarding how the configuration should be managed by the user after the installation:
-
Git like
$ myscript config server.host example.org
$ myscript config server.proxypath /home/johndoe/proxy
$ myscript config server.httppath /home/johndoe/web -
Interactive
$ myscript config
Enter the server hostname: example.org
Enter the path to the proxy on the server: /home/johndoe/proxy
Enter the path to the http directory on the server: /home/johndoe/web -
getopts with long options
$ myscript –host example.org –proxypath /home/johndoe/proxy –httppath /home/johndoe/web -
Simple
$ myscript config example.org /home/johndoe/proxy /home/johndoe/web
Are there any other ways of doing this that you would consider?
Any best practices, anything elegant?
3
What would I expect from a sane program (a shell script or not):
- I never ever have to alter the executable to just configure it. This is not an OS kernel.
- I can pass any setting using command line. This is a must for every piece of information that does not have a reasonable default. The only exception is a password which needs interactive input.
- Optionally, I can pass a setting using an environment variable.
- I can write the settings down into a config file, and this file will be used if it is present under a well-known name, or explicitly pointed to using the two methods above.
- The config file uses the very same setting names and syntax as command line.
3
When I need to write an elaborate script with various configuration options I use Python with the argparse and ConfigParser libraries. Those help with implementation but the process applies to any shell script:
- Look for a config file. If one exists, read any settings it has into a dictionary / lookup table.
- Parse named command line arguments. For each argument provided, override the value loaded from the config file if it exists. For any argument not passed in the command line and not in the config, use a default.
- Execute the script’s main function
- If the config file doesn’t exist, write the passed and/or default values to it.
My preference is for a config file to hold the preferred options when the script is going to be used repeatedly, but let any command line arguments override. Write the config file using those parameters the first time it’s run. The config file can then be shared and committed to a code repository.
In my most recent case, I also wrote the defaults to the [DEFAULT]
section at the top of the config file, then had a section for each “environment” with appropriate overrides for each. The “environment” is the first unnamed parameter to the script. So in this case parameters are chosen as built-in default -> config file default -> config file section value -> command-line parameter. An additional command line parameter gives the option to overwrite the existing config with the latest run’s value. This config file is written into the current directory so it applies per project and can be committed with the rest of the code. Anyone else checking out the same project will then start with the same configuration.
2
Editing placeholders is error prone.
I would go with using a Config file.
Your concern about dependency is valid, however, I don’t remember using too many tools that is composed of a single file. So theoretically you are correct, but practically it should be quite OK.
A third option is to make the configuration software write a new tailored version that is specific to the selected options and parameters. This may be harder to write and test of course 🙂