[英]In Bash, how can I check if a string begins with some value?
I would like to check if a string begins with "node" eg "node001".我想检查一个字符串是否以“node”开头,例如“node001”。 Something like
就像是
if [ $HOST == user* ]
then
echo yes
fi
How can I do it correctly?我怎样才能正确地做到这一点?
I further need to combine expressions to check if HOST is either "user1" or begins with "node"我还需要结合表达式来检查 HOST 是“user1”还是以“node”开头
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi
> > > -bash: [: too many arguments
How can I do it correctly?我怎样才能正确地做到这一点?
This snippet on the Advanced Bash Scripting Guide says: Advanced Bash Scripting Guide上的这个片段说:
# The == comparison operator behaves differently within a double-brackets
# test than within single brackets.
[[ $a == z* ]] # True if $a starts with a "z" (wildcard matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
So you had it nearly correct;所以你几乎是正确的; you needed double brackets, not single brackets.
你需要双括号,而不是单括号。
With regards to your second question, you can write it this way:关于你的第二个问题,你可以这样写:
HOST=user1
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
echo yes1
fi
HOST=node001
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
echo yes2
fi
Which will echo哪个会回响
yes1
yes2
Bash's if
syntax is hard to get used to (IMO). Bash 的
if
语法很难习惯(IMO)。
If you're using a recent version of Bash (v3+), I suggest the Bash regex comparison operator =~
, for example,如果您使用的是最新版本的 Bash (v3+),我建议使用 Bash 正则表达式比较运算符
=~
,例如,
if [[ "$HOST" =~ ^user.* ]]; then
echo "yes"
fi
To match this or that
in a regex, use |
要在正则表达式中匹配
this or that
,请使用|
, for example, , 例如,
if [[ "$HOST" =~ ^user.*|^host1 ]]; then
echo "yes"
fi
Note - this is 'proper' regular expression syntax.注意 - 这是“正确的”正则表达式语法。
user*
means use
and zero-or-more occurrences of r
, so use
and userrrr
will match. user*
表示use
和r
的零次或多次出现,因此use
和userrrr
将匹配。user.*
means user
and zero-or-more occurrences of any character, so user1
, userX
will match. user.*
表示user
和零次或多次出现的任何字符,因此user1
, userX
将匹配。^user.*
means match the pattern user.*
at the begin of $HOST. ^user.*
表示匹配 $HOST 开头的模式user.*
。 If you're not familiar with regular expression syntax, try referring to this resource .如果您不熟悉正则表达式语法,请尝试参考此资源。
Note that the Bash =~
operator only does regular expression matching when the right hand side is UNQUOTED .请注意,Bash
=~
运算符仅在右侧为UNQUOTED时进行正则表达式匹配。 If you do quote the right hand side, "any part of the pattern may be quoted to force it to be matched as a string.".如果您确实引用了右侧,则“可以引用模式的任何部分以强制将其作为字符串匹配。”。 You should not quote the right hand side even when doing parameter expansion.
即使在进行参数扩展时,也不应引用右侧。
I always try to stick with POSIX sh
instead of using Bash extensions, since one of the major points of scripting is portability (besides connecting programs, not replacing them).我总是尝试坚持使用 POSIX
sh
而不是使用 Bash 扩展,因为脚本的要点之一是可移植性(除了连接程序,而不是替换它们)。
In sh
, there is an easy way to check for an "is-prefix" condition.在
sh
中,有一种简单的方法可以检查“is-prefix”条件。
case $HOST in node*)
# Your code here
esac
Given how old, arcane and crufty sh is (and Bash is not the cure: It's more complicated, less consistent and less portable), I'd like to point out a very nice functional aspect: While some syntax elements like case
are built-in, the resulting constructs are no different than any other job.考虑到 sh 有多古老、晦涩难懂(而且 Bash 不是灵丹妙药:它更复杂、更不连贯且更不便携),我想指出一个非常好的功能方面:虽然一些语法元素(如
case
是内置的)在,由此产生的构造与任何其他工作没有什么不同。 They can be composed in the same way:它们可以用相同的方式组成:
if case $HOST in node*) true;; *) false;; esac; then
# Your code here
fi
Or even shorter甚至更短
if case $HOST in node*) ;; *) false;; esac; then
# Your code here
fi
Or even shorter (just to present !
as a language element -- but this is bad style now)甚至更短(只是为了呈现
!
作为语言元素——但现在这是不好的风格)
if ! case $HOST in node*) false;; esac; then
# Your code here
fi
If you like being explicit, build your own language element:如果您喜欢明确,请构建自己的语言元素:
beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
Isn't this actually quite nice?这真的不是很好吗?
if beginswith node "$HOST"; then
# Your code here
fi
And since sh
is basically only jobs and string-lists (and internally processes, out of which jobs are composed), we can now even do some light functional programming:而且由于
sh
基本上只是作业和字符串列表(以及内部进程,其中作业组成),我们现在甚至可以进行一些轻量级的函数式编程:
beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
checkresult() { if [ $? = 0 ]; then echo TRUE; else echo FALSE; fi; }
all() {
test=$1; shift
for i in "$@"; do
$test "$i" || return
done
}
all "beginswith x" x xy xyz ; checkresult # Prints TRUE
all "beginswith x" x xy abc ; checkresult # Prints FALSE
This is elegant.这是优雅的。 Not that I'd advocate using
sh
for anything serious -- it breaks all too quickly on real world requirements (no lambdas, so we must use strings. But nesting function calls with strings is not possible, pipes are not possible, etc.)并不是说我主张将
sh
用于任何严重的事情——它在现实世界的要求上太快了(没有 lambda,所以我们必须使用字符串。但是用字符串嵌套函数调用是不可能的,管道是不可能的,等等)
I prefer the other methods already posted, but some people like to use:我更喜欢已经发布的其他方法,但有些人喜欢使用:
case "$HOST" in
user1|node*)
echo "yes";;
*)
echo "no";;
esac
Edit:编辑:
I've added your alternates to the case statement above我已将您的替代者添加到上面的案例陈述中
In your edited version you have too many brackets.在您编辑的版本中,括号太多。 It should look like this:
它应该如下所示:
if [[ $HOST == user1 || $HOST == node* ]];
While I find most answers here quite correct, many of them contain unnecessary Bashisms.虽然我发现这里的大多数答案都非常正确,但其中许多都包含不必要的 Bashisms。 POSIX parameter expansion gives you all you need:
POSIX 参数扩展为您提供所需的一切:
[ "${host#user}" != "${host}" ]
and和
[ "${host#node}" != "${host}" ]
${var#expr}
strips the smallest prefix matching expr
from ${var}
and returns that. ${var#expr}
从${var}
中去除与expr
匹配的最小前缀并返回它。 Hence if ${host}
does not start with user
( node
), ${host#user}
( ${host#node}
) is the same as ${host}
.因此,如果
${host}
不以user
( node
) 开头,则${host#user}
( ${host#node}
) 与${host}
相同。
expr
allows fnmatch()
wildcards, thus ${host#node??}
and friends also work. expr
允许fnmatch()
通配符,因此${host#node??}
和朋友也可以使用。
Since #
has a meaning in Bash, I got to the following solution.由于
#
在 Bash 中具有含义,因此我得到了以下解决方案。
In addition I like better to pack strings with "" to overcome spaces, etc.此外,我更喜欢用 "" 打包字符串以克服空格等。
A="#sdfs"
if [[ "$A" == "#"* ]];then
echo "Skip comment line"
fi
Adding a tiny bit more syntax detail to Mark Rushakoff's highest rank answer.为 Mark Rushakoff 的最高等级答案添加一点点语法细节。
The expression表达方式
$HOST == node*
Can also be written as也可以写成
$HOST == "node"*
The effect is the same.效果是一样的。 Just make sure the wildcard is outside the quoted text.
只要确保通配符在引用的文本之外。 If the wildcard is inside the quotes it will be interpreted literally (ie not as a wildcard).
如果通配符在引号内,它将按字面意思解释(即不作为通配符)。
@OP, for both your questions you can use case/esac: @OP,对于您的两个问题,您都可以使用 case/esac:
string="node001"
case "$string" in
node*) echo "found";;
* ) echo "no node";;
esac
Second question第二个问题
case "$HOST" in
node*) echo "ok";;
user) echo "ok";;
esac
case "$HOST" in
node*|user) echo "ok";;
esac
Or Bash 4.0或重击 4.0
case "$HOST" in
user) ;&
node*) echo "ok";;
esac
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi
doesn't work, because all of [
, [[
, and test
recognize the same nonrecursive grammar.不起作用,因为所有
[
、 [[
和test
都识别相同的非递归语法。 See section CONDITIONAL EXPRESSIONS on your Bash man page.请参阅 Bash 手册页上的条件表达式部分。
As an aside, the SUSv3 says顺便说一句,SUSv3 说
The KornShell-derived conditional command (double bracket [[]] ) was removed from the shell command language description in an early proposal.
在早期的提案中,KornShell 派生的条件命令(双括号[[]] )已从 shell 命令语言描述中删除。 Objections were raised that the real problem is misuse of the test command ( [ ), and putting it into the shell is the wrong way to fix the problem.
有人提出反对,认为真正的问题是滥用测试命令 ( [ ),将其放入 shell 是解决问题的错误方法。 Instead, proper documentation and a new shell reserved word ( ! ) are sufficient.
相反,适当的文档和新的 shell 保留字 ( ! ) 就足够了。
Tests that require multiple test operations can be done at the shell level using individual invocations of the test command and shell logicals, rather than using the error-prone -o flag of test .
需要多个测试操作的测试可以在 shell 级别使用test命令和 shell 逻辑的单独调用来完成,而不是使用test的容易出错的-o标志。
You'd need to write it this way, but test doesn't support it:你需要这样写,但test不支持它:
if [ $HOST == user1 -o $HOST == node* ];
then
echo yes
fi
test uses = for string equality, and more importantly it doesn't support pattern matching. test使用=表示字符串相等,更重要的是它不支持模式匹配。
case
/ esac
has good support for pattern matching: case
/ esac
对模式匹配有很好的支持:
case $HOST in
user1|node*) echo yes ;;
esac
It has the added benefit that it doesn't depend on Bash, and the syntax is portable.它还有一个额外的好处是它不依赖于 Bash,并且语法是可移植的。 From the Single Unix Specification , The Shell Command Language :
来自单一 Unix 规范, Shell 命令语言:
case word in
[(]pattern1) compound-list;;
[[(]pattern[ | pattern] ... ) compound-list;;] ...
[[(]pattern[ | pattern] ... ) compound-list]
esac
grep
Forgetting performance, this is POSIX and looks nicer than case
solutions:忘记性能,这是 POSIX,看起来比
case
解决方案更好:
mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
echo matches
fi
Explanation:解释:
printf '%s'
to prevent printf
from expanding backslash escapes: Bash printf literal verbatim string printf '%s'
以防止printf
扩展反斜杠转义: Bash printf 文字逐字字符串grep -q
prevents echo of matches to stdout: How to check if a file contains a specific string using Bash grep -q
防止匹配到标准输出的回显: 如何使用 Bash 检查文件是否包含特定字符串grep -E
enables extended regular expressions, which we need for the ^
grep -E
启用扩展正则表达式,我们需要^
I tweaked @markrushakoff's answer to make it a callable function:我调整了@markrushakoff 的答案,使其成为可调用函数:
function yesNo {
# Prompts user with $1, returns true if response starts with y or Y or is empty string
read -e -p "
$1 [Y/n] " YN
[[ "$YN" == y* || "$YN" == Y* || "$YN" == "" ]]
}
Use it like this:像这样使用它:
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] y
true
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] Y
true
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] yes
true
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n]
true
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] n
false
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] ddddd
false
Here is a more complex version that provides for a specified default value:这是一个提供指定默认值的更复杂的版本:
function toLowerCase {
echo "$1" | tr '[:upper:]' '[:lower:]'
}
function yesNo {
# $1: user prompt
# $2: default value (assumed to be Y if not specified)
# Prompts user with $1, using default value of $2, returns true if response starts with y or Y or is empty string
local DEFAULT=yes
if [ "$2" ]; then local DEFAULT="$( toLowerCase "$2" )"; fi
if [[ "$DEFAULT" == y* ]]; then
local PROMPT="[Y/n]"
else
local PROMPT="[y/N]"
fi
read -e -p "
$1 $PROMPT " YN
YN="$( toLowerCase "$YN" )"
{ [ "$YN" == "" ] && [[ "$PROMPT" = *Y* ]]; } || [[ "$YN" = y* ]]
}
Use it like this:像这样使用它:
$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi
asfd [y/N]
false
$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi
asfd [y/N] y
true
$ if yesNo "asfd" y; then echo "true"; else echo "false"; fi
asfd [Y/n] n
false
Keep it simple把事情简单化
word="appel"
if [[ $word = a* ]]
then
echo "Starts with a"
else
echo "No match"
fi
你可以做的另一件事是cat
出你是什么呼应和管道与inline cut -c 1-1
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.