提问人:Tim 提问时间:2/1/2010 最后编辑:Matthias BraunTim 更新时间:7/7/2023 访问量:975662
在 Bash 中,如何检查字符串是否以某个值开头?
In Bash, how can I check if a string begins with some value?
问:
我想检查字符串是否以“node”开头,例如“node001”。类似的东西
if [ $HOST == node* ]
then
echo yes
fi
我怎样才能正确地做到这一点?
我还需要组合表达式来检查是“user1”还是以“node”开头:HOST
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi
> > > -bash: [: too many arguments
我怎样才能正确地做到这一点?
答:
您可以只选择要检查的字符串部分:
if [ "${HOST:0:4}" = user ]
对于您的后续问题,您可以使用 OR:
if [[ "$HOST" == user1 || "$HOST" == node* ]]
评论
${HOST:0:4}
HOST='a b'; if [ ${HOST:0:4} = user ] ; then echo YES ; fi
if [[ ${HOST:0:4} = user ]]
如果您使用的是最新版本的 Bash (v3+),我建议使用 Bash 正则表达式比较运算符 ,例如,=~
if [[ "$HOST" =~ ^user.* ]]; then
echo "yes"
fi
要在正则表达式中匹配,请使用 ,例如,this or that
|
if [[ "$HOST" =~ ^user.*|^host1 ]]; then
echo "yes"
fi
注意 - 这是“正确的”正则表达式语法。
user*
均值和 的零次或多次出现,所以 和 将匹配。use
r
use
userrrr
user.*
表示值和任何字符的零次或多次出现,因此 将匹配。user
user1
userX
^user.*
均值与$HOST开头的模式匹配。user.*
如果您不熟悉正则表达式语法,请尝试参考此资源。
请注意,Bash 运算符仅在右侧为 UNQUOTED 时执行正则表达式匹配。如果您确实引用了右侧,“可以引用模式的任何部分以强制将其匹配为字符串。即使在进行参数扩展时,也不应引用右侧。=~
评论
=~
运算符仅在右侧未加引号时执行正则表达式匹配。如果您确实引用了右侧的“可以引用模式的任何部分,以强制将其匹配为字符串。(1.) 确保始终将正则表达式放在右侧不带引号,并且 (2.) 如果您将正则表达式存储在变量中,请确保在进行参数扩展时不要在右侧引用。
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).
所以你说得几乎正确;您需要双括号,而不是单括号。
关于你的第二个问题,你可以这样写:
HOST=user1
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
echo yes1
fi
HOST=node001
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
echo yes2
fi
这将回声
yes1
yes2
Bash 的语法很难习惯 (IMO)。if
评论
[[ $a == z* ]]
[[ $a == "z*" ]]
[[ $a == *com ]]
我更喜欢已经发布的其他方法,但有些人喜欢使用:
case "$HOST" in
user1|node*)
echo "yes";;
*)
echo "no";;
esac
编辑:
我已将您的替代项添加到上面的案例陈述中
在编辑后的版本中,括号太多。它应该看起来像这样:
if [[ $HOST == user1 || $HOST == node* ]];
评论
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi
不起作用,因为所有 、 和 都识别相同的非递归语法。请参见 Bash 手册页上的 CONDITIONAL EXPRESSIONS 部分。[
[[
test
顺便说一句,SUSv3 说
在早期的提案中,KornShell 派生的条件命令(双括号 [[]])已从 shell 命令语言描述中删除。有人提出反对意见,认为真正的问题是滥用测试命令([),将其放入shell是解决问题的错误方法。相反,适当的文档和新的 shell 保留字 (!) 就足够了。
需要多个测试操作的测试可以在 shell 级别使用测试命令和 shell 逻辑的单独调用来完成,而不是使用 test 的容易出错的 -o 标志。
你需要这样写,但测试不支持它:
if [ $HOST == user1 -o $HOST == node* ];
then
echo yes
fi
test 使用 = 表示字符串相等,更重要的是它不支持模式匹配。
case
/ esac
对模式匹配有很好的支持:
case $HOST in
user1|node*) echo yes ;;
esac
它还有一个额外的好处,那就是它不依赖于 Bash,并且语法是可移植的。从单一的 Unix 规范中,shell 命令语言:
case word in
[(]pattern1) compound-list;;
[[(]pattern[ | pattern] ... ) compound-list;;] ...
[[(]pattern[ | pattern] ... ) compound-list]
esac
评论
[
并且是 Bash 内置程序以及外部程序。尝试。test
type -a [
if [ -z $aa -or -z $bb ]
if [ -z "$aa" -o -z "$bb" ] ; ...
@OP,对于您的两个问题,您都可以使用案例/ESAC:
string="node001"
case "$string" in
node*) echo "found";;
* ) echo "no node";;
esac
第二个问题
case "$HOST" in
node*) echo "ok";;
user) echo "ok";;
esac
case "$HOST" in
node*|user) echo "ok";;
esac
或Bash 4.0的
case "$HOST" in
user) ;&
node*) echo "ok";;
esac
评论
;&
我总是尝试坚持使用 POSIX 而不是使用 Bash 扩展,因为脚本的主要要点之一是可移植性(除了连接程序,而不是替换它们)。sh
在 中,有一种简单的方法来检查 “is-prefix” 条件。sh
case $HOST in node*)
# Your code here
esac
考虑到 sh 是多么古老、晦涩和粗糙(而且 Bash 不是解药:它更复杂、更不一致、更不便携),我想指出一个非常好的功能方面:虽然一些语法元素是内置的,但生成的结构与任何其他工作没有什么不同。它们可以用相同的方式组成:case
if case $HOST in node*) true;; *) false;; esac; then
# Your code here
fi
甚至更短
if case $HOST in node*) ;; *) false;; esac; then
# Your code here
fi
甚至更短(只是为了呈现为一种语言元素——但现在这是糟糕的风格)!
if ! case $HOST in node*) false;; esac; then
# Your code here
fi
如果你喜欢显式,可以构建你自己的语言元素:
beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
这其实不是很好吗?
if beginswith node "$HOST"; then
# Your code here
fi
由于基本上只有作业和字符串列表(以及内部进程,由这些进程组成作业),我们现在甚至可以做一些简单的函数式编程: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
这是优雅的。并不是说我主张将它用于任何严肃的事情——它在现实世界的需求上破坏得太快了(没有 lambdas,所以我们必须使用字符串。但是不可能使用字符串嵌套函数调用,管道是不可能的,等等)sh
评论
case $HOST in user01 | node* ) ...
if case $HOST in node*) true;; *) false;; esac; then
case
if ... then
beginswith() { case "$2" in "$1"*) true;; *) false;; esac; }
$1
*
?
由于在 Bash 中具有意义,因此我得到了以下解决方案。#
此外,我更喜欢用“”来包装字符串以克服空格等。
A="#sdfs"
if [[ "$A" == "#"* ]];then
echo "Skip comment line"
fi
评论
blah:
case $A in "#"*) echo "Skip comment line";; esac
既短又便携。
虽然我发现这里的大多数答案都非常正确,但其中许多都包含不必要的 Bashisms。POSIX 参数扩展为您提供所需的一切:
[ "${host#user}" != "${host}" ]
和
[ "${host#node}" != "${host}" ]
${var#expr}
从中剥离匹配的最小前缀并返回该前缀。因此,如果不以 () 开头,则 () 与 相同。expr
${var}
${host}
user
node
${host#user}
${host#node}
${host}
expr
允许 fnmatch()
通配符,因此和朋友也可以工作。${host#node??}
评论
[[ $host == user* ]]
[ "${host#user}" != "${host}" ]
bash
has_prefix()
在 Mark Rushakoff 的最高排名答案中添加更多语法细节。
表达式
$HOST == node*
也可以写成
$HOST == "node"*
效果是一样的。只需确保通配符位于引用的文本之外即可。如果通配符在引号内,则按字面意思解释(即不作为通配符)。
我调整了@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" == "" ]]
}
像这样使用它:
$ 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
下面是一个更复杂的版本,它提供了指定的默认值:
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* ]]
}
像这样使用它:
$ 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
grep
忘记性能,这是 POSIX,看起来比解决方案更好:case
mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
echo matches
fi
解释:
printf '%s'
防止扩展反斜杠转义: Bash printf literal verbatim stringprintf
grep -q
防止匹配回显到 stdout:如何使用 Bash 检查文件是否包含特定字符串grep -E
启用扩展的正则表达式,我们需要^
保持简单
word="appel"
if [[ $word = a* ]]
then
echo "Starts with a"
else
echo "No match"
fi
上一个:图像比较 - 快速算法
评论