Question

I'm sure there's some trivial one-liner with perl, ruby, bash whatever that would let me run a command in a loop until I observe some string in stdout, then stop. Ideally, I'd like to capture stdout as well, but if it's going to console, that might be enough.

The particular environment in question at the moment is RedHat Linux but need same thing on Mac sometimes too. So something, generic and *nixy would be best. Don't care about Windows - presumably a *nixy thing would work under cygwin.

UPDATE: Note that by "observe some string" I mean "stdout contains some string" not "stdout IS some string".

Was it helpful?

Solution

In Perl:

#!/usr/local/bin/perl -w

if (@ARGV != 2)
{
    print "Usage: watchit.pl <cmd> <str>\n";
    exit(1);
}

$cmd = $ARGV[0];
$str = $ARGV[1];

while (1)
{
    my $output = `$cmd`;
    print $output; # or dump to file if desired
    if ($output =~ /$str/)
    {
        exit(0);
    }
}

Example:

[bash$] ./watchit.pl ls stop
watchit.pl
watchit.pl~
watchit.pl
watchit.pl~
... # from another terminal type "touch stop"
stop 
watchit.pl
watchit.pl~

You might want to add a sleep in there, though.

OTHER TIPS

There's a bunch of ways to do this, the first that came to mind was:

OUTPUT=""; 
while [ `echo $OUTPUT | grep -c somestring` = 0 ]; do 
  OUTPUT=`$cmd`; 
done

Where $cmd is your command to execute.

For the heck of it, here's a BASH function version, so you can call this more easily if it's something you're wanting to invoke from an interactive shell on a regular basis:

function run_until () {
  OUTPUT="";
  while [ `echo $OUTPUT | grep -c $2` = 0 ]; do
    OUTPUT=`$1`;
    echo $OUTPUT;
  done
}

Disclaimer: only lightly tested, may need to do some additional escaping etc. if your commands have lots of arguments or the string contains special chars.

EDIT: Based on feedback from Adam's comment - if you don't need the output for any reason (i.e. don't want to display the output), then you can use this shorter version, with less usage of backticks and therefore less overhead:

OUTPUT=0; 
while [ "$OUTPUT" = 0 ]; do 
  OUTPUT=`$cmd | grep -c somestring`;
done

BASH function version also:

function run_until () {
  OUTPUT=0; 
  while [ "$OUTPUT" = 0 ]; do 
    OUTPUT=`$1 | grep -c $2`; 
  done
}

I'm surprised I haven't seen a brief Perl one-liner mentioned here:

perl -e 'do { sleep(1); $_ = `command`; print $_; } until (m/search/);'

Perl is a really nice language for stuff like this. Replace "command" with the command you want to repeatedly run. Replace "search" with what you want to search for. If you want to search for something with a slash in it, then replace m/search/ with m#search string with /es#.

Also, Perl runs on lots of different platforms, including Win32, and this will work wherever you have a Perl installation. Just change your command appropriately.

grep -c 99999 will print 99999 lines of context for the match (I assume this will be enough):

while true; do /some/command | grep expected -C 99999 && break; done

or

until /some/command | grep expected -C 9999; do echo -n .; done

...this will print some nice dots to indicate progress.

while (/bin/true); do
  OUTPUT=`/some/command`
  if [[ "x$OUTPUT" != "x" ]]; then
    echo $OUTPUT
    break
  fi

  sleep 1
done

EDIT: My original answer was assuming that "some string" means "any string". If you need to look for a specific one, Perl is probably your best option, since almost nothing can beat Perl when it comes to REGEX matching.

However, if you can't use Perl for any reason (you can expect Perl to be present in most Linux distros, but nobody forces a user to install it, though Perl may not be available), you can do it with the help of grep. However, some of the grep solutions I have seen so far are suboptimal (they are slower than would be necessary). In that case I would rather do the following:

MATCH=''; while [[ "e$MATCH" == "e" ]]; do MATCH=`COMMAND | grep "SOME_STRING"`; done; echo $MATCH

Replace COMMAND with the actually command to run and SOME_STRING with the string to search. If SOME_STRING is found in the output of COMMAND, the loop will stop and print the output where SOME_STRING was found.

ORIGINAL ANSWER:

Probably not the best solution (I'm no good bash programmer), but it will work :-P

RUN=''; while [[ "e$RUN" == "e" ]]; do RUN=`XXXX`; done ; echo $RUN

Just replace XXXX with your command call, e.g. try using "echo" and it will never return (as echo never prints anything to stdout), however if you use "echo test" it will terminate at once and finally print out test.

CONT=1; while [ $CONT -gt 0 ]; do $CMD | tee -a $FILE | grep -q $REGEXP; CONT=$? ; done

The tee command can capture stdout in a pipe while still passing the data on, and -a makes it append to the file instead of overwriting it every time. grep -q will return 0 if there was a match, 1 otherwise and doesn't write anything to stdout. $? is the return value of the previous command, so $CONT will be the return value of grep in this case.

A simple way to do this would be

until `/some/command`
do
  sleep 1
done

The backticks around the command make the until test for some output to be returned rather than testing the exit value of the command.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top