There are a few commands that can be used to find files on a Linux system.
- find command
- locate command
- whereis command
- which command (this article)
The which command can be used to determine if a Command Line Interface (CLI) exists in $PATH. For example, which can be used to determine if the java CLI exists in $PATH.
which java
If the java CLI does exist in $PATH, something like this should be returned, meaning that the java CLI is located at /usr/bin/java.
/usr/bin/java
And the exit code should be 0 (success).
~]# echo $?
0
On the other hand, in this example, the which command show that the jar command does not exist in $PATH.
~]$ which jar
/usr/bin/which: no jar in (/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/john.doe/.local/bin:/home/john.doe/bin)
And the exit code should be 1 (failed).
~]# echo $?
1
The directories being searched are the directories in $PATH.
~]# echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/john.doe/.local/bin:/home/john.doe/bin
Let's say the jar command is at /tmp/java/bin/jar. In the /tmp/java/bin directory is not in $PATH, the which command returns "no jar". A temporary solution is to use the export command to add /tmp/java/bin to $PATH.
export PATH=$PATH:/tmp/java/bin
And now the which command finds the jar command at /tmp/java/bin/jar.
~]# which jar
/tmp/java/bin/jar
Be aware that often, the directories in $PATH are loaded from your users bash profile file, such as /home/john.doe/.bash_profile.
~]$ cat .bash_profile
PATH=$PATH:$HOME/.local/bin:$HOME/bin
export PATH
For example, let's say echo returns the following.
~]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/john.doe/.local/bin:/home/john.doe/bin
Then if you were to run the following Python script using subprocess and logger to run echo $PATH.
~]$ cat testing.py
#!/usr/bin/python3
import shutil
import logging
import getpass
import sys
import subprocess
log_file="/home/john.doe/testing.log"
logger = logging.getLogger()
logger.setLevel(logging.INFO)
format = logging.Formatter(fmt="[%(asctime)s %(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
fileHandler = logging.FileHandler(log_file)
fileHandler.setFormatter(format)
logger.addHandler(fileHandler)
consoleHandler = logging.StreamHandler(sys.stdout)
consoleHandler.setFormatter(format)
logger.addHandler(consoleHandler)
command="echo $PATH"
stdout, stderr = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
logging.info(f"PATH = {stdout.decode('utf-8').strip()}")
The output should be exactly the same as if you ran the echo $PATH command on the command line.
~]$ python3 testing.py
[2024-04-08 04:44:56 INFO] PATH = /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/john.doe/.local/bin:/home/john.doe/bin
But if you use crontab to run the script.
* * * * * python3 /home/john.doe/testing.py
$PATH may only contain /usr/bin and /bin.
[2024-04-08 04:37:01 INFO] stdout = /usr/bin:/bin
Which may cause which to return None (if using shutil.which) or stderr if a CLI exists in a $PATH other than /usr/bin and /bin. This occurs because cron jobs run in a minimal environment, and since they're executed directly by crond
without a shell (unless you force one to be created), the regular shell setup never happens. One option is to include PATH in crontab, something like this. Be aware that this will apply to every entry in your users crontab.
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/john.doe/.local/bin:/home/john.doe/bin
Or you could specify the full path to the CLI in shutil.which.
#!/usr/bin/python3
import shutil
response = shutil.which('/usr/local/bin/java')
print(response)
Did you find this article helpful?
If so, consider buying me a coffee over at