如何在 bash 脚本中循环此命令,直到响应包含字符串“连接成功”

如何在 bash 脚本中循环此命令,直到响应包含字符串“连接成功”

我对此毫无进展。

我只是想制作一个循环 10 次的 bash 脚本,如果输出包含字符串“连接成功”则中断。

ret=$?我尝试了类似then 的操作if [$ret -ne 0],但我不断地发生 else 语句,即使ret是 1。

所以现在我尝试使用 grep 搜索“连接成功”一词,但我不知道如何使用该语法。

所以我想要这样的东西:

for i in {1..10}
do
  ret=bluetoothctl connect 26:EE:F1:58:92:AF | grep "connection successful"

  if [$ret -ne ""]; then
    break
  fi
done

但显然使用正确的语法$ret=bluetoothctl connect 26:EE:F1:58:92:AF | grep "connection successful"

任何帮助将不胜感激。

答案1

grep当您只想测试某些文本是否与模式匹配时,很少需要存储输出。相反,只需grep使用其-q选项进行调用并对其退出状态进行操作:

#!/bin/sh

tries=10

while [ "$tries" -gt 0 ]; do
    if bluetoothctl connect '26:EE:F1:58:92:AF' | grep -q 'connection successful'
    then
        break
    fi

    tries=$(( tries - 1 ))
done

if [ "$tries" -eq 0 ]; then
    echo 'failed to connect' >&2
    exit 1
fi

如果bluetoothctl在失败和成功时返回正常的退出状态,那么您甚至不需要grep并且可以将if循环中的 - 语句缩短为以下内容:

if bluetoothctl connect '26:EE:F1:58:92:AF' >/dev/null
then
    break
fi

事实上,您也可以将bluetoothctl循环条件作为一部分(假设您从for循环切换到使用while循环,就像我在这里展示的那样):

#!/bin/sh

tries=10

while [ "$tries" -gt 0 ] && ! bluetoothctl connect '26:EE:F1:58:92:AF'
do
    tries=$(( tries - 1 ))
done >/dev/null

if [ "$tries" -eq 0 ]; then
    echo 'failed to connect' >&2
    exit 1
fi

考虑使用https://www.shellcheck.net验证 shell 脚本的语法。对于问题中的脚本,它会指出测试需要以下空格[ ... ]

  if [$ret -ne ""]; then
  ^-- SC1009 (info): The mentioned syntax error was in this if expression.
     ^-- SC1035 (error): You need a space after the [ and before the ].
     ^-- SC1073 (error): Couldn't parse this test expression. Fix to allow more checks.
                  ^-- SC1020 (error): You need a space before the ].
                  ^-- SC1072 (error): Missing space before ]. Fix any mentioned problems and try again.

测试非空字符串是用[ -n "$ret" ]or完成的[ "$ret" != "" ]。请注意,这-ne是一个算术测试。

您也没有将管道的输出正确分配给ret.不幸的是,语法绝对正确,但执行的操作完全不同,因此 ShellCheck 不会识别它。你打算使用的是

ret=$( bluetoothctl ... | grep ... )

答案2

Kusalananda 给出了如何改进代码的提示,但没有准确解释你做错了什么。让我们回顾一下:

ret=$?我尝试了类似then 的操作if [$ret -ne 0],但即使 ret 为 1,我仍然会发生 else 语句。

[是一个命令,因此您需要在命令及其参数之间放置一个空格,否则参数将被理解为命令名称的一部分。如果ret设置为1,bash 应该输出错误[1: command not found,并且if应该因为错误而看到 falsy 。同样,[要求最后一个参数为]0]不是这样。您还需要在那里有一个空间。

你想要的是:

if [ "$ret" -ne 0 ]

下一部分:

ret=bluetoothctl connect 26:EE:F1:58:92:AF | grep "connection successful"

您在这里所做的是运行 pipeline connect 26:EE:F1:58:92:AF | grep "connection successful",同时为connect命令(可能不存在)提供ret值为 value 的环境变量bluetoothctl

为了收集其输出,您需要将命令括在$()

  ret="$(bluetoothctl connect 26:EE:F1:58:92:AF | grep "connection successful")"

对这个:

  if [$ret -ne ""]; then

你也有同样的缺少空格的问题,但需要进一步理解的是,基本上大多数东西都是 bash 中隐式的字符串,引号主要只是一种更明确的方式(除了控制扩展的一些细节之外)。这意味着它的[$ret -ne ""]计算结果与以下内容几乎完全相同:

[$ret -ne ]
[$ret -ne ""''""'']
"["$ret "-ne" """]"
"["$ret"" "-ne""""" """]"""

如果$ret为空,则等同于[ -ne ]。它的计算结果为 true,因为只有一个参数(忽略]),[检查它是否是一个非空字符串,并且-ne是。

如果$ret是其他类似的东西foo bar baz,它会因为缺少引号而被分词,并且你会得到命令[foo bar baz -ne ],它有 4 个参数(包括])并且是假的,因为 bash 找不到命令[foo。如果你引用了$retlike ["$ret" -ne ""],那么你会得到等价的"[foo bar baz" -ne ],它将有 2 个参数(包括])并且也是假的,因为 bash 找不到该命令[foo bar baz

您在这里犯的另一个错误是使用-ne,它专门用于数字比较。即使您解决了空格和引号问题,bash 也会给您错误integer expression expected。您想要的是!=,用于字符串比较。

总结并解决所有这些问题,您想要的是:

if [ "$ret" != "" ]; then

也可以写成

if [ -n "$ret" ]; then

或者

if [ "$ret" ]; then

相关内容