提问人:davidsheldon 提问时间:10/23/2008 最后编辑:Andrzej Sydordavidsheldon 更新时间:8/17/2023 访问量:3021973
如何在 Bash 中检查字符串是否包含子字符串
How to check if a string contains a substring in 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
这看起来有点笨拙。
答:
我不确定是否使用 if 语句,但您可以使用 case 语句获得类似的效果:
case "$string" in
*foo*)
# Do stuff
;;
esac
评论
[[ $string == *foo* ]]
/usr/xpg4/bin/sh
[[ $string == *foo* ]]
如果您使用双括号,您也可以在 case 语句之外使用 Marcus 的答案(* 通配符):
string='My long string'
if [[ $string == *"My long"* ]]; then
echo "It's there!"
fi
请注意,针串中的空格需要放在双引号之间,通配符应该在外面。另请注意,使用的是简单的比较运算符(即 ),而不是正则表达式运算符。*
==
=~
评论
[[: not found
#!/bin/sh
#!/bin/bash
[
[[
sh
公认的答案是最好的,但由于有多种方法可以做到这一点,这里有另一种解决方案:
if [ "$string" != "${string/foo/}" ]; then
echo "It's there!"
fi
${var/search/replace}
是替换为 的第一个实例,如果找到它(它不会改变)。如果您尝试不进行任何替换,并且字符串已更改,则显然已找到。$var
search
replace
$var
foo
foo
评论
$XDG_CURRENT_DESKTOP
$string
if [ "$XDG_CURRENT_DESKTOP" != "${XDG_CURRENT_DESKTOP/GNOME/}" ]; then echo MATCHES GNOME; fi
"x$string" != "x${string/foo/}"
如果您更喜欢正则表达式方法:
string='My string';
if [[ $string =~ "My" ]]; then
echo "It's there!"
fi
评论
=~
.*
[[ $string =~ "My s" ]]
E14)
re="My s"; if [[ $string =~ $re ]]
if [[ ! "abc" =~ "d" ]]
You should remember that shell scripting is less of a language and more of a collection of commands. Instinctively you think that this "language" requires you to follow an with a or a . Both of those are just commands that return an exit status indicating success or failure (just like every other command). For that reason I'd use , and not the command.if
[
[[
grep
[
Just do:
if grep -q foo <<<"$string"; then
echo "It's there"
fi
Now that you are thinking of as testing the exit status of the command that follows it (complete with semi-colon), why not reconsider the source of the string you are testing?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
#...
The option makes grep not output anything, as we only want the return code. makes the shell expand the next word and use it as the input to the command, a one-line version of the here document (I'm not sure whether this is standard or a Bashism).-q
<<<
<<
评论
if grep -q foo <(echo somefoothing); then
echo
-e
-n
printf
grep -q foo <<<"$mystring"
echo $mystring | grep -q foo
/path/to/grep
)
echo
echo "nope\c"
echo -e "nope"
printf '%s' "nope"
printf '%s\n' 'nope\c'
grep -q
is useful for this purpose.
The same using :awk
string="unix-bash 2389"
character="@"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'
Output:
Not Found
string="unix-bash 2389"
character="-"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'
Output:
Found
Original source: http://unstableme.blogspot.com/2008/06/bash-search-letter-in-string-awk.html
评论
echo
is unportable, you should be using instead. I'm editing the answer because the user doesn't appear to exist anymore.printf '%s' "$string"
How about this:
text=" <tag>bmnmn</tag> "
if [[ "$text" =~ "<tag>" ]]; then
echo "matched"
else
echo "not matched"
fi
评论
Try oobash.
It is an OO-style string library for Bash 4. It has support for German umlauts. It is written in Bash.
Many functions are available:
base64Decode
base64Encode
capitalize
center
charAt
concat
contains
count
endsWith
equals
equalsIgnoreCase
reverse
hashCode
indexOf
isAlnum
isAlpha
isAscii
isDigit
isEmpty
isHexDigit
isLowerCase
isSpace
isPrintable
isUpperCase
isVisible
lastIndexOf
length
matches
replaceAll
replaceFirst
startsWith
substring
swapCase
toLowerCase
toString
toUpperCase
trim
zfill
请看 contains 示例:
[Desktop]$ String a testXccc
[Desktop]$ a.contains tX
true
[Desktop]$ a.contains XtX
false
oobash 可在 Sourceforge.net 上使用。
一是:
[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' || echo 'no'
评论
expr
是那些瑞士军刀实用程序之一,一旦你弄清楚了如何去做,通常可以做任何你需要做的事情,但一旦实施,你永远不记得它为什么或如何做它正在做的事情,所以你再也不会碰它,并希望它永远不会停止做它正在做的事情。
expr
expr
expr
expr
test
test
expr
expr: syntax error: unexpected argument ‘.*.*’
bash: [: -ne: unary operator expected
我发现经常需要这个功能,所以我在我的 like this 中使用了一个自制的 shell 函数,它允许我根据需要经常重用它,并带有一个易于记忆的名称:.bashrc
function stringinstring()
{
case "$2" in
*"$1"*)
return 0
;;
esac
return 1
}
例如,要测试(比如,abc)是否包含在(比如,123abcABC)中,我只需要运行并检查返回值$string1
$string2
stringinstring "$string1" "$string2"
stringinstring "$str1" "$str2" && echo YES || echo NO
评论
x
strstr()
这也有效:
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
是不可移植的,您应该改用。printf '%s' "$haystack
echo
-
echo
xpg_echo
printf
的 POSIX 规范中列出,但您无论如何都应该使用,以避免包含字符时出现问题。--
printf '%s' "$anything"
$anything
%
这个 Stack Overflow 答案是唯一一个捕获空格和破折号字符的答案:
# For null cmd arguments checking
to_check=' -t'
space_n_dash_chars=' -'
[[ $to_check == *"$space_n_dash_chars"* ]] && echo found
评论
全面重写 2023-07-03!!
字符串包含:POSIX 兼容性、大小写无关、提示和备注。bash
介绍
之前的答案是基于参数扩展的,但是在与基于案例的解决方案进行比较后,正如Marcus Griep的答案所提出的那样,我不得不承认:案例方法的效率要高得多!
简述必不可少
case $string in
*$substring* )
do something with "$substring"
;;
esac
As a function:
stringContain() { case $2 in *$1* ) return 0;; *) return 1;; esac ;}
Usage sample
for string in 'echo "My string"' "Don't miss quotes" ''; do # 3 strings
for substr in "'t mis" 'o "My' "s" "Y" ""; do # 5 substrings
if stringContain "$substr" "$string"; then
printf 'Match: %-12s %s\n' "'$substr'" "'$string'"
else
printf 'No match: %s\n' "'$substr'"
fi
done
done
No match: ''t mis'
Match: 'o "My' 'echo "My string"'
Match: 's' 'echo "My string"'
No match: 'Y'
Match: '' 'echo "My string"'
Match: ''t mis' 'Don't miss quotes'
No match: 'o "My'
Match: 's' 'Don't miss quotes'
No match: 'Y'
Match: '' 'Don't miss quotes'
No match: ''t mis'
No match: 'o "My'
No match: 's'
No match: 'Y'
Match: '' ''
Alternative using parameter expansion
In previous answer I'd proposed:
stringContain() { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ];};}
But after doing some comparisons, using , , and , here is my average result:dash
busybox shell
bash
ksh
Comparing time PExp vs Case method under bash : 634.71%
Comparing time PExp vs Case method under dash : 878.87%
Comparing time PExp vs Case method under ksh : 217.95%
Comparing time PExp vs Case method under busybox : 752.42%
Full test script: stringContain-test.sh
case
method is at least 2 time quicker thanparameter expansion
method regardless shell implementation used.
Semantically:
- case method: in case string match anything (could be nothing), followed by substring, followed by anything. is a single test.
- parameter expansion: If substring is empty or string where anything followed by substring followed by anything is replaced by nothing is nothing and string do contain something is a complex multiple test after string transformation
From this point of view, this seem easy to understand that case
method is more efficient!
Case independent
Under bash and some other shell, you could use parameter expansion to quickly transform your string to lower or upper case, by using respectively: and :${var,,}
${var^^}
So adding option to function, for case independent, could be done by:-i
stringContain() {
if [[ $1 == -i ]] ; then
case ${3,,} in
*${2,,}*) return 0;;
*) return 1;;
esac
else
case $2 in
*$1*) return 0;;
*) return 1;;
esac
fi
}
stringContain hello 'Hello world!' && echo yes || echo no
no
stringContain -i hello 'Hello world!' && echo yes || echo no
yes
评论
string_contains() { [ -z "${2##*$1*}" ] && [ -n "$2" -o -z "$1" ]; }
-o -z "$1"
So there are lots of useful solutions to the question - but which is fastest / uses the fewest resources?
Repeated tests using this frame:
/usr/bin/time bash -c 'a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done'
Replacing TEST each time:
[[ $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 was in F. Houri's answer)
And for giggles:
echo $b|grep -q $a 12.68 user 30.86 system 3:42.40 elapsed 19% CPU !ouch!
So the simple substitution option predictably wins whether in an extended test or a case. The case is portable.
Piping out to 100000 greps is predictably painful! The old rule about using external utilities without need holds true.
评论
[[ $b == *$a* ]]
case
$b in *$a
[[ $b == *$a* ]]
case
[[ $b == *$a* ]]
case
[[ $b == *$a* ]]
case $b in *$a):;;esac
$a
$b
[[
case
help case
case $b in *$a*):;; *) false ;; esac
As Paul mentioned in his performance comparison:
if echo "abcdefg" | grep -q "bcdef"; then
echo "String contains is true."
else
echo "String contains is not true."
fi
This is POSIX compliant like the 'case "$string" in' the answer provided by Marcus, but it is slightly easier to read than the case statement answer. Also note that this will be much much slower than using a case statement. As Paul pointed out, don't use it in a loop.
评论
[[ $string == *foo* ]] && echo "It's there" || echo "Couldn't find"
评论
echo "Couldn't find
|| echo "Couldn't find"
I like sed.
substr="foo"
nonsub="$(echo "$string" | sed "s/$substr//")"
hassub=0 ; [ "$string" != "$nonsub" ] && hassub=1
Edit, Logic:
Use sed to remove instance of substring from string
If new string differs from old string, substring exists
Exact word match:
string='My long string'
exactSearch='long'
if grep -E -q "\b${exactSearch}\b" <<<${string} >/dev/null 2>&1
then
echo "It's there"
fi
My .bash_profile file and how I used grep:
If the PATH environment variable includes my two directories, don't append them,bin
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
U=~/.local.bin:~/bin
if ! echo "$PATH" | grep -q "home"; then
export PATH=$PATH:${U}
fi
评论
grep -q -E 'pattern1|...|patternN'
我使用这个函数(一个依赖项不包括在内,但很明显)。它通过了如下所示的测试。如果函数返回的值> 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}" ]]; then
echo "${#x} + 1" | bc -l
else
echo 0
fi
}
function test_str_instr {
str_instr "(" "'foo@host (dev,web)'" | assert_eq 11
str_instr ")" "'foo@host (dev,web)'" | assert_eq 19
str_instr "[" "'foo@host [dev,web]'" | assert_eq 11
str_instr "]" "'foo@host [dev,web]'" | assert_eq 19
str_instr "a" "abc" | assert_eq 1
str_instr "z" "abc" | assert_eq 0
str_instr "Eggs" "Green Eggs And Ham" | assert_eq 7
str_instr "a" "" | assert_eq 0
str_instr "" "" | assert_eq 0
str_instr " " "Green Eggs" | assert_eq 6
str_instr " " " Green " | assert_eq 1
}
评论
str_escape_special_characters
似乎已经变成了.请参阅 @ ArcLogicSoftware/Arcshellstr_escape
arcshell_str.sh
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,在字符串上使用 grep 不区分大小写,带通配符:
if echo "SomeString" | grep -iq "Some.*ing"; then
示例 12,使用 doublehash 进行比较(如果变量 empty 可能导致错误正语等)(区分大小写):
if [[ ! ${str##*$substr*} ]] ;then #found
享受。
评论
${str,,}
$str
${str}
${$MYVAR,,}
bad substitution
这里回答的问题的扩展 如何判断一个字符串是否包含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; 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"
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*"
评论
contains "-n" "n"
echo -n
-n
printf "%s\n" "$string"
Since the POSIX/BusyBox question is closed without providing the right answer (IMHO), I'll post an answer here.
The shortest possible answer is:
[ ${_string_##*$_substring_*} ] || echo Substring found!
or
[ "${_string_##*$_substring_*}" ] || echo 'Substring found!'
Note that the double hash is obligatory with some shells (). Above will evaluate when the substring is not found. It returns no error. When the substring is found the result is empty and it evaluates . This will throw error code 1 since the string is completely substituted (due to ).ash
[ stringvalue ]
[ ]
*
The shortest more common syntax:
[ -z "${_string_##*$_substring_*}" ] && echo 'Substring found!'
or
[ -n "${_string_##*$_substring_*}" ] || echo 'Substring found!'
Another one:
[ "${_string_##$_substring_}" != "$_string_" ] && echo 'Substring found!'
or
[ "${_string_##$_substring_}" = "$_string_" ] || echo 'Substring found!'
Note the single equal sign!
msg="message"
function check {
echo $msg | egrep [abc] 1> /dev/null
if [ $? -ne 1 ];
then
echo "found"
else
echo "not found"
fi
}
check
This will find any occurance of a or b or c
The generic needle haystack example is following with variables
#!/bin/bash
needle="a_needle"
haystack="a_needle another_needle a_third_needle"
if [[ $haystack == *"$needle"* ]]; then
echo "needle found"
else
echo "needle NOT found"
fi
case $string in (*foo*)
# Do stuff
esac
This is the same answer as https://stackoverflow.com/a/229585/11267590. But simple style and also POSIX Compliant.
Accepted answer is correct but it is hard to read and understand.
For problems related to searching you should always use the $needle in a $haystack idiom.
Since its suggested edit queue is full, I post this:
haystack='There are needles here.'
if [[ "$haystack" == *"needle"* ]]; then
echo "It's there!"
fi
评论
bash
#!/bin/bash
You can use a logic && to be more compact
#!/bin/bash
# NO MATCH EXAMPLE
string="test"
[[ "$string" == *"foo"* ]] && {
echo "YES"
}
# MATCH EXAMPLE
string="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; then
echo "It's there!"
fi
这是 POSIX 变体,但具有:sed
string="My string"
pattern="string"
if [ "${string}" != "$(printf '%s' "${string}" | sed 's/'"${pattern}"'//g')" ]; then
echo "It's there!";
fi
一些解释:
sed 's/'"${pattern}"'//g'
去除 的模式。
因此,它看起来像这样:${string}
if [ "My string" != "My " ];
它们不相等,而是 ,所以这意味着模式就在那里。true
如果使用不同的模式,例如 ,则等式为:pattern="foo"
if [ "My string" != "My string" ];
因为在这种情况下不会剥离任何东西,它会产生.sed
false
它看起来仍然很笨拙,但此选项适用于许多 shell,例如 dash、zsh,而不仅仅是 bash。
评论
expr