cron calls my bash script (it's a backup script) with a very sparse PATH of
/usr/bin:/usr. As a result, while a line like
lvcreate --size 1G --snapshot ...
works just fine when I run the script from a terminal, it can't find lvcreate if run from the crontab. A similar problem was described here, but I am more interested in general strategies to deal with the underlying problem.
Ideas I've come up with so far:
As a quick workaround, I manually set PATH at the top of my script. I also could do this in the crontab. Should system-wide PATH ever change, I might need to manually update all those lines.
A more permanent solution would be to use absolute paths in my script, e.g. substitute the call with something like
/sbin/lvcreate --size 1G ... but while this would work on the Ubuntu Server where I first encountered this problem, on my Arch Linux ARM (RPi) lvcreate is located in /usr/bin/.
which every command with PATH set properly, store the results and set a local alias at the top of my script, but that seems a bit messy to me.
I thought about sourcing /etc/environment. That would work for my Ubuntu machines (and I think also for plain debian), but then again on my RPi the system-wide PATH is not set there. For the same reason (not wanting to rely on the location and/or use of certain files in various distros) I am hesitant to use .bashrc or similar.
So my question is: What is a good way to make scripts not rely on PATH being set and still keep them portable? Is there a state of the art method?
If you do it from your script, you can add to the current setting of
$PATH, then you'll respect whatever is there and just add what you want to ensure is not missing:
#!/bin/bash PATH=$PATH:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin ...
Perhaps it would be more appropriate to set
PATH in the crontab configuration (you can set environment variables there), since in effect you're working around a shortcoming of cron itself, so perhaps fixing it in cron would make more sense than modifying your script.
If you set it in cron, you'll need to make the
PATH setting complete, since you can't refer to its current value from there:
# my user's crontab PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin 0 2 * * * /usr/local/bin/my2amjob.sh
(You might need a slightly different order of directories, depending on whether some of your systems or yourself end up installing "overrides" for simpler versions installed in directories that are listed later in
$PATH, but check what your systems currently set and see if you can reconcile that.)
Hopefully this will solve your problem.
Should system-wide PATH ever change, I might need to manually update all those lines.
Frankly, I wouldn't worry too much about that. Your distribution packages will always install their binaries under
/sbin, so you'll always be able to access them without any path changes. Distributions have long figured that updating
$PATH to include new directories is a nightmare, so they've been avoiding that, adding symlinks or wrapper scripts to the main
bin directories instead.
For software you install yourself, that recommends updating
$PATH, I'd recommend doing the same, creating symlinks or wrapper scripts under
/usr/local/bin and avoiding changes to
$PATH if possible.
Linux distributions are also taking some steps to make these differences in
$PATH a problem of the past.
First there's the
/usr merge effort, which turns
/bin into a symlink to
/sbin into a symlink to
/usr/sbin. All the binaries are installed under
/usr (easier for packagers) but scripts which still refer to them through some absolute path like
/sbin/mytool will keep working through the symlinks. This has been adopted by distros such as Fedora and ArchLinux, and other distros like Debian and Ubuntu are currently going through the steps of adoption.
ArchLinux seems to have gone a step further, and it has merged
bin together. So in modern ArchLinux where the merge was adopted, you can refer to a binary by any of the four possible absolute paths and you'll find them. Remains to see whether other distributions will also adopt this second merge.
Finally, you might want to consider more modern alternatives to cron. Cron has quite a few idiosyncrasies, like the bare environment you're experiencing, but also use of emails for command output (rather than using a logging system) and awkward command line escaping.
You mentioned Ubuntu and ArchLinux, both of them support systemd Timers, built-in, without having to install any packages.
You might want to check the ArchLinux wiki, which has excellent articles on how to use them. In particular, you might want to check using systemd Timers as a cron replacement, which has specific recipes which might be very useful for your particular use case.
PATHin cron in the same you'll get with
getconf PATH. It seldom includes the
sbindirectories on Linux. — Mar 10, 2019 at 17:34
External links referenced by this document:
Local articles referenced by this article: