关于shell:在posix sh中如何判断一个字符串是否包含另一个字符串?

How do you tell if a string contains another string in POSIX sh?

本问题已经有最佳答案,请猛点这里访问。

我想编写一个unix shell脚本,如果另一个字符串中有一个字符串,它将执行各种逻辑。例如,如果我在某个文件夹中,请关闭分支。有人能告诉我怎么做吗?如果可能的话,我想让它不特定于shell(也就是说,不只是bash),但是如果没有其他方法,我可以用它来做。

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env sh

if ["$PWD" contains"String1" ]
then
    echo"String1 present"
elif ["$PWD" contains"String2" ]
then
    echo"String2 present"
else
    echo"Else"
fi


这里还有另一个解决方案。它使用了posix子字符串参数扩展,所以可以在bash、dash、ksh中使用。

1
test"${string#*$word}" !="$string" && echo"$word found in $string"

编辑:这是个好主意,C.罗斯。下面是一个功能化版本,其中包含一些示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# contains(string, substring)
#
# Returns 0 if the specified string contains the specified substring,
# otherwise returns 1.
contains() {
    string="$1"
    substring="$2"
    if test"${string#*$substring}" !="$string"
    then
        return 0    # $substring is in $string
    else
        return 1    # $substring is not in $string
    fi
}

contains"abcd""e" || echo"abcd does not contain e"
contains"abcd""ab" && echo"abcd contains ab"
contains"abcd""bc" && echo"abcd contains bc"
contains"abcd""cd" && echo"abcd contains cd"
contains"abcd""abcd" && echo"abcd contains abcd"
contains"""" && echo"empty string contains empty string"
contains"a""" && echo"a contains empty string"
contains"""a" || echo"empty string does not contain a"
contains"abcd efgh""cd ef" && echo"abcd efgh contains cd ef"
contains"abcd efgh""" && echo"abcd efgh contains a space"


纯POSIX外壳:

1
2
3
4
5
6
7
8
#!/bin/sh
CURRENT_DIR=`pwd`

case"$CURRENT_DIR" in
  *String1*) echo"String1 present" ;;
  *String2*) echo"String2 present" ;;
  *)         echo"else" ;;
esac

像ksh或bash这样的扩展shell具有奇特的匹配机制,但是旧式的case的功能却惊人地强大。


遗憾的是,我不知道在sh中实现这一点的方法。但是,使用bash(从3.0.0版开始,这可能是您所拥有的版本),您可以使用如下的=~操作符:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
CURRENT_DIR=`pwd`

if [["$CURRENT_DIR" =~"String1" ]]
then
 echo"String1 present"
elif [["$CURRENT_DIR" =~"String2" ]]
then
 echo"String2 present"
else
 echo"Else"
fi

作为额外的奖励(和/或警告,如果字符串中有任何有趣的字符),=~如果省略引号,则接受regex作为正确的操作数。


1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env sh

# Searches a subset string in a string:
# 1st arg:reference string
# 2nd arg:subset string to be matched

if echo"$1" | grep -q"$2"
then
    echo"$2 is in $1"
else
    echo"$2 is not in $1"
fi


这里有一个链接,指向您的问题的各种解决方案。

这是我最喜欢的,因为它让人最容易理解:

星型通配符方法

1
2
3
4
if [["$string" == *"$substring"* ]]; then
    return 1
fi
return 0


1
2
3
4
5
6
case $(pwd) in
  *path) echo"ends with path";;
  path*) echo"starts with path";;
  *path*) echo"contains path";;
  *) echo"this is the default";;
esac


有bash regexps。或者有‘expr’:

1
2
3
4
5
 if expr"$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname"$PRG"`/"$link"
  fi

在某些特殊情况下,如果您希望查找某个单词是否包含在长文本中,可以使用循环对长文本进行迭代。

1
2
3
4
5
6
7
8
9
found=F
query_word=this
long_string="many many words in this text"
for w in $long_string; do
    if ["$w" ="$query_word" ]; then
          found=T
          break
    fi
done

这是纯伯恩贝壳。


请参见"测试"程序的手册页。如果您只是测试目录的存在性,那么通常会这样做:

1
2
3
if test -d"String1"; then
  echo"String1 present"
end

如果您实际尝试匹配字符串,也可以使用bash扩展规则和通配符:

1
2
3
if test -d"String*"; then
  echo"A directory starting with 'String' is present"
end

如果你需要做一些更复杂的事情,你需要使用另一个程序,比如expr。


如果你想要一个只有ksh的方法,它和"test"一样快,你可以做如下的事情:

1
2
3
4
5
6
7
8
contains() # haystack needle
{
    haystack=${1/$2/}
    if [ ${#haystack} -ne ${#1} ] ; then
        return 1
    fi
    return 0
}

它的工作原理是删除草垛中的针,然后比较新旧草垛的串长。


1
test $(echo"stringcontain""ingcon" |awk '{ print index($1, $2) }') -gt 0 && echo"String 1 contain string 2"

-->output:字符串1包含字符串2