I have a short script which is executed by a system daemon for particular events. I know the event is occurring and the script is executing, but it does not do what I intend. Strangely, it does when I run it manually, so I am very confused.
How can I figure out what is going on? The script is basically a series of commands like this:
/bin/foo on 3
sudo bar a
2 Answers 2
First, if the script is run by a system daemon and that daemon is running with root privileges, you do not need to use sudo
. This includes init
(and systemd
), which includes rc.local
. If that daemon is not running with root privileges, then sudo
will not work unless /etc/sudoers
is configured to allow such (and without a password). Raspbian users may be confused by this since the pi
user is allowed to do anything by default (and if you look in /etc/sudoers
you will see how that is accomplished).
Next, you can capture output from any bash
script, or any set of commands inside a bash script, by executing them in a subshell like this:1
(
/bin/foo on 3
sudo bar a
) &> /var/log/myTestLog.txt
The ()
indicates a subshell. All output from anything inside this is being redirected to a /var/log/myTestLog.txt
file. A few notes:
&>
is a bashism, so if the script is executed via a shebang on the first line, it should be#!/bin/bash
, not just/bin/sh
. "Bashisms" work only in thebash
shell.This includes
/etc/rc.local
, which by default uses/bin/sh
(ie., yes, you can safely change that to/bin/bash
)./var/log
requires root privileges to write to. If the process does not have such, use or create a directory you know it can. When in doubt, if you can test this without having to shutdown or reboot the system, use/tmp
, which is world writable (i.e., by anyone). However/tmp
does not persist across boots. It is also a small, RAM based partition, so do not write gigs of data to it. It is not your SD card [actually on current versions of Raspbian it is, but don't count on this in practice].&>
will overwrite anything inmyTestLog.txt
. If instead you want to append to a existing log, which might be a good idea for debugging purposes, use&>>
. You could then add a command to the beginning of that subshell like this:echo Starting $(date)
To separate the information from each run. If you're not sure what this does, try on the command line.
This last point is a good illustration of something you can do with regard to commands that don't output anything -- but most of them do if you include, e.g., -v
for "verbose". Beware for some commands -v
means "print version information". Have a look in the man page for the command to be sure if and how this will work (some commands also use a different switch than -v
).
By convention, commands also return a value of 0 when complete. This is sometimes called the "exit status" and you do not normally see it, but the shell will show you with echo $?
. Try
ls /
echo $?
ls /nonexistantdir
echo $?
You'll get 0 and 2. If you then look in the man page for ls
under "Exit status", you'll see the rather unspecific, cryptic:
2 if serious trouble (e.g., cannot access command-line argument).
Which may or may not be better than nothing, but there you go.
At the very least, this indicates the command failed for some reason. The exit status also allows you to do things like this:
/bin/foo && sudo bar
The &&
in this case means "if the first command succeeds", presuming the first command uses the convention of returning 0 (which is why they usually do). If /bin/foo
does not work, cannot be found, etc., then sudo bar
will never happen.
Using a combination of logging messages and conditional execution (&&
) should get you much closer to figuring out the problem, or at least getting information that might be useful to others in helping you solve the problem. Without that, the most anyone else can often do is guess.
1. You can accomplish the same output redirection for an entire script from the inside by using:
exec &> /var/log/myTestLog.txt
At the top (or anywhere, and it will apply to everything subsequent).
-
1The answer does not clearly state what
&>
does. It redirects both stdout and stderr. See here for full explanation: stackoverflow.com/questions/11255447/what-does-meanDaniel K.– Daniel K.2020年08月12日 09:26:33 +00:00Commented Aug 12, 2020 at 9:26
One important aspect people tend to forget when running scripts as daemons is shell environment, and $PATH
variable in particular. In your example, the second line relies on $PATH
: the full name of sudo
is /usr/bin/sudo
, and your user shell only knows that because it was told to search /usr/bin
when looking for executables. The same is true for bar
.
Considering that sudo
is not needed when running scripts as daemons, your second line should look like:
/path/to/bar a
U&L
. I misread the title and would suggest "Logging the output of a system script for debugging" would make the purpose clearer. Also my brain freezes over when I read thesefoo
bar
examples, I prefer something which looks more real world. I am reluctant to edit any post, so will leave this to you if you think it can be improved.