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

我在Bash中有一个字符串:

string="My string"

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

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

其中??是我的未知运算符。我使用echogrep吗?

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

这看起来有点笨拙。

2815049 次浏览

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

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

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

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

请注意,针形字符串中的空格需要放在双引号之间,*通配符应该在外面。还要注意,使用了一个简单的比较运算符(即==),而不是正则表达式运算符=~

公认的答案是最好的,但由于有不止一种方法可以做到这一点,这里有另一个解决方案:

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

${var/search/replace}$varsearch的第一个实例被replace替换,如果它被找到(它没有改变$var)。如果您尝试用无替换foo,并且字符串发生了变化,那么显然找到了foo

如果您更喜欢regex方法:

string='My string';
if [[ $string =~ "My" ]]; thenecho "It's there!"fi

你应该记住,shell脚本与其说是一种语言,不如说是命令的集合。你本能地认为这种“语言”要求你在if后面跟着[[[。这两个都只是返回指示成功或失败的退出状态的命令(就像其他命令一样)。出于这个原因,我使用grep,而不是[命令。

只要做到:

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

既然您认为if是测试它后面的命令的退出状态(用分号完成),为什么不重新考虑您正在测试的字符串的来源呢?

## Instead of thisfiletype="$(file -b "$1")"if grep -q "tar archive" <<<"$filetype"; then#...
## Simply do thisif file -b "$1" | grep -q "tar archive"; then#...

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

grep -q在这方面很有用。

使用相同的awk

string="unix-bash 2389"character="@"printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

输出:

未找到

string="unix-bash 2389"character="-"printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

输出:

找到

来源:http://unstableme.blogspot.com/2008/06/bash-search-letter-in-string-awk.html

这个怎么样:

text="   <tag>bmnmn</tag>  "if [[ "$text" =~ "<tag>" ]]; thenecho "matched"elseecho "not matched"fi

试试oobash

它是Bash 4的OO风格的字符串库。它支持德语变音符号。它是用Bash编写的。

许多功能可供选择:-base64Decode-base64Encode-capitalize-center-charAt-concat-contains-count-endsWith-equals-base64Encode0、-base64Encode1、-base64Encode2、-base64Encode3、-base64Encode4、-base64Encode5、-base64Encode6、-base64Encode7、-base64Encode8、-base64Encode9、-capitalize0、-capitalize1、-capitalize2、-capitalize3、-capitalize4、-capitalize5、-capitalize6、-capitalize7、-capitalize8、-capitalize9、-center0、-center1、-center2、-center3、-center4、-center5、-center6和-center7。

看看包含的例子:

[Desktop]$ String a testXccc[Desktop]$ a.contains tXtrue[Desktop]$ a.contains XtXfalse

Obash在Sourceforge.net

一个是:

[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' ||  echo 'no'

我发现我经常需要这个功能,所以我在我的.bashrc中使用了一个自制的shell函数,这样我就可以根据需要经常重用它,并且有一个易于记忆的名称:

function stringinstring(){case "$2" in*"$1"*)return 0;;esacreturn 1}

要测试$string1(比如abc)是否包含在$string2(比如123abcABC)中,我只需要运行stringinstring "$string1" "$string2"并检查返回值,例如

stringinstring "$str1" "$str2"  &&  echo YES  ||  echo NO

这也有效:

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

阴性测试是:

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

我想这种风格更经典一点——不太依赖Bash shell的特性。

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

注意:在紧密循环中,此代码将比使用内部Bash shell功能慢,因为将创建一个(或两个)单独的进程并通过管道连接。

这个堆栈溢出的答案是唯一一个捕获空格和破折号字符的:

# For null cmd arguments checkingto_check=' -t'space_n_dash_chars=' -'[[ $to_check == *"$space_n_dash_chars"* ]] && echo found

stringContain变体(兼容或大小写无关)

由于这些Stack Overflow答案主要是关于Bash的,我在这篇文章的最底部发布了一个独立于大小写 Bash函数…

不管怎样,这是我的

相容的答案

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

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

在实践中,这可以:

string='echo "My string"'for reqsubstr in 'o "M' 'alt' 'str';doif [ -z "${string##*$reqsubstr*}" ] ;thenecho "String '$string' contain substring: '$reqsubstr'."elseecho "String '$string' don't contain substring: '$reqsubstr'."fidone

这是在Bash、DashKornShellksh)和灰烬(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"shiftstring="$@"if [ -z "${string##*$reqsubstr*}" ] ;thenecho "String '$string' contain substring: '$reqsubstr'.";elseecho "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;fino$ if stringContain 'o "M' 'echo "My String"';then echo yes;else echo no;fiyes

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

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

或者正如Adrian Gü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; fiyes$ if stringContain 'o "M' ''; then echo yes; else echo no; fino

案例独立(仅限Bash!)

对于不关心大小写的字符串测试,只需将每个字符串转换为小写:

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

检查:

stringContain 'o "M3' 'echo "my string"' && echo yes || echo nonostringContain 'o "My' 'echo "my string"' && echo yes || echo noyesif stringContain '' ''; then echo yes; else echo no; fiyesif stringContain 'o "M' ''; then echo yes; else echo no; fino

所以这个问题有很多有用的解决方案-但哪一个最快/使用最少的资源?

使用此框架重复测试:

/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

(在F. Houri的回答中包含)

至于傻笑:

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

因此,无论是在扩展测试中还是在案例中,简单替换选项都可预见地获胜。案例是可移植的。

输出到100000 greps是可以预见的痛苦!关于不需要使用外部实用程序的旧规则是正确的。

作为他的性能比较中的保罗提到

if echo "abcdefg" | grep -q "bcdef"; thenecho "String contains is true."elseecho "String contains is not true."fi

这是POSIX兼容的,就像'马库斯给出的答案中的'case"$string"一样,但它比case语句答案稍微容易阅读。还要注意,这比使用case语句慢得多。正如Paul指出的,不要在循环中使用它。

[[ $string == *foo* ]] && echo "It's there" || echo "Couldn't find"

我喜欢Sed

substr="foo"nonsub="$(echo "$string" | sed "s/$substr//")"hassub=0 ; [ "$string" != "$nonsub" ] && hassub=1

编辑,逻辑:

  • 使用ed从string中删除substring的实例

  • 如果新字符串与旧字符串不同,则子字符串存在

准确的单词匹配:

string='My long string'exactSearch='long'
if grep -E -q "\b${exactSearch}\b" <<<${string} >/dev/null 2>&1thenecho "It's there"fi

我的.bash_profile文件以及我如何使用grep:

如果PATH环境变量包含我的两个bin目录,请不要追加它们,

# .bash_profile# Get the aliases and functionsif [ -f ~/.bashrc ]; then. ~/.bashrcfi
U=~/.local.bin:~/bin
if ! echo "$PATH" | grep -q "home"; thenexport PATH=$PATH:${U}fi

我使用这个函数(一个不包括但明显的依赖项)。它通过了下面显示的测试。如果函数返回值>0,则找到字符串。您可以轻松返回1或0。

function str_instr {# Return position of ```str``` within ```string```.# >>> str_instr "str" "string"# str: String to search for.# string: String to search.typeset str string x# Behavior here is not the same in bash vs ksh unless we escape special characters.str="$(str_escape_special_characters "${1}")"string="${2}"x="${string%%$str*}"if [[ "${x}" != "${string}" ]]; thenecho "${#x} + 1" | bc -lelseecho 0fi}
function test_str_instr {str_instr "(" "'foo@host (dev,web)'" | assert_eq 11str_instr ")" "'foo@host (dev,web)'" | assert_eq 19str_instr "[" "'foo@host [dev,web]'" | assert_eq 11str_instr "]" "'foo@host [dev,web]'" | assert_eq 19str_instr "a" "abc" | assert_eq 1str_instr "z" "abc" | assert_eq 0str_instr "Eggs" "Green Eggs And Ham" | assert_eq 7str_instr "a" "" | assert_eq 0str_instr "" "" | assert_eq 0str_instr " " "Green Eggs" | assert_eq 6str_instr " " " Green "  | assert_eq 1}

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

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

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

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

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

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

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

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

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

     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

示例9,在区分大小写的字符串上使用grep:

     if echo "SomeString" | grep -q "String"; then

示例10,在不区分大小写的字符串上使用grep:

     if echo "SomeString" | grep -iq "string"; then

示例11,在不区分大小写的字符串w/通配符上使用grep:

     if echo "SomeString" | grep -iq "Some.*ing"; then

示例12,使用双哈希进行比较(如果变量为空可能导致假正则等)(区分大小写):

     if [[ ! ${str##*$substr*} ]] ;then  #found

好好享受。

这里回答的问题的扩展如何判断一个字符串是否包含POSIX sh中的另一个字符串?

此解决方案适用于特殊字符:

# contains(string, substring)## Returns 0 if the specified string contains the specified substring,# otherwise returns 1.contains() {string="$1"substring="$2"
if echo "$string" | $(type -p ggrep grep | head -1) -F -- "$substring" >/dev/null; thenreturn 0    # $substring is in $stringelsereturn 1    # $substring is not in $stringfi}
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"
contains "abcd [efg] hij" "[efg]" && echo "abcd [efg] hij contains [efg]"contains "abcd [efg] hij" "[effg]" || echo "abcd [efg] hij does not contain [effg]"
contains "abcd *efg* hij" "*efg*" && echo "abcd *efg* hij contains *efg*"contains "abcd *efg* hij" "d *efg* h" && echo "abcd *efg* hij contains d *efg* h"contains "abcd *efg* hij" "*effg*" || echo "abcd *efg* hij does not contain *effg*"

由于POSIX/BusyBox问题在没有提供正确答案的情况下被关闭(IMHO),我将在这里发布答案。

最简短的答案是:

[ ${_string_##*$_substring_*} ] || echo Substring found!

[ "${_string_##*$_substring_*}" ] || echo 'Substring found!'

请注意,双哈希义务,带有一些shell(ash)。当找不到子字符串时,上面将评估[ stringvalue ]。它不返回错误。当找到子字符串时,结果为空,它评估[ ]。这将抛出错误代码1,因为字符串被完全替换(由于*)。

最常用的语法:

[ -z "${_string_##*$_substring_*}" ] && echo 'Substring found!'

[ -n "${_string_##*$_substring_*}" ] || echo 'Substring found!'

另一个:

[ "${_string_##$_substring_}" != "$_string_" ] && echo 'Substring found!'

[ "${_string_##$_substring_}" = "$_string_" ] || echo 'Substring found!'

注意单一等号!

msg="message"
function check {echo $msg | egrep [abc] 1> /dev/null
if [ $? -ne 1 ];thenecho "found"elseecho "not found"fi}
check

这将发现a或b或c的任何出现

泛型针干草堆示例接下来是变量

#!/bin/bash
needle="a_needle"haystack="a_needle another_needle a_third_needle"if [[ $haystack == *"$needle"* ]]; thenecho "needle found"elseecho "needle NOT found"fi
case $string in (*foo*)# Do stuffesac

这与https://stackoverflow.com/a/229585/11267590的答案相同。但风格简单且符合POSIX。

接受的答案是正确的,但很难阅读和理解。
对于与搜索相关的问题,您应该始终使用大海捞针习语。
建议编辑队列已满开始,我发布了这个:

haystack='There are needles here.'if [[ "$haystack" == *"needle"* ]]; thenecho "It's there!"fi

您可以使用逻辑&&来更紧凑

#!/bin/bash
# NO MATCH EXAMPLEstring="test"[[ "$string" == *"foo"* ]] && {echo "YES"}
# MATCH EXAMPLEstring="tefoost"[[ "$string" == *"foo"* ]] && {echo "YES"}

使用jq

string='My long string'echo $string | jq -Rr 'select(contains("long"))|"It is there"'

在jq中最难的事情是打印单引号:

echo $string | jq --arg quote "'" -Rr 'select(contains("long"))|"It\($quote)s there"'

使用jq只是为了检查条件:

if jq -Re 'select(contains("long"))|halt' <<< $string; thenecho "It's there!"fi