如何检查字符串是否在 Bash 中包含子字符串

我在 Bash 中有一个字符串:

string="My string"

如何测试它是否包含另一个字符串?

if [ $string ?? 'foo' ]; then
  echo "It's there!"
fi

哪里??是我未知的运算符。我是否使用 echo 和grep

if echo "$string" | grep 'foo'; then
  echo "It's there!"
fi

看起来有点笨拙。

答案

如果使用双括号,您也可以在案例语句之外使用Marcus 的答案(* 通配符)

string='My long string'
if [[ $string == *"My long"* ]]; then
  echo "It's there!"
fi

请注意,针线中的空格必须放在双引号之间,并且*通配符应位于外部。

如果您更喜欢正则表达式方法:

string='My string';

if [[ $string =~ "My" ]]
then
   echo "It's there!"
fi

我不确定要使用 if 语句,但是用 case 语句可以得到类似的效果:

case "$string" in 
  *foo*)
    # Do stuff
    ;;
esac

正如这些 Stack Overflow 答案主要介绍了Bash 一样 ,我在这篇文章的底部发布了一个不区分大小写的 Bash 函数。

反正有我的

兼容答案

由于使用 Bash 特定功能已经有了很多答案,因此有一种方法可以在功能较差的 shell 下工作,例如BusyBox

[ -z "${string##*$reqsubstr*}" ]

实际上,这可以使:

string='echo "My string"'
for reqsubstr in 'o "M' 'alt' 'str';do
  if [ -z "${string##*$reqsubstr*}" ] ;then
      echo "String '$string' contain substring: '$reqsubstr'."
    else
      echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
  done

在 Bash, DashKornShellksh )和ash (BusyBox)下进行了测试,结果始终是:

String 'echo "My string"' contain substring: 'o "M'.
String 'echo "My string"' don't contain substring: 'alt'.
String 'echo "My string"' contain substring: 'str'.

融入一项功能

正如 @EeroAaltonen 要求的,这是相同演示的版本,在相同的 shell 下进行了测试:

myfunc() {
    reqsubstr="$1"
    shift
    string="$@"
    if [ -z "${string##*$reqsubstr*}" ] ;then
        echo "String '$string' contain substring: '$reqsubstr'.";
      else
        echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
}

然后:

$ myfunc 'o "M' 'echo "My String"'
String 'echo "My String"' contain substring 'o "M'.

$ myfunc 'alt' 'echo "My String"'
String 'echo "My String"' don't contain substring 'alt'.

注意:您必须转义或双引号和 / 或双引号:

$ myfunc 'o "M' echo "My String"
String 'echo My String' don't contain substring: 'o "M'.

$ myfunc 'o "M' echo \"My String\"
String 'echo "My String"' contain substring: 'o "M'.

功能简单

这是在 BusyBox,Dash 和 Bash 下测试的:

stringContain() { [ -z "${2##*$1*}" ]; }

那是所有人!

那么现在:

$ if stringContain 'o "M3' 'echo "My String"';then echo yes;else echo no;fi
no
$ if stringContain 'o "M' 'echo "My String"';then echo yes;else echo no;fi
yes

... 或者,如 @Sjlver 指出的那样,如果提交的字符串可以为空,则该函数将变为:

stringContain() { [ -z "${2##*$1*}" ] && [ -z "$1" -o -n "$2" ]; }

或如AdrianGünter 的评论所建议,避免使用-o开关:

stringContain() { [ -z "${2##*$1*}" ] && { [ -z "$1" ] || [ -n "$2" ];};}

最终(简单)功能:

并反转测试以使其更快地变得可能:

stringContain() { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ];};}

使用空字符串:

$ if stringContain '' ''; then echo yes; else echo no; fi
yes
$ if stringContain 'o "M' ''; then echo yes; else echo no; fi
no

不区分大小写(仅限 Bash!)

对于不区分大小写的字符串测试,只需将每个字符串转换为小写即可:

