3

So I've been dying to ask this but didn't want to risk losing reputation over it, however at this point I really need some direct input. I'm a Linux newb, using a Raspberry Pi to set up a LAMP server and I like creating shell files of all my installation commands, but I also like keeping things modular, for example:

#!/bin/sh
#LAMPinstall.sh
./apacheinstall.sh
./mysqlinstall.sh
./phpinstall.sh

I'm used to coding environments where such calls are trivial, but the obvious problem with this script is it requires the current working directory to be the script's directory at the time of the call.

I did a lot of Linux-specific reading on absolute vs relative paths, sourcing vs calling, bash vs shell, and various methods to cite the path of a script as a solution to relative path calls outside the CWD, but at the end of the day I came no closer to a solution I feel good about, and maybe never will. For now I'm content with using absolute paths stored in a variable, such as:

#!/bin/sh
#LAMPinstall.sh
thisdir=/path/to/scripts
${thisdir}/apacheinstall.sh
${thisdir}/mysqlinstall.sh
${thisdir}/phpinstall.sh

even though I'd have to modify the script each time the path changes.

So my question is more of a best practice question than asking for a solution to a particular problem (although it is a specific problem), to cover the following altogether instead of the articles and questions I read about each individual piece:

  1. Is it a bad idea to organize calls to shell scripts in the manner I'm using?
  2. Is it a bad idea to cite the path of a script using something like $(dirname "$0"). If so, how else would you maintain the organization of multiple shell files?
  3. When making considerations such as #2, is it important to consider all possible uses of your script (i.e.: if there are subdirectories and it's sourced) by someone else, or is it enough to say "This script must only be called, not sourced, if someone misuses it then it's on them." Sorry I couldn't phrase that any better.

Also, please forgive me if this is a naive question, I would just really like some input on this.

spiff
  • 308
  • 1
  • 3
  • 15
  • Great answer below, but my first thought is "don't overthink this too much". Trying to code around every possible error that you can imagine (or run into) could significantly distract you from your real purpose. Your summary in #3 is the right approach, "This is how this program must be used" (and document that well in comments , usage message and possibly man pages (not needed for an installer). Good luck! – shellter Jan 08 '20 at 15:01
  • Yeah, I agree, it's a bad habit of mine, especially given the small scope of the application. I recently fell into a similar rabbit hole regarding sed (see https://stackoverflow.com/q/59636708/3130769) when, in the end, the "error handling" was a wild "what if?" scenario that I could neatly ignore. My thinking is always "Well at least I learned a lot," but the effort in learning was not proportional to the need. My ultimate goal with this is to make these installation and configuration scripts packaged to run on a fresh install via rc local so my environment is ready out of the box. – spiff Jan 08 '20 at 16:19
  • Well I'm not saying don't try to improve the quality of your day-to-day coding (I don't think you are either). It **is** good to always challenge yourself. (all IMHO). Good luck. – shellter Jan 08 '20 at 17:12
  • Found this question which discusses it in depth, but it seems the user asked about shell and most of the answers were about bash https://stackoverflow.com/q/242538/3130769 – spiff Jan 15 '20 at 21:18

1 Answers1

2

The shell does not offer very sophisticated facilities for maintaining a library of related scripts. Common hacks include your suggested or at least implied

#!/bin/sh
here="$(dirname "$0")"
"$here"/apacheinstall.sh
"$here"/mysqlinstall.sh
"$here"/phpinstall.sh

or somewhat similarly

#!/bin/sh
PATH="$(dirname "$0")":$PATH
apacheinstall.sh
mysqlinstall.sh
phpinstall.sh

or requiring the user to set a suitable variable:

#!/bin/sh
: ${LAMP_INSTALL_PATH?Need this variable to be set}
"$LAMP_INSTALL_PATH"/apacheinstall.sh
"$LAMP_INSTALL_PATH"/mysqlinstall.sh
"$LAMP_INSTALL_PATH"/phpinstall.sh

A similar related technique is to default to something like /usr/local/lib/lampinstall and allow the user to override this path by similar means.

#!/bin/sh
: ${LAMP_INSTALL_PATH=/usr/local/lib/lampinstall}
"$LAMP_INSTALL_PATH"/apacheinstall.sh
"$LAMP_INSTALL_PATH"/mysqlinstall.sh
"$LAMP_INSTALL_PATH"/phpinstall.sh

Of course, with any reasonably modern distro, the real solution for this particular problem is to package the stuff you need into a package, and just install that. On Debianish platforms, for example,

sudo apt install -y local-lamp-stuff

where you simply need to create local-lamp-stuff.deb and have it Depends: on the packages you need as prerequisites.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Great answer, just wish you would also show us how to make a `package.deb` and dependcies too ;-) ! Good luck. to all. – shellter Jan 08 '20 at 14:57
  • My own favorite in-line-configuration trick uses default value as shown in the last block of code above, but know that you can override that value on the cmd-line by pre-appending the value you want before your command, i.e. `LAMP_INSTALL_PATH=/different/path ./installer.sh` for example. This also makes it simpler to include debugging variables in your code, but not have to process them from the cmd-line. Just `dbg=true dbg2=false ./installer.sh` Good luck to all! – shellter Jan 08 '20 at 15:05
  • For a simple `.deb` package which simply pulls in a bunch of dependencies, try `equivs`. For providing local values to override the default package configuration, look at `debconf` preseeding. – tripleee Jan 08 '20 at 15:16
  • I will look into creating a custom deb package, thanks – spiff Jan 08 '20 at 16:20