continue:仅在“for”、“while”或“until”循环中有意义

continue:仅在“for”、“while”或“until”循环中有意义

我有一个循环检查是否跳到下一次迭代的某些标准(A)。我意识到,如果我调用一个skip调用 的函数 ( ) continue,就好像它是在子进程中调用的,因为它看不到循环 (B)。此外,建议的依赖于eval字符串的解决方法也不起作用 (C)。

# /usr/bin/bash env

skip()
{
    echo "skipping : $1"
    continue   
}

skip_str="echo \"skipping : $var\"; continue"

while read -r var;
do
    if [[ $var =~  ^bar$  ]];
    then
        # A
        #        echo "skipping : $var"
        #        continue
        # B
        #        skip "$var" #  continue: only meaningful in a `for', `while', or `until' loop
        # C
        eval "$skip_str"
    fi
    echo "processed: $var"
done < <(cat << EOF 
foo
bar
qux
EOF
        )

方法C:

$ source ./job-10.sh
processed: foo
skipping : 
processed: qux

另请参阅:

Bash 中的函数是否作为子进程运行?

PS1:有人可以提醒我为什么< <而不是<之后需要done吗?

PS2:没有找到while因此的标签for

答案1

该函数确实在同一个进程中运行,只是在语法上与循环不同。在几乎任何其他编程语言中,您也不能在函数内部使用break或来影响其外部的循环。continue

但实际上,在外壳中,未指定:

continue [n]
如果n指定后,继续实用程序应返回到顶部nth 包含 for、while 或 Until 循环。如果n未指定,继续的行为就像n被指定为 1。[...]

[...]如果没有封闭循环,则行为未指定。

你得到什么取决于外壳:

Bash 给出错误并忽略continue

$ bash -c 'f() { continue; }; for x in a b c; do f; echo $x; done; echo done'
environment: line 0: continue: only meaningful in a ‘for’, ‘while’, or ‘until’ loop
a
environment: line 0: continue: only meaningful in a ‘for’, ‘while’, or ‘until’ loop
b
environment: line 0: continue: only meaningful in a ‘for’, ‘while’, or ‘until’ loop
c
done

Ksh 默默地忽略它:

$ ksh -c 'f() { continue; }; for x in a b c; do f; echo $x; done; echo done'
a
b
c
done

虽然 Dash、Yash 和 Zsh 实际上确实应用了它:

$ dash -c 'f() { continue; }; for x in a b c; do f; echo $x; done; echo done'
done

我确实预计类似的考虑因素eval也可能适用于使用,但情况似乎并非如此。至少我看不出外壳之间有任何区别。

总而言之,不要这样做。只要写出来:

while read -r var;
do
    if [[ $var =~  ^bar$  ]];
    then
        echo "skipping '$var'"
        continue
    fi
    echo "processed: $var"
done

答案2

问题是当你的函数被执行时,它不再在循环内。它不在子外壳中,不,但它也不在任何循环内。就该函数而言,它是一段独立的代码,并且不知道它是从哪里调用的。

然后,当您运行时,eval "$skip_str"没有值,因为您在未定义的时间$var设置了。这实际上应该按照您的预期工作,它只是无缘无故地非常复杂和危险(如果您不 100% 控制输入):skip_string$var

#! /usr/bin/env bash

while read -r var;
do
  skip_str="echo \"skipping : $var\"; continue"
  if [[ $var =~  ^bar$  ]];
  then
    eval "$skip_str"
  fi
  echo "processed: $var"

done < <(cat << EOF 
foo
bar
qux
EOF
        )

那……确实不太漂亮。就我个人而言,我只会使用一个函数来进行测试,然后对测试结果进行操作。像这样:

#! /usr/bin/env bash

doTest(){
  if [[ $1 =~  ^bar$  ]];
  then
    return 1
  else
    return 0
  fi
  
}

while read -r var;
do
  if doTest "$var"; then
    echo "processed: $var"
  else
    echo "skipped: $var"
    continue
  fi

## Rest of your code here

done < <(cat << EOF 
foo
bar
qux
EOF
        )

如果你解释一下你的目标是什么,我可能会给你更好的东西。

最后,你不需要< <after done,你需要< <()。是<正常的输入重定向,<()称为流程替代and 是一个技巧,可以让您将命令的输出视为文件名。

如果您使用该函数只是为了避免重复诸如 之类的额外内容echo "skipping $1",则可以简单地将更多逻辑移至该函数中,以便在那里有一个循环。像这样的东西:关联

相关内容