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

Tim*_*Tim 677 string bash comparison

我想检查字符串是否以"node"开头,例如"node001".就像是

if [ $HOST == user* ]  
  then  
  echo yes  
fi
Run Code Online (Sandbox Code Playgroud)

我该怎么做才能正确?


我还需要组合表达式来检查HOST是"user1"还是以"node"开头

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

> > > -bash: [: too many arguments
Run Code Online (Sandbox Code Playgroud)

怎么做正确?

Mar*_*off 967

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).
Run Code Online (Sandbox Code Playgroud)

所以你几乎是正确的; 你需要括号,而不是单括号.


关于你的第二个问题,你可以这样写:

HOST=user1
if  [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes1
fi

HOST=node001
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes2
fi
Run Code Online (Sandbox Code Playgroud)

哪个会回应

yes1
yes2
Run Code Online (Sandbox Code Playgroud)

Bash的if语法很难习惯(IMO).

  • 对于正则表达式,你的意思是[[$ a =〜^ z.*]]? (11认同)
  • 您不需要语句分隔符";" 如果你把"然后"放在自己的路上 (5认同)
  • 那么`[[$ a == z*]]`和`[[$ a =="z*"]]`之间是否存在功能差异?换句话说:他们的工作方式不同吗?当你说"$ a等于z*"时,你的意思是什么? (3认同)
  • 只是为了完整性:检查字符串ENDS是否为......:[[$ a ==*com]]` (3认同)
  • ABS是一个不幸的参考选择 - 它是W3的bash学校,充满了过时的内容和糟糕的练习例子; freenode #bash频道一直试图阻止其使用[至少自2008年以来](http://wooledge.org/~greybot/meta/abs).有没有机会在[BashFAQ#31](http://mywiki.wooledge.org/BashFAQ/031)重新报道?(我也建议使用Bash-Hackers的wiki,但现在已经有点了.) (3认同)
  • 如果不讨论如何处理通配符的 shell 扩展,这是没有用的。 (2认同)
  • 这是否不需要在 lh 周围加上引号(例如 `[[ "$a" == z* ]]`)?如果没有,为什么?空的“a”不会引起问题吗? (2认同)
  • `[[` 不会进行分词或全局扩展,因此不需要引用 `a$`。[参考](http://mywiki.wooledge.org/BashFAQ/031)。 (2认同)

bra*_*ter 186

如果您正在使用最近的bash(v3 +),我建议使用bash正则表达式比较运算符=~,即

if [[ "$HOST" =~ ^user.* ]]; then
    echo "yes"
fi
Run Code Online (Sandbox Code Playgroud)

匹配this or that正则表达式使用|,即

if [[ "$HOST" =~ ^user.*|^host1 ]]; then
    echo "yes"
fi
Run Code Online (Sandbox Code Playgroud)

注意 - 这是'正确'的正则表达式语法.

  • user*意味着use和零次或多次出现r,匹配useuserrrr匹配.
  • user.*装置user和零或更多的出现任意字符,那么user1,userX将匹配.
  • ^user.*表示匹配user.*$ HOST开头的模式.

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

注意 - 如果你将每个新问题都问为一个新问题,它会更好,它会使stackoverflow更整洁,更有用.您始终可以包含回到上一个问题的链接以供参考.

  • **仅当右侧是UNQUOTED时,Bash` = ~`运算符才会进行正则表达式匹配.如果您引用右侧"可以引用模式的任何部分以强制它匹配为字符串."**(1.)确保始终将正则表达式放在右侧未引用的(和2.)如果将正则表达式存储在变量中,请确保在进行参数扩展时不引用右侧. (16认同)
  • 遗憾的是,已接受的答案未提及正则表达式的语法。 (2认同)

Jo *_* So 120

我总是试图坚持使用POSIX sh而不是使用bash扩展,因为脚本的一个主要点是可移植性.(除了连接程序,不替换它们)

在sh中,有一种简单的方法来检查"is-prefix"条件.

case $HOST in node*)
    your code here
esac
Run Code Online (Sandbox Code Playgroud)

考虑到多大年龄,神秘和苛刻的sh(并且bash不是治愈方法:它更复杂,更不一致,更不便携),我想指出一个非常好的功能方面:虽然有一些类似的语法元素case是内置的,结果构造与任何其他工作没有什么不同.它们可以以相同的方式组成:

if case $HOST in node*) true;; *) false;; esac; then
    your code here
fi
Run Code Online (Sandbox Code Playgroud)

甚至更短

if case $HOST in node*) ;; *) false;; esac; then
    your code here
fi
Run Code Online (Sandbox Code Playgroud)

或者甚至更短(只呈现!为一个语言元素-但是这是不好的风格现在)

if ! case $HOST in node*) false;; esac; then
    your code here
fi
Run Code Online (Sandbox Code Playgroud)

如果您喜欢明确,请构建自己的语言元素:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
Run Code Online (Sandbox Code Playgroud)

这不是很好吗?

if beginswith node "$HOST"; then
    your code here
fi
Run Code Online (Sandbox Code Playgroud)

由于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
Run Code Online (Sandbox Code Playgroud)

这很优雅.并不是说我会主张使用sh来处理任何严重的事情 - 它在现实世界的要求上打得太快(没有lambda,所以必须使用字符串.但是用字符串嵌套函数调用是不可能的,管道是不可能的......)

  • +1这不仅是便携式的,它还具有可读性,惯用性和优雅性(适用于shell脚本).它也自然地延伸到多种模式; `case $ HOST in user01 | 节点*)...` (9认同)

mar*_*ton 75

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

if [ "${HOST:0:4}" = user ]
Run Code Online (Sandbox Code Playgroud)

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

if [[ "$HOST" == user1 || "$HOST" == node* ]]
Run Code Online (Sandbox Code Playgroud)

  • 你应该双重引用`$ {HOST:0:4}` (7认同)

Pau*_*ce. 56

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

case "$HOST" in 
    user1|node*) 
            echo "yes";;
        *)
            echo "no";;
esac
Run Code Online (Sandbox Code Playgroud)

编辑:

我已将您的备选项添加到上面的案例陈述中

在您编辑的版本中,您有太多括号.它应该如下所示:

if [[ $HOST == user1 || $HOST == node* ]];
Run Code Online (Sandbox Code Playgroud)

  • "有些人喜欢......":这个版本在shell和shell之间更具可移植性. (8认同)

dhk*_*hke 31

虽然我发现这里的大多数答案都很正确,但其中许多都含有不必要的基础.POSIX参数扩展为您提供所需的一切:

[ "${host#user}" != "${host}" ]
Run Code Online (Sandbox Code Playgroud)

[ "${host#node}" != "${host}" ]
Run Code Online (Sandbox Code Playgroud)

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

expr允许fnmatch()通配符,因此${host#node??}和朋友也一起工作.

  • 我认为 bashism `[[ $host == user* ]]` 可能是必要的,因为它比 `[ "${host#user}" != "${host}" ]` 更具可读性。由于您可以控制执行脚本的环境(以最新版本的`bash` 为目标),前者更可取。 (3认同)
  • @x-yuri 坦率地说,我只是将它打包到一个 `has_prefix()` 函数中,然后再也不看它了。 (3认同)

ozm*_*zma 29

因为#在bash中有意义所以我得到了以下解决方案.
另外我更喜欢用""打包字符串来克服空间等.

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "skip comment line"
fi
Run Code Online (Sandbox Code Playgroud)


Tho*_*der 13

把事情简单化

word="appel"

if [[ $word = a* ]]
then
  echo "Starts with a"
else
  echo "No match"
fi
Run Code Online (Sandbox Code Playgroud)


MrP*_*ead 9

为Mark Rushakoff的最高等级答案添加更多的语法细节.

表达方式

$HOST == node*
Run Code Online (Sandbox Code Playgroud)

也可以写成

$HOST == "node"*
Run Code Online (Sandbox Code Playgroud)

效果是一样的.只需确保通配符在引用文本之外.如果通配符引号内,它将按字面解释(即不作为通配符).


gho*_*g74 8

@OP,对于你的两个问题,你可以使用case/esac

string="node001"
case "$string" in
  node*) echo "found";;
  * ) echo "no node";;
esac
Run Code Online (Sandbox Code Playgroud)

第二个问题

case "$HOST" in
 node*) echo "ok";;
 user) echo "ok";;
esac

case "$HOST" in
 node*|user) echo "ok";;
esac
Run Code Online (Sandbox Code Playgroud)

或者Bash 4.0

case "$HOST" in
 user) ;& 
 node*) echo "ok";; 
esac
Run Code Online (Sandbox Code Playgroud)


jus*_*ody 6

if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];  
then  
echo yes 
fi
Run Code Online (Sandbox Code Playgroud)

不起作用,因为所有[,[[并且测试识别相同的非递归语法.请参阅bash手册页中的CONDITIONAL EXPRESSIONS部分.

顺便说一句,SUSv3说

在早期提案中,从shell命令语言描述中删除了KornShell派生的条件命令(双括号[[]]).提出异议,认为真正的问题是滥用测试命令([),并将其放入shell中是解决问题的错误方法.相反,适当的文档和新的shell保留字(!)就足够了.

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

你需要这样写,但测试不支持它:

if [ $HOST == user1 -o $HOST == node* ];  
then  
echo yes 
fi
Run Code Online (Sandbox Code Playgroud)

test uses =表示字符串相等,更重要的是它不支持模式匹配.

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

case $HOST in
user1|node*) echo yes ;;
esac
Run Code Online (Sandbox Code Playgroud)

它具有额外的好处,它不依赖于bash,语法是可移植的.来自Single Unix规范,Shell命令语言:

case word in
    [(]pattern1) compound-list;;
    [[(]pattern[ | pattern] ... ) compound-list;;] ...
    [[(]pattern[ | pattern] ... ) compound-list]
esac
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 5

grep

忘记性能,这是 POSIX 并且看起来比解决方案更好case

mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
  echo matches
fi
Run Code Online (Sandbox Code Playgroud)

解释: