繁体   English   中英

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

[英]In Bash, how can I check if a string begins with some value?

我想检查一个字符串是否以“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

我怎样才能正确地做到这一点?

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 的if语法很难习惯(IMO)。

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

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

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

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

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

  • user*表示user的零次或多次出现,因此useuserrrr将匹配。
  • user.*表示user和零次或多次出现的任何字符,因此user1userX将匹配。
  • ^user.*表示匹配 $HOST 开头的模式user.*

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

请注意,Bash =~运算符仅在右侧为UNQUOTED时进行正则表达式匹配。 如果您确实引用了右侧,则“可以引用模式的任何部分以强制将其作为字符串匹配。”。 即使在进行参数扩展时,也不应引用右侧。

我总是尝试坚持使用 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,所以我们必须使用字符串。但是用字符串嵌套函数调用是不可能的,管道是不可能的,等等)

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

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

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

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

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

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

编辑:

我已将您的替代者添加到上面的案例陈述中

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

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

虽然我发现这里的大多数答案都非常正确,但其中许多都包含不必要的 Bashisms。 POSIX 参数扩展为您提供所需的一切:

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

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

${var#expr}${var}中去除与expr匹配的最小前缀并返回它。 因此,如果${host}user ( node ) 开头,则${host#user} ( ${host#node} ) 与${host}相同。

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

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

此外,我更喜欢用 "" 打包字符串以克服空格等。

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

为 Mark Rushakoff 的最高等级答案添加一点点语法细节。

表达方式

$HOST == node*

也可以写成

$HOST == "node"*

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

@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

重击 4.0

case "$HOST" in
 user) ;&
 node*) echo "ok";;
esac
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

不起作用,因为所有[[[test都识别相同的非递归语法。 请参阅 Bash 手册页上的条件表达式部分。

顺便说一句,SUSv3 说

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

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

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

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

grep

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

mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
  echo matches
fi

解释:

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

把事情简单化

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM