2

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?

4 Answers 4

2

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.

3
  • 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, 2009 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, 2009 at 4:10
  • 1
    better: "usage: $0 <regex> <exec-this> [<arg1> ... <argn>]\n"
    – ayrnieu
    Oct 10, 2009 at 4:10
3

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.

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

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/

0

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.

You must log in to answer this question.

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