继续运行命令,直到输出与之前在 Bash 中运行的不同

Mr.*_*. T 26 bash

我有一个脚本,我想每 x 秒运行一次,直到输出发生变化。通过一个简单的 while 或 until 循环,我知道如何使用 grep 检查特定字符串的输出,但我想要做的是继续迭代循环,直到当前迭代的输出不等于前一次迭代的输出。

我怎样才能在 bash 中实现这一点?

我得到的最接近的是下面,命令在每次迭代中运行两次,但这会产生额外的运行,因为 while 循环无法记住上一次迭代的输出。

while [ "$(command)" = "$(command)" ]; do sleep 10m; done
Run Code Online (Sandbox Code Playgroud)

Kam*_*ski 56

来自man 1 watch

-g,--chgexit
命令的输出改变时退出。

watchPOSIX 不需要,但无论如何它很常见。在 Debian 或 Ubuntu 中,它procpskillps(以及一些其他工具)一起包含在包中。

例子:

watch -g -n 5 'date +%H:%M'
Run Code Online (Sandbox Code Playgroud)

  • @l0b0 好吧,有趣的是我刚刚学会了 `watch` 可以做到这一点。我正在考虑像你这样的脚本(临时文件?或变量?变量中的哈希?),我想到可能已经有一个工具可以做到这一点。如果有的话,那么“看”。我查了一下,就在这里。 (4认同)

l0b*_*0b0 8

这应该可以解决问题 - 内联解释:

#!/usr/bin/env bash

# Exit if any errors occur or any undefined variables are dereferenced
set -o errexit -o nounset

# Store the outputs in a separate directory
output_directory="$(mktemp --directory)"
last_output_file="${output_directory}/last"
current_output_file="${output_directory}/current"
touch "$current_output_file"
ln --symbolic "$current_output_file" "$last_output_file"
# The count is just here to show that we always run at least twice, and that
# after that we run the command a random number of times.
count=0

while true
do
    echo "$((++count))"

    # This is our command; it prints 0 or 1 randomly
    if [[ "$RANDOM" -gt 16383 ]]
    then
        echo 0 > "$current_output_file"
    else
        echo 1 > "$current_output_file"
    fi

    # Abort the loop when the files differ
    if ! diff --brief "$last_output_file" "$current_output_file"
    then
        break
    fi

    # Shunt the current output to the last
    mv "$current_output_file" "$last_output_file"
done
Run Code Online (Sandbox Code Playgroud)

可能有更简单的方法可以做到这一点,但它有几个有用的功能:

  • 它避免创建任何无关的文件,例如mktemp用于每个输出。
  • 通过将最后一个输出文件作为当前输出文件的符号链接开始,我们保证第一个diff命令看到两个相同的文件。这避免了重复我们正在测试的命令,代价是额外diff执行一次。


ilk*_*chu 6

显然,这watch是去这里的方法,如另一个答案中所述。但是,如果您确实想要或需要在 shell 中执行此操作,可以使用以下一种方法:

#!/bin/sh
unset prev
while output=$(some_command);
      [ "${prev+set}" != set ] || [ "$output" = "$prev" ];
do
    prev=$output
    sleep 10;
done
Run Code Online (Sandbox Code Playgroud)

"${prev+set}"扩展为setifprev在开始时未设置后设置为一个值,因此实际上它强制循环的内容至少运行一次。或者,我们可以prevprev=$(some_command)some_command循环开始时运行额外时间为代价进行初始化。

此外,如评论中所述,命令替换会从命令输出中删除所有尾随换行符,因此如果结果中的唯一更改是这些更改,则将无法区分输出。不过,这应该很少重要。