ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Bash 的“if”语句中比较两个字符串变量? [复制]

这个问题在这里已经有了答案:How to compare strings in Bash (11 answers) 3年前关闭。

我试图让 if 语句在 Bash 中工作(使用 Ubuntu):

#!/bin/bash

s1="hi"
s2="hi"

if ["$s1" == "$s2"]
then
  echo match
fi

我尝试了各种形式的 if 语句,使用 [["$s1" == "$s2"]],带和不带引号,使用 ===-eq,但我仍然收到以下错误:

[嗨:找不到命令

我查看了各种网站和教程并复制了它们,但它不起作用 - 我做错了什么?

最后,我想说如果 $s1 包含 $s2,那么我该怎么做呢?

我只是计算了空格...:/我怎么说包含?

我试过了

if [[ "$s1" == "*$s2*" ]]

但它没有用。


B
Basil Musa

对于字符串相等比较,使用:

if [[ "$s1" == "$s2" ]]

对于字符串不等于比较,请使用:

if [[ "$s1" != "$s2" ]]

对于 a 包含 b,请使用:

if [[ $s1 == *"$s2"* ]]

(并确保在符号之间添加空格):

坏的:

if [["$s1" == "$s2"]]

好的:

if [[ "$s1" == "$s2" ]]

stackoverflow.com/a/229606/376454 我不得不使用这个答案将变量与固定字符串进行比较。
IRC 上挑剔的家伙告诉我你应该使用 if [[ "$s1" == "$s2" ]] 或 case。
双等号在第一种情况下是错误的。 Bash 可以容忍它,但可移植的变体是 if [ "$s1" = "$s2" ]。另请参阅Rahul's answer
嗨,我想知道为什么这很糟糕-> if ["$s1" == "$s2"] 空格有什么意义?
@Sangimed,[ 是一个 command (实际上是命令 test 的替代名称);如果您运行 which [,您会看到磁盘上实际上有一个可执行文件(即使 shell 可能提供内置实现作为性能优化)。就像您必须在要打印的文件名之前的命令名称 ls 之间放置一个空格一样,您需要在 [ 命令的名称及其第一个参数之后放置一个空格,并且在它传递的每个参数之间(如果作为 [ 而不是 test 调用,它期望它的最后一个参数是 ])。
u
unwind

你需要空格:

if [ "$s1" == "$s2" ]

只是想说确保在开始和结束的方括号和 "$s1" == "$s2" 语句之间留一个空格,否则它将不起作用。此外,这也有效:if test "$s1" = "$s2"
一切都与空间有关:))
@racl101 的第一条评论为我解决了这个问题。谢谢!!
== 不适用于 ash、dash 或其他地方的基准 POSIX 实现 test。请改用 =
R
Robin Daugherty

您应该小心在 '[' 符号和双引号之间留一个空格,其中变量包含以下内容:

if [ "$s1" == "$s2" ]; then
#   ^     ^  ^     ^
   echo match
fi

^ 显示您需要留下的空格。


非常感谢您指出必要的空间。解决了我的问题。今天刚开始bash,似乎很多时候空格会导致错误,即声明变量等。
包含 ; thenfi 部分的奖励积分。
== 不适用于 ash、dash 或其他地方的基准 POSIX 实现 test。请改用 =
P
Peter Mortensen

我建议这个:

if [ "$a" = "$b" ]

请注意左/右括号和变量之间的空格以及包裹“=”符号的空格。

另外,请注意您的脚本标题。是否使用不一样

#!/bin/bash

或者

#!/bin/sh

Here's the source


支持,但在阅读 ABS 时请务必小心。链接到更权威的来源可能是首选。
感谢您的建议,确保更权威的来源更准确。
/bin/sh: 1: [: 缺失]
@holms,当按照此处给出的精确使用时,OP 的代码不会发生这种情况。您需要显示您的确切用法。
P
Peter Mortensen

Bash 4+ 示例。注意:当单词包含空格等时,不使用引号会导致问题。始终在 Bash IMO 中引用。

以下是 Bash 4+ 的一些示例:

示例 1,检查字符串中的“是”(不区分大小写):

if [[ "${str,,}" == *"yes"* ]] ;then

示例 2,检查字符串中的“是”(不区分大小写):

if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then

示例 3,检查字符串中的“是”(区分大小写):

 if [[ "${str}" == *"yes"* ]] ;then

示例 4,检查字符串中的“是”(区分大小写):

 if [[ "${str}" =~ "yes" ]] ;then

示例 5,完全匹配(区分大小写):

 if [[ "${str}" == "yes" ]] ;then

示例 6,完全匹配(不区分大小写):

 if [[ "${str,,}" == "yes" ]] ;then

示例 7,完全匹配:

 if [ "$a" = "$b" ] ;then

很好的答案。打赌,如果它离顶部不那么远,它会得到更多的支持。
G
Gabriel Staples

这个问题已经有了很好的答案,但在这里使用单等号(=)和双等号(==)之间似乎有点混淆

if [ "$s1" == "$s2" ]

主要区别在于您使用的脚本语言。如果您使用 Bash,则在脚本的开头包含 #!/bin/bash 并将您的脚本另存为 filename.bash。要执行,请使用 bash filename.bash - 然后您必须使用 ==

如果您使用的是 sh,则使用 #!/bin/sh 并将您的脚本另存为 filename.sh。要执行使用 sh filename.sh - 那么您必须使用单个 =。避免将它们混合在一起。


断言“您必须使用 ==”是不正确的。 Bash 支持 ===。此外,如果您的脚本开头有 #!/bin/bash,您可以使其可执行并像 ./filename.bash 一样运行它(不是文件扩展名很重要)。
完美,我想我现在必须删除这个答案,但是如果你解释为什么在不使文件可执行并通过在文件名前添加 sh/bash 运行时它不起作用,这将非常有帮助?
这对shebang和文件名的意义感到困惑。如果您正确地将 #!/bin/sh#!/bin/bash 作为脚本的第一行,您只需使用 ./filename 运行它,实际文件名可以是完全任意的。
现在这变得有趣了,即使您不添加任何 shebang 和任何扩展名并使用“bash/sh 文件名”执行它,无论您使用单等号还是双等号,它都可以工作。如果你做同样的事情,还有一件事文件(没有 shebang 和任何扩展名)可执行文件,然后你可以像 ./filename 一样执行它(无论是单等还是双等)。(在 Arch linux 上用 bash 4.3.46 试过)。
如果你通过运行say“bash filename”来执行文件——那么你只是将'filename'作为参数传递给程序'bash'——这当然会导致bash运行它。但是,如果您设置“文件名”执行权限,并尝试通过例如“./文件名”运行它 - 那么您依赖于当前命令 shell 的默认“执行”行为 - 这可能需要 "#!(shell) " 脚本开始处的行,以便工作。
P
Peter Mortensen

我会建议:

#!/bin/bash

s1="hi"
s2="hi"

if [ $s1 = $s2 ]
then
  echo match
fi

没有双引号,只有一个等于。


是的,这是真的,我错过了空间。使用 "[ $s1 = $s2 ]" 它可以工作。
为什么要省略双引号?在这种有限的特定情况下,它们是可选的,但无害,但在许多实际情况下,删除它们将是一个严重的错误。另请参阅stackoverflow.com/questions/10067266/…
尝试使用 s1='*'s2='*',您会发现省略双引号是一个严重的错误。
无论引号如何,我都使用 = 或 == 得到相同的行为。我不确定这是否特定于 shell,我在我的 mac OS 版本上使用 zsh:10.15.7 (19H15)
q
qwerty
$ if [ "$s1" == "$s2" ]; then echo match; fi
match
$ test "s1" = "s2" ;echo match
match
$

在 Bash 中允许使用双等号,但在其他一些方言中则不允许。对于可移植性,应该首选单个等号,如果您只针对 Bash,则双括号 [[ 扩展在多功能性、健壮性和便利性方面会更加出色。
P
Peter Mortensen

我现在无法访问 Linux 机器,但 [ 实际上是一个程序(和内置 Bash),所以我认为您必须在 [ 和第一个参数之间放置一个空格。

另请注意,字符串相等运算符似乎是单个 =


符号 [ 曾经是指向 /bin/test 的链接(反之亦然)。在 ubuntu 16.04 上显然不再是这种情况了;不知道变化发生在何时何地。
P
Peter Mortensen

这更像是一个澄清而不是一个答案!是的,线索在错误消息中:

[嗨:找不到命令

这表明您的“hi”已连接到“[”。

与更传统的编程语言不同,在 Bash 中,“[”是一个命令,就像更明显的“ls”等一样——它没有因为它是一个符号而被特殊对待,因此“[”和(替换的)“$在您的问题中紧挨着的 s1" 被连接起来(对于 Bash 来说是正确的),然后它会尝试在该位置找到一个命令:[hi - 这是 Bash 未知的。

在 C 和其他一些语言中,“[”将被视为不同的“字符类”,并且与后面的“hi”不相交。

因此,您需要在开头的“[”之后有一个空格。


P
Peter Mortensen

利用:

#!/bin/bash

s1="hi"
s2="hi"

if [ "x$s1" == "x$s2" ]
then
  echo match
fi

在里面添加一个额外的字符串使它更安全。

您还可以对单行命令使用另一种表示法:

[ "x$s1" == "x$s2" ] && echo match

“更安全”是什么意思?为了完整和清晰起见,解释任何此类限定是很重要的。
事实上它并不安全,现在我知道如果你不引用它会更安全,这样可以防止其中一个为空时出现语法错误
P
Peter Mortensen

对于具有纯 Bash 且没有 test 但非常丑陋的版本,请尝试:

if ( exit "${s1/*$s2*/0}" )2>/dev/null
then
   echo match
fi

说明:在 ( ) 中打开了一个额外的子外壳。如果有匹配,它会以 0 退出,如果没有匹配会引发错误(丑陋),它会尝试以 $s1 退出。此错误指向 /dev/null


一点也不差。就像解释和 sed 正则表达式一样。我从来不需要那样使用 subshell。据我所知,您可以使用 command 或 $(command) 获得输出。我相信你可以测试它然后让它变得更好。
这在 s2 包含 glob 的情况下很容易被打破:要解决这个问题,您需要引用扩展 $s2: "${s1/*"$s2"*/0}"。但是还有其他一些无法修复的细微错误:例如,如果 s10 的列表:s1=000000; s2=some_other_stuff 将声明匹配。所以我强烈建议不要使用这种方法!另一个错误:s1=--; s2=stuff。从 bash 4.4 开始,s1=--help; s2=stuff 还会向标准输出发送垃圾邮件。