如何在循环中运行命令,直到在stdout中看到一些字符串?

时间:2020-03-06 14:40:47  来源:igfitidea点击:

我敢肯定,perl,ruby,bash有一些琐碎的单行代码,无论哪种方式都会让我循环运行命令,直到在stdout中观察到一些字符串,然后停止。理想情况下,我也想捕获stdout,但是如果要进行控制台操作,这可能就足够了。

目前有问题的特定环境是RedHat Linux,但有时在Mac上也需要同样的东西。因此,通用和* nixy最好。不必关心Windows,大概在cygwin下可以解决*麻烦的事情。

更新:请注意,"观察某些字符串"是指" stdout包含某些字符串",而不是" stdout是某些字符串"。

解决方案

有很多方法可以做到这一点,首先想到的是:

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

$ cmd是我们要执行的命令。

对于此问题,这里是BASH函数版本,因此如果我们希望定期从交互式shell调用它,则可以更轻松地调用它:

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

免责声明:仅经过轻微测试,如果命令中包含大量参数或者字符串包含特殊字符,则可能需要进行一些其他转义操作。

编辑:基于亚当评论的反馈,如果我们出于任何原因不需要输出(即不想显示输出),则可以使用此较短的版本,并减少反引号的使用,从而减少开销:

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

BASH功能版本也:

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

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

  sleep 1
done

编辑:
我最初的答案是假设"某个字符串"的意思是"任何字符串"。如果我们需要寻找一个特定的产品,Perl可能是最佳选择,因为在REGEX匹配方面几乎没有什么可以击败Perl的。

但是,如果由于某种原因不能使用Perl(我们可以期望Perl出现在大多数Linux发行版中,但是没有人强迫用户安装它,尽管Perl可能不可用),则可以借助grep。但是,到目前为止,我看到的一些grep解决方案不是最佳的(它们比必要的要慢)。在这种情况下,我宁愿执行以下操作:

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

将COMMAND替换为要运行的实际命令,并将SOME_STRING替换为要搜索的字符串。如果在COMMAND的输出中找到SOME_STRING,则循环将停止并在找到SOME_STRING的位置打印输出。

原始答案:

可能不是最好的解决方案(我不是很好的bash程序员),但它会工作:-P

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

只需将XXXX替换为命令调用即可,例如尝试使用" echo",它将永远不会返回(因为echo不会将任何内容输出到stdout),但是,如果我们使用" echo test",它将立即终止并最终打印出测试。

在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);
    }
}

例子:

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

不过,我们可能想要在其中添加睡眠。

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

tee命令可以在仍然传递数据的同时捕获管道中的stdout,而-a使其添加到文件中而不是每次都覆盖它。如果存在匹配,grep -q返回0,否则将返回'1'并且不向stdout写任何东西。 $是上一条命令的返回值,因此在这种情况下$ CONT将是grep的返回值。

一个简单的方法是

until `/some/command`
do
  sleep 1
done

命令周围的反引号对返回的某些输出进行"直到"测试,而不是测试命令的退出值。

grep -c 99999将为匹配显示99999行上下文(我想这已经足够了):

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

或者

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

...这将打印一些漂亮的圆点以指示进度。

我很惊讶我没有看到这里提到的简短的Perl单行代码:

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

对于诸如此类的东西,Perl是一种非常不错的语言。将"命令"替换为要重复运行的命令。用我们要搜索的内容替换"搜索"。如果我们要搜索带有斜杠的内容,则将m / search /替换为带有/ es#m#search字符串。

另外,Perl可以在许多不同的平台上运行,包括Win32,并且无论我们安装了Perl还是在哪里都可以使用。只需适当地更改命令即可。