I've just recently tackled this same problem. I'm trying to monitor dmesg
so that I can discover USB MIDI devices as they are connected.
First, let's not use dmesg -w
, since doing so will also display events that have occurred recently in the past. Instead, let's tail /var/log/messages
in refresh mode, specifying 0
lines to be displayed initially:
tail -f -n 0 '/var/log/messages'
Now that we know what command we want to monitor, how can we await a new dmesg
entry that meets our criteria, and then terminate our polling? Fortunately, bash
redirection makes it easy to put a task into the background, and subsequently access its STDOUT
and STDIN
:
exec 3< <( tail -f -n 0 '/var/log/messages' )
Here, we've redirected the output of our tail
operation into file descriptor 3
, which we can unset as follows once we've encountered our criteria:
exec 3>&-
Note that unsetting the file descriptor also terminates (via SIGPIPE) the command which was bound to it, so in this case, we don't have to worry about cleaning up the tail
operation.
Now that we know how to bind and unbind our backgrounded command to a file descriptor, we can execute a while
loop that will read
and process each line of text provided via file descriptor 3
:
exec 3< <( tail -f -n 0 '/var/log/messages' )
local line
while IFS='' read line
do
:
done <&3
The read
command will patiently await new lines of text to appear on file descriptor 3
, and the while command will loop until the read
command says that there are no more lines of text to read. read
would only do so if file descriptor 3
is closed, and since that descriptor is bound to tail -f
, which never closes its output voluntarily, the while
loop will not terminate, and we must thus manually break the loop.
Now we can add a statement that evaluates $line
in some manner, and breaks the loop if some particular criteria matches. Here, we can use the success status of grep -o
to determine if we've found what we're looking for, and if so, do something appropriate, including breaking the loop and then closing file descriptor 3
:
exec 3< <( tail -f -n 0 '/var/log/messages' )
local line
while IFS='' read line
do
echo "${line}" | grep -o 'sd[[:lower:]]' && {
echo "YAY! ${line} matched."
break
}
done <&3
exec 3>&-
If you want to implement a solution that doesn't use bash
file redirection in this manner, please refer to several useful alternatives documented at Bash: Capture output of command run in background.