Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Similar to this question, in Linux tcsh I want to tail a log file and when I match a regular expression I want to execute an arbitrary command. How can I do this?

share|improve this question

4 Answers 4

up vote 2 down vote accepted

In the poster's note a comment in that question leads to an SO post on disabling the auto-buffering when using pipes. The solution was to use unbuffer (from expect), as in:

$ unbuffer tail -f foo | find-and-run "findwhat" "runwhat"

wfaulk's answer (plus ayrnieu's cleanup) provides us with the script find-and-run:

#!/usr/bin/perl
# run: find-and-run "regex-to-find" "commandtorun"
die "usage: $0 <regex> <exec-this> [exec-this-arg1 ...]\n"
   unless @ARGV >= 2;

my ($re, @run) = @ARGV; 

while (<>) {
        last if /$re/;
}
exec { $run[0] } @run;

Note 1: on Debian lenny, you'll need to install the "expect-dev" package; unbuffer is installed as "expect_unbuffer".

Note 2: this is completely untested and completely slightly naive. for security, don't use this. if you do use this, you probably want to single-quote your regex to avoid shell substitution/expansion.

Note 3: big thanks to ayrnieu for script completion and cleanup.

share|improve this answer
1  
die "usage: regex exec-this [arg1 ...]\n" unless @ARGV >= 2; my ($re, @run) = @ARGV; ... last if /$re/; ... exec { $run[0] } @run –  ayrnieu Oct 10 '09 at 4:08
    
exec/system is not so important, but $1 $2 as is will definitely fail, and usage: is just a good idea. –  ayrnieu Oct 10 '09 at 4:10
1  
better: "usage: $0 <regex> <exec-this> [<arg1> ... <argn>]\n" –  ayrnieu Oct 10 '09 at 4:10

This seems suited to a short perl script:

#!/usr/bin/perl

while (<>) {
        last if /regex/;
}
system("ls");

If you want to provide a pattern and a command with as flagged arguments, see the documentation at perldoc Getopt::Std.

share|improve this answer
1  
I really wanted "<> until /regex/;" to work, but I couldn't make it happen. –  wfaulk Oct 9 '09 at 23:28
1  
perl -Mwarnings -e 'scalar <> until /a/' -- note the warning. –  ayrnieu Oct 10 '09 at 1:12

In my case (on RHEL, I wanted tail -n 0 -f file | grep -m 1 pattern to terminate immediately when pattern occurs in the growing file), plain usage of the unbuffer utility from the Expect package didn't solve the problem for some reason.

But based on a blog post (http://www.smop.co.uk/blog/index.php/2006/06/26/tail-f-and-awk/) I've discovered that redirecting input from tail launched in a subshell did the trick:

grep -m 1 pattern <(tail -n 0 -f file)

This was not as simple, though. While working in an interactive shell, the same command, when run remotely using SSH, still froze as usual:

ssh login@hostname 'grep -m 1 pattern <(tail -n 0 -f file)'

I've discovered that in this case, one must unbuffer tail's output using the unbuffer utility from Expect:

ssh login@hostname 'grep -m 1 pattern <(unbuffer -p tail -n 0 -f file)'

This must not be used on an interactive shell - unbuffer will cause an ioctl(raw): I/O error!

So the advice for you is that if the proposed solutions don't work, try launching tail -f in a subshell and if you want to do it in a script or on a non-interactive shell, using unbuffer or unbuffer -p might be required.

BTW, see this article for thorough explanation of the output buffering problem: http://www.pixelbeat.org/programming/stdio_buffering/

share|improve this answer

A different approach is to use a program like swatch. Swatch is built to do exactly what you are talking about, plus a dozen other things.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.