【问题标题】:How to check if while loop was executed?如何检查while循环是否被执行?
【发布时间】:2013-05-09 00:12:51
【问题描述】:
cat list.txt | grep '^http://.*\.osm\..*$' | while read line; do
    fn=$(basename $line)
    do_something()
done
# TODO: check if it did something

如果 grep 命令不返回任何内容,它不会进入循环并且 do_something() 不会被执行。

我无法在 while 循环之外检查 $fn 中的内容,请参阅 Bash variable scope

检查 do_something() 是否在此处执行的侵入性最小的解决方案是什么?

【问题讨论】:

  • 为什么不在 do/done 中分配一个变量?
  • @klugerama 除非您使用bash 4.2 和lastpipe 选项,否则在该循环内设置的变量在之后无法显示。

标签: bash


【解决方案1】:

由于while 循环在子shell 中运行,它无法将值传播回父shell。但是,仍然有方法可以做想要的事情。这是一个:

if grep -q '^http://.*\.osm\..*$' list.txt       # loop will definitely run
then
  while read line
  do
    # do something with line
  done < <(grep '^http://.*\.osm\..*$' list.txt)
else                                             # no matches, loop will not run
  # do something else
fi

它具有运行grep 两次的副作用,这可以通过保存grep 的输出并对其进行后处理来避免,正如另一个答案中所建议的那样,但在某些方面这更容易理解...

【讨论】:

    【解决方案2】:

    您可以使用flag,如果执行循环,其值将被更改。下面是一个 poc。

    shopt -s lastpipe   # bash 4.2 or greater only
    flag="false"
    cat list.txt | grep '^http://.*\.osm\..*$' | while read line; do
        fn=$(basename $line)
        do_something()
        flag="true"
    done
    
    if [ "$flag" = "true" ]
    then
        echo "loop was executed"
    fi
    

    如果while 跨越sub-shell,您需要使用以下内容(感谢下方评论的所有人)

     while read line
       do
           fn=$(basename $line)
           do_something()
           flag="true"
       done < <(grep '^http://.*\.osm\..*$' list.txt)
    

    【讨论】:

    • 真的有用吗? while 循环的主体在子 shell 中运行,所以我会想到更改,即使是现有变量也不会传播回来。
    • 由于作用域不同,这不起作用,请参阅我的描述中的链接
    • 这取决于特定的外壳。假设最近版本的bash 似乎最常见,但它不会。旧版本的,例如ksh 会工作,不过...
    【解决方案3】:

    grep 的输出保存在变量中并显式测试。

    filtered=$(cat list.txt | grep ....)
    
    if [ -z "$filtered" ] ;
      ... handle empty output ..
     else
      ... do your while loop here... 
    fi
    

    顺便说一句:换行符保留在"$filtered" 中,但请务必在使用时引用它。

    【讨论】:

      【解决方案4】:

      您可以使用进程替换来代替管道,这样可以有效地设置标志:

      flag=0
      while read line; do
          flag=1
          fn=$(basename $line)
          do_something()
      done < <( grep '^http://.*\.osm\..*$' list.txt )
      
      if [[ $flag == 1 ]]; then
         ...
      fi
      

      【讨论】:

        【解决方案5】:

        @chepner 解决方案的变体:

        flag=0
        while read line; do
            grep -q '^http://.*\.osm\..*$' <<< $line && {
              flag=1
              fn=$(basename $line)
              do_something()
            }
        done < list.text
        
        if [[ $flag == 1 ]]; then
           ...
        fi
        

        然后使用更适合您需要的那个。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-06
          • 2022-01-27
          • 2022-07-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-25
          相关资源
          最近更新 更多