1

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/.

  • I could 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?

5
  • Really? An empty path? What Unix is this? Note that /etc/environment is not a shell script that you can source generally. It's a configuration file.
    – Kusalananda
    Commented Mar 10, 2019 at 16:01
  • Search & read is a useful strategy; these look relevant: 1. How can I run a cron command with existing environmental variables? , 2. Where can I set environment variables that crontab will use?. In particular, #2 looks relevant to your question.
    – Seamus
    Commented Mar 10, 2019 at 16:35
  • Kusalananda, my bad, PATH is actually set to /usr/bin:/bin. I will update my question. However, that does not solve my problem: The command I want to run is in /sbin.
    – Andreas K
    Commented Mar 10, 2019 at 17:09
  • @Seamus: As you can see from my question, I did do my research and I am aware of those options. If this is really the best/only way to go, that's ok for me. filbranden's answer certainly reduced my concerns about possible changes to PATH.
    – Andreas K
    Commented Mar 10, 2019 at 17:28
  • @AndreasK: Fair enough, and apologies if that sounded accusatory. I felt like the 2nd link pretty much answered yr Q, but perhaps not. As far as change... well that's just a constant :) As a famous man once said, "you may not like it, but accept it"
    – Seamus
    Commented Mar 10, 2019 at 18:54

1 Answer 1

3

Set PATH explicitly.

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 /usr/bin, /usr/sbin, /bin or /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 /usr/bin and /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 /bin/mytool or /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 sbin and 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.

3
  • 1
    My guess is that the default PATH in cron in the same you'll get with getconf PATH. It seldom includes the sbin directories on Linux.
    – Kusalananda
    Commented Mar 10, 2019 at 17:34
  • 2
    Great answer! I like the idea of a systemd Timer and the possibility to use it as a replacement for anacron at the same time.
    – Andreas K
    Commented Mar 10, 2019 at 17:40
  • @AndreasK It's a bit out of scope, but I thought it was worth mentioning it. Cheers!
    – filbranden
    Commented Mar 10, 2019 at 17:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .