在Bash中,如何检查字符串是否以某个值开头?


744

我想检查字符串是否以“ node”开头,例如“ node001”。就像是

if [ $HOST == user* ]
  then
  echo yes
fi

如何正确执行?


我还需要结合表达式来检查HOST是“ user1”还是以“ node”开头

if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

> > > -bash: [: too many arguments

如何正确执行?


7
不要太想组合表达式。尽管可以提供更好的错误消息并使脚本更易于调试,但具有两个单独的条件可能看起来更难看。另外,我会避免使用bash功能。切换是必经之路。
hendry 2010年

Answers:


1072

高级Bash脚本指南》中的此代码段说:

# 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的if语法很难适应(IMO)。


12
对于正则表达式,您的意思是[[$ a =〜^ z。*]]吗?
JStrahl

3
那么[[ $a == z* ]]和之间有功能上的区别[[ $a == "z*" ]]吗?换句话说:它们的工作方式不同吗?当您说“ $ a等于z *”时,这是什么意思?
Niels Bom 2015年

5
您不需要语句分隔符“;” 如果您将“ then”放在自己的行上
Yaza 2015年

6
仅出于完整性考虑:检查字符串是否以...结尾::[[ $a == *com ]]
lepe

4
ABS是不幸的选择,它是W3Schools的bash,充满了过时的内容和不当行为的例子。自从2008年以来,freenode #bash频道一直试图阻止其使用。是否有机会重新指向BashFAQ#31?(我也曾建议过Bash-Hackers的Wiki,但现在已经下降了一点)。
查尔斯·达菲

207

如果您使用的是最新版本的Bash(v3 +),则建议使用Bash regex比较运算符=~,例如,

if [[ "$HOST" =~ ^user.* ]]; then
    echo "yes"
fi

要匹配this or that正则表达式,请使用|,例如,

if [[ "$HOST" =~ ^user.*|^host1 ]]; then
    echo "yes"
fi

注意-这是“正确”的正则表达式语法。

  • user*的平均值use和零个或多个出现r,因此useuserrrr将匹配。
  • user.*装置user和零或更多的出现任意字符,那么user1userX将匹配。
  • ^user.*表示匹配user.*$ HOST开头的模式。

如果您不熟悉正则表达式语法,请尝试参考此资源


谢谢,Brabster!我在原始帖子中添加了一个新问题,即如何在if聚类中组合表达式。
蒂姆

2
遗憾的是,已接受的答案未提及正则表达式的语法。
卡洛斯·罗斯

20
仅供参考,Bash =~运算符仅在右侧为UNQUOTED时执行正则表达式匹配。如果确实引用了右侧,则“可能会引用该模式的任何部分以强制将其匹配为字符串。” (1.)确保始终将正则表达式放在右引号之外;(2。)如果将正则表达式存储在变量中,请确保在进行参数扩展时不引用右手边。
特雷弗·博伊德·史密斯

145

我始终尝试使用POSIX sh而不是使用Bash扩展,因为脚本编写的主要要点之一就是可移植性(除了连接程序,而不是替换它们)。

在中sh,有一种简单的方法可以检查“ is-prefix”条件。

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

真优雅 我并不是提倡使用sh任何严重的东西-它在现实世界的要求中会很快崩溃(没有lambda,所以我们必须使用字符串。但是不能使用字符串嵌套函数调用,不能使用管道,等等)。


12
+1不仅可移植,而且可读性,惯用性和优雅性(用于shell脚本)。它自然也扩展到多种模式。case $HOST in user01 | node* ) ...
Tripleee 2013年

这类代码格式有名称吗?if case $HOST in node*) true;; *) false;; esac; then 我到处都看过它,在我看来,它看起来有些皱缩。
Niels Bom 2015年

@NielsBom我不知道格式化到底是什么意思,但是我的意思是shell代码非常容易组合。因为case命令是命令,所以它们可以进入if ... then
Jo So

我什至不知道为什么它是可组合的,对此我还不了解足够的shell脚本:-)我的问题是有关此代码如何使用不匹配的括号和双分号。它看起来不像我以前见过的shell脚本,但是我可能习惯于看bash脚本而不是sh脚本,所以可能就是这样。
Niels Bom 2015年

注意:beginswith() { case "$2" in "$1"*) true;; *) false;; esac; }否则应使用$1文字*?否则可能给出错误的答案。
LLFourn'2

80

您可以只选择要检查的字符串部分:

if [ "${HOST:0:4}" = user ]

对于后续问题,您可以使用OR

if [[ "$HOST" == user1 || "$HOST" == node* ]]

8
你应该双引号${HOST:0:4}
乔所以

