我在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
看起来有点笨拙。
如果您使用双方括号,您也可以在case语句之外使用Marcus的回答(*通配符):
string='My long string'
if [[ $string == *"My long"* ]]; then
echo "It's there!"
fi
请注意,针对搜索字符串中的空格需要放在双引号之间,而 *
通配符应该位于外部。还要注意使用简单比较运算符(即 ==
),而不是正则表达式运算符 =~
。
#!/bin/sh
。请尝试改为使用#!/bin/bash
。 - Dennis Williamson#!/bin/bash
,为了可移植性,你应该使用 #!/usr/bin/env bash
。 - Swivel[
运算符一起使用(双括号 [[
与通配符可能是必需的)。 - Hakimsh
一起使用。 - t7e如果您更喜欢正则表达式的方法:
string='My string';
if [[ $string =~ "My" ]]; then
echo "It's there!"
fi
=~
运算符已经在整个字符串中搜索匹配项;这里的 .*
是多余的。此外,引号通常比反斜杠更可取:[[ $string =~ "My s" ]]
。 - bukzorre="My s"; if [[ $string =~ $re ]]
。参考链接为:http://tiswww.case.edu/php/chet/bash/FAQ E14
。 - seanfif [[ ! "abc" =~ "d" ]]
的值为真。 - KrisWebDev我不确定是否使用if语句,但您可以通过使用case语句获得类似的效果:
case "$string" in
*foo*)
# Do stuff
;;
esac
/usr/xpg4/bin/sh
和ksh(>= 88)中,[[ $string == *foo* ]]
也可以工作。 - maxschlepzig[[ $string == *foo* ]]
这样的命令将无法运行。 - t7estringContain
变体(兼容或不区分大小写)由于这些 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
ksh
)和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" ]; }
或者按照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; fi
yes
$ if stringContain 'o "M' ''; then echo yes; else echo no; fi
no
要测试字符串而不考虑大小写,只需将每个字符串转换为小写:
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
string_contains() { [ -z "${2##*$1*}" ] && [ -n "$2" -o -z "$1" ]; }
最后思考一下:空字符串是否包含空字符串?上面的版本认为是(因为有-o -z "$1"
这部分代码)。 - Sjlver/proc/cpuinfo
中的processor
),请参见此答案以从命令行获取Linux中的CPU /核心数量? - F. Hauri - Give Up GitHub请记住,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 扩展下一个单词并将其用作命令的输入,这是 <<
here 文档的一行版本(我不确定这是否是标准或 Bashism)。
echo
本身是非常可移植的。但是标志不是。如果你发现自己在考虑-e
或-n
,请使用printf
。 - Bruno Bronoskygrep -q foo <<<"$mystring"
意味着要进行1次 fork,并且是* bashism *,而 echo $mystring | grep -q foo
意味着要进行2次 fork(一次用于管道,第二次用于运行 /path/to/grep
)。 - F. Hauri - Give Up GitHubecho
可能仍然存在意想不到的可移植性问题。例如,在某些平台上,echo "nope\c"
的预期行为类似于在其他一些平台上使用 echo -e "nope"
。printf '%s' "nope"
与 printf '%s\n' 'nope\c'
。 - tripleee最佳答案已经被接受,但由于有多种方法可以实现它,这里提供另一种解决方案:
if [ "$string" != "${string/foo/}" ]; then
echo "It's there!"
fi
${var/search/replace}
是将 $var
中第一个出现的 search
替换为 replace
,如果找到的话(它不会更改 $var
)。 如果你尝试用空字符串替换 foo
,并且字符串已经改变,那么显然是找到了 foo
。
if [ "$string" != "${string/foo/}" ]; then echo "It's there!" fi
非常有用。由于一些bash正则表达式未实现,所以接受的解决方案在BusyBox中无法工作。请注意,此翻译不包括任何解释性内容。
- TPoschel$XDG_CURRENT_DESKTOP
与 $string
进行比较。你想要的表达式是 if [ "$XDG_CURRENT_DESKTOP" != "${XDG_CURRENT_DESKTOP/GNOME/}" ]; then echo MATCHES GNOME; fi
。 - Todd Lewis"x$string" != "x${string/foo/}"
更好。 - tomo_iris427因此,对于这个问题有很多有用的解决方案 - 但哪个是最快的/使用最少的资源?
使用这个框架进行反复测试:
/usr/bin/time bash -c 'a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done'
每次替换TEST:
[[ $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个grep肯定是令人痛苦的!有关不必要使用外部工具的旧规则仍然正确。
[[ $b == *$a* ]]
。 - Mad Physicistcase
的总时间消耗最小。不过,在 $b in *$a
后面你漏掉了一个星号。当修复了这个 bug 之后,我得到的 [[ $b == *$a* ]]
的结果比 case
稍微快一些,但当然也可能取决于其他因素。 - tripleee[[ $b == *$a* ]]
很快,而 case
几乎一样快(并且令人愉悦地符合 POSIX 标准)。 - tripleee[[ $b == *$a* ]]
和case语句case $b in *$a):;;esac
在无匹配条件下是不等价的。交换$a
和$b
会导致条件表达式[[
的退出代码为1,而case
语句的退出代码为0。根据help case
:退出状态:返回最后一个执行的命令的状态。如果没有匹配到任何模式,则返回状态为零,这可能不是预期的行为。为了在无匹配条件下返回1,应该使用以下语句:case $b in *$a*):;; *) false ;; esac
。 - r aBash 4+示例。注意:在单词包含空格等情况下不使用引号会导致问题。在Bash中始终使用引号,这是我的建议。
以下是一些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,对带通配符的字符串使用不区分大小写的grep:
if echo "SomeString" | grep -iq "Some.*ing"; then
示例12,使用doublehash进行比较(如果变量为空可能会导致假阳性等情况)(区分大小写):
if [[ ! ${str##*$substr*} ]] ;then #found
享受。
${str,,}
中的两个逗号将 $str
转换为小写字母,我才理解它。伟大的解决方案/伟大的列表! - hey${str}
?${$MYVAR,,}
有效吗?Bash 显示 bad substitution
。 - Sam Sirry${str}
中的str
是一个变量名而不是字面字符串。这解决了我的问题 :) - Sam Sirry这个也可以工作:
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 shell特性慢得多,因为将创建一个(或两个)单独的进程,并通过管道连接。
echo
输出除了 -
以外的字面文本或带转义的文本。尽管它可能适用于你的情况,但它并不可移植。甚至在 Bash 中,echo
命令的行为也会根据是否设置了 xpg_echo
选项而有所不同。附言: 我之前的评论忘记关闭双引号了。 - alexia--
在 POSIX spec for printf
中没有列出,但是你应该始终使用 printf '%s' "$anything"
,以避免 $anything
包含 %
字符时出现问题。 - alexiaif echo "abcdefg" | grep -q "bcdef"; then
echo "String contains is true."
else
echo "String contains is not true."
fi
这个类似于 POSIX 标准的 'case "$string" in' Marcus 给出的答案,但比 case 语句的答案更易读。同时请注意,这种方法比使用 case 语句慢得多得多。正如 Paul 指出的那样,请勿在循环中使用。
expr
命令。 - cifer