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

1133
我想检查一个字符串是否以"node"开头,例如"node001"。类似这样的操作。
if [ $HOST == node* ]
  then
  echo yes
fi

我该如何正确地做呢?


我还需要结合表达式来检查HOST是否为"user1"或以"node"开头。
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

> > > -bash: [: too many arguments

怎样才能正确地做到呢?

7
不要过于诱惑将表达式结合在一起。虽然两个分离的条件语句可能看起来不那么美观,但这样可以提供更好的错误消息,并使脚本更易于调试。另外,我建议避免使用bash特性,而是使用switch语句。 - hendry
13个回答

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

由于所有的`[`、`[[`和`test`都识别相同的非递归语法,因此不起作用。请参阅Bash手册上的“条件表达式”部分。
另外,SUSv3表示:
KornShell派生的条件命令(双括号`[[]]`)在早期的提案中已从shell命令语言描述中删除。有人提出反对意见,认为真正的问题是`test`命令的误用,并将其放入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

1
[test 是 Bash 内置命令,也是外部程序。尝试使用 type -a [ 命令。 - Dennis Williamson
非常感谢@just somebody解释“复合或”的问题 - 我正需要这样的东西!干杯!PS 注意(与OP无关):if [ -z $aa -or -z $bb ] ; ...会出现“bash:[:-or:binary operator expected”;但是if [ -z "$aa" -o -z "$bb" ] ; ...则可以通过。 - sdaau

5

3
我修改了@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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接