stringContain() {
    local _lc=${2,,}
    [ -z "$1" ] || { [ -z "${_lc##*${1,,}*}" ] && [ -n "$2" ] ;} ;}

校验:

stringContain 'o "M3' 'echo "my string"' && echo yes || echo no
no
stringContain 'o "My' 'echo "my string"' && echo yes || echo no
yes
if stringContain '' ''; then echo yes; else echo no; fi
yes
if stringContain 'o "M' ''; then echo yes; else echo no; fi
no

您应该记住,shell 脚本不是一种语言,而是更多的命令集合。本能地认为,这种 “语言” 要求您在if加上[[[ 。这两个都是返回退出状态以指示成功或失败的命令(就像其他命令一样)。因此,我将使用grep ,而不使用[命令。

做就是了:

if grep -q foo <<<"$string"; then
    echo "It's there"
fi

现在,您正在考虑if测试其后的命令的退出状态(以分号完成),为什么不重新考虑要测试的字符串的来源呢?

## Instead of this
filetype="$(file -b "$1")"
if grep -q "tar archive" <<<"$filetype"; then
#...

## Simply do this
if file -b "$1" | grep -q "tar archive"; then
#...

-q选项使 grep 不输出任何内容,因为我们只需要返回代码。 <<<使 shell 扩展下一个单词并将其用作命令的输入,这是<<这里文档的单行版本(我不确定这是标准的还是 Bashism 的)。

公认的答案是最好的,但是由于有多种方法可以实现,因此这是另一种解决方案:

if [ "$string" != "${string/foo/}" ]; then
    echo "It's there!"
fi

${var/search/replace}$var ,如果找到了search的第一个实例,则将其替换为replace (如果找到)(它不会更改$var )。如果您尝试不使用foo替换foo ,并且字符串已更改,那么显然可以找到foo

因此,对于这个问题有很多有用的解决方案 - 但是哪一个最快 / 使用最少的资源?

使用此框架重复测试:

/usr/bin/time bash -c 'a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done'

每次更换测试:

[[ $b =~ $a ]]           2.92 user 0.06 system 0:02.99 elapsed 99% CPU

[ "${b/$a//}" = "$b" ]   3.16 user 0.07 system 0:03.25 elapsed 99% CPU

[[ $b == *$a* ]]         1.85 user 0.04 system 0:01.90 elapsed 99% CPU

case $b in *$a):;;esac   1.80 user 0.02 system 0:01.83 elapsed 99% CPU

doContain $a $b          4.27 user 0.11 system 0:04.41 elapsed 99%CPU

(doContain 在 F. Houri 的回答中)

对于傻笑:

echo $b|grep -q $a       12.68 user 30.86 system 3:42.40 elapsed 19% CPU !ouch!

因此,无论是在扩展测试中还是在案例中,简单的替换选项都可以预期获胜。手提箱是便携式的。

预测达到 100000 次抖动很痛苦!关于无需使用外部实用程序的旧规则仍然适用。

这也适用:

if printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  printf "Found needle in haystack"
fi

负面测试是:

if ! printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  echo "Did not find needle in haystack"
fi

我认为这种样式更经典 - 较少依赖 Bash shell 的功能。

--参数是纯 POSIX 偏执狂,用于防止类似于选项的输入字符串,例如--abc-a

注意:在一个紧凑的循环此代码会比使用内部击壳特征慢得多 ,因为一个(或两个)独立的过程将被创建并经由配管连接。

Bash 4 + 示例。注意:如果单词包含空格等,不使用引号会引起问题。请始终在 IMO 的 Bash 中引用。

以下是一些 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

示例 8,通配符匹配. ext(不区分大小写):

if echo "$a" | egrep -iq "\.(mp[3-4]|txt|css|jpg|png)" ; then

请享用。

这个怎么样:

text="   <tag>bmnmn</tag>  "
if [[ "$text" =~ "<tag>" ]]; then
   echo "matched"
else
   echo "not matched"
fi