@Jo So:是什么原因?
Peter Mortensen

@PeterMortensen,尝试HOST='a b'; if [ ${HOST:0:4} = user ] ; then echo YES ; fi
Jo So

60

我更喜欢已经发布的其他方法,但是有些人喜欢使用:

case "$HOST" in 
    user1|node*) 
            echo "yes";;
        *)
            echo "no";;
esac

编辑:

我已将您的替代品添加到上述案例声明中

在您编辑的版本中,括号太多。它看起来应该像这样:

if [[ $HOST == user1 || $HOST == node* ]];

谢谢,丹尼斯!我在原始帖子中添加了一个新问题,即如何在if聚类中组合表达式。
蒂姆(Tim)2010年

11
“有些人喜欢...”:此版本在各个版本和shell中更具可移植性。
2011年

使用case语句,因为没有分词,所以您可以省略变量周围的引号。我知道这毫无意义且前后矛盾,但我确实希望保留那里的引号,以使其在本地更具视觉吸引力。
乔于是

就我而言,我必须在)之前省略引号:“ / *”)不起作用,/ *)起作用。(我正在寻找以/开头的字符串,即绝对路径)
JosiahYoder处于活动状态,除非。.17Feb 18'20

36

虽然我发现这里的大多数答案是正确的,但其中许多包含不必要的Bashisms。POSIX参数扩展可满足您的所有需求:

[ "${host#user}" != "${host}" ]

[ "${host#node}" != "${host}" ]

${var#expr}去掉最小的前缀匹配expr${var}返回这一点。因此,如果${host}没有下手usernode), (${host#user}${host#node}是一样的${host}

expr允许使用fnmatch()通配符,因此${host#node??}和朋友也可以工作。


2
我认为bashism [[ $host == user* ]]可能是必要的,因为它比更具可读性[ "${host#user}" != "${host}" ]。因此,如果您控制执行脚本的环境(以的最新版本为目标bash),则最好使用前者。
x-yuri

2
@ x-yuri坦白说,我只是将其打包成一个has_prefix()函数,并且不再关注它。
dhke

30

由于#在Bash中具有含义,因此我获得了以下解决方案。

另外,我更喜欢用“”来包装字符串,以克服空格等问题。

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "Skip comment line"
fi

这正是我所需要的。谢谢!
尼卡比曹

谢谢,我也想知道如何匹配以开头的字符串blah:,看起来这就是答案!
Anentropic

12

在Mark Rushakoff的最高排名答案中添加了更多语法细节。

表达方式

$HOST == node*

也可以写成

$HOST == "node"*

效果是一样的。只要确保通配符在引号之外即可。如果通配符位于引号内,它将按字面意义进行解释(即不作为通配符)。


8

@OP,对于两个问题,都可以使用case / 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

请注意,;&只有在击> = 4,可用
已暂停,直至另行通知。

6
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

不工作的,因为所有的[[[以及test识别相同的非递归语法。请参见Bash手册页上的“ 条件表达式 ” 部分。

顺便说一句,SUSv3说

在早期的提议中,从KornShell派生的条件命令(双括号[[]])已从Shell命令语言描述中删除。有人提出反对意见,认为真正的问题是滥用测试命令([),将其放入外壳是解决问题的错误方法。而是使用适当的文档和一个新的shell保留字()就足够了。

可以使用单独的test命令和shell逻辑调用在shell级别上完成需要多次测试操作的测试,而不是使用test的容易出错的-o标志。

您需要以这种方式编写它,但是测试不支持它:

if [ $HOST == user1 -o $HOST == node* ];
then
echo yes
fi

测试使用=来实现字符串相等性,更重要的是,它不支持模式匹配。

case/ esac对模式匹配有很好的支持:

case $HOST in
user1|node*) echo yes ;;
esac

它具有不依赖Bash的附加优点,并且语法可移植。根据Single Unix规范Shell命令语言

case word in
    [(]pattern1) compound-list;;
    [[(]pattern[ | pattern] ... ) compound-list;;] ...
    [[(]pattern[ | pattern] ... ) compound-list]
esac

1
[并且test是Bash内置程序以及外部程序。尝试type -a [
暂停,直到另行通知。

非常感谢您解释“化合物或”的问题,@仅仅是有人-正是在寻找类似的东西!干杯! PS说明(无关OP)if [ -z $aa -or -z $bb ]; ...给出“ bash:[:-或:期望的二进制运算符 ”;但是if [ -z "$aa" -o -z "$bb" ] ; ...过去了。
sdaau 2011年


2

我调整了@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

-5

您可以做的另一件事cat是回显并传递内容inline cut -c 1-1

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.