我想检查一个字符串是否以“节点”开始,例如。“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

我怎样才能正确地做呢?


你可以选择你想要检查的字符串部分:

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

对于你接下来的问题,你可以使用OR:

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

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

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

要在正则表达式中匹配这个或那个,使用|,例如,

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

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

User *表示使用和零次或多次出现r,因此使用和userrrr将匹配。 用户。*表示用户和零或多次出现任何字符,因此user1, userX将匹配。 ^用户。*表示匹配模式用户。*在$HOST的开头。

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

请注意,Bash =~操作符仅在右侧为unquote时进行正则表达式匹配。如果你在右边引用,“模式的任何部分都可以被引用,以迫使它作为字符串进行匹配。”即使在进行参数展开时,也不应该在右边引用。


高级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)。


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

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

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

说句题外话,SUSv3说

The KornShell-derived conditional command (double bracket [[]]) was removed from the shell command language description in an early proposal. Objections were raised that the real problem is misuse of the test command ([), and putting it into the shell is the wrong way to fix the problem. Instead, proper documentation and a new shell reserved word (!) are sufficient. Tests that require multiple test operations can be done at the shell level using individual invocations of the test command and shell logicals, rather than using the error-prone -o flag of test.

你需要这样写,但是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

@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

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


因为#在Bash中有意义,所以我采用了以下解决方案。

此外,我更喜欢用“”来填充字符串,以克服空格等。

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

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

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

and

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

${var#expr}从${var}中剥离匹配expr的最小前缀并返回。因此,如果${host}不以用户(节点)开头,${host#user} (${host#node})与${host}相同。

Expr允许fnmatch()通配符,因此${host#node??朋友也能工作。


在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,看起来比大小写解决方案更好:

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

解释:

printf '%s'以防止printf扩展反斜杠转义:Bash printf字面逐字串 grep -q防止匹配回显到stdout:如何使用Bash检查文件是否包含特定字符串 grep -E启用扩展正则表达式,这是^ . grep所需要的


保持简单

word="appel"

if [[ $word = a* ]]
then
  echo "Starts with a"
else
  echo "No match"
fi