bash 如何在循环中运行命令,直到在标准输出中看到一些字符串?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/128853/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 20:29:24  来源:igfitidea点击:

How do I run a command in a loop until I see some string in stdout?

linuxbashshell

提问by Alex Miller

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.

我确信有一些带有 perl、ruby、bash 的简单单行代码,可以让我在循环中运行命令,直到我在 stdout 中观察到一些字符串,然后停止。理想情况下,我也想捕获标准输出,但如果要进行控制台,那可能就足够了。

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.

目前有问题的特定环境是 RedHat Linux,但有时在 Mac 上也需要同样的东西。所以一些东西,通用的和 *nixy 会是最好的。不关心 Windows - 大概一个 *nixy 的东西可以在 cygwin 下工作。

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

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

采纳答案by Derek Park

In Perl:

在 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.

不过,您可能想在那里添加睡眠。

回答by Jay

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.

$cmd 是您要执行的命令。

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:

最重要的是,这里有一个 BASH 函数版本,因此如果您想定期从交互式 shell 调用它,您可以更轻松地调用它:

function run_until () {
  OUTPUT="";
  while [ `echo $OUTPUT | grep -c ` = 0 ]; do
    OUTPUT=``;
    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'tneed 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:

BASH 函数版本还有:

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

回答by markets

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

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

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#searchstring with /es#.

Perl 对于这样的东西来说是一种非常好的语言。将“command”替换为您要重复运行的命令。将“搜索”替换为您要搜索的内容。如果你想寻找的东西,在上面有一条斜线,然后替换m/search/m#search字符串/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.

此外,Perl 可以在许多不同的平台上运行,包括 Win32,这将适用于任何安装了 Perl 的地方。只需适当更改您的命令即可。

回答by ordnungswidrig

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

grep -c 99999将为匹配打印 99999 行上下文(我认为这已经足够了):

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.

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

回答by skoob

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 -amakes it append to the file instead of overwriting it every time. grep -qwill return 0if there was a match, 1otherwise and doesn't write anything to stdout. $?is the return value of the previous command, so $CONTwill be the return value of grep in this case.

tee 命令可以在仍然传递数据的同时捕获管道中的标准输出,并-a使其附加到文件而不是每次都覆盖它。grep -qreturn 0如果有一个匹配,1否则并没有写任何东西到标准输出。$?是上一个命令的返回值,因此$CONT在这种情况下 grep 的返回值也是如此。

回答by zigdon

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

  sleep 1
done

回答by Mecki

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.

编辑:我最初的答案是假设“某个字符串”意味着“任何字符串”。如果你需要寻找一个特定的,Perl 可能是你最好的选择,因为在 REGEX 匹配方面几乎没有什么能打败 Perl。

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:

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

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

Replace COMMANDwith the actually command to run and SOME_STRINGwith 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.

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

ORIGINAL ANSWER:

原始答案:

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

可能不是最好的解决方案(我不是一个好的 bash 程序员),但它会起作用:-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.

只需将 XXXX 替换为您的命令调用,例如尝试使用“ echo”,它永远不会返回(因为 echo 从不向标准输出打印任何内容),但是如果您使用“ echo test”,它将立即终止并最终打印出测试。

回答by Dave Webb

A simple way to do this would be

一个简单的方法是

until `/some/command`
do
  sleep 1
done

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

命令周围的反引号until用于测试要返回的某些输出,而不是测试命令的退出值。