【问题标题】:How to know if file in a loop is the last one?如何知道循环中的文件是否是最后一个?
【发布时间】:2012-08-31 03:58:59
【问题描述】:

例子

for FILE in $DIR/* 
do
  if(<is last File>)
    doSomethingSpecial($FILE)
  else
    doSomethingRegular($FILE)
  fi
done

&lt;is last file&gt; 调用什么来检查当前文件是否是数组中的最后一个?

是否有一个简单的内置检查而不自己检查数组的长度?

【问题讨论】:

  • 不,我不想只处理最后一个文件,而是处理每个文件(就像在我的循环示例中一样),然后对最后一个文件执行另一个操作。
  • 对不起,如果我不清楚,您可以使用 ls + tail 来检测最后一个文件名。然后在你的循环中检查文件名 -eq 检查。那将是 条件。我不在我的 *nix 控制台之前,只是提供一个想法
  • 如果最后一个文件名包含换行符,ls | tail 将不起作用(这种情况很少发生,但并非不可能)。

标签: bash loops


【解决方案1】:

这是个老问题 - 但基于@GregReynolds 的回答,如果命令仅在最后一次传递的参数上有所不同,请使用此单行代码。单行爱好者的丑陋,丑陋的代码

( ff="" ; for f in * "" ; do [ -n "$ff" ] &amp;&amp; echo $(${f:+false} &amp;&amp; echo $ff alternate params here || echo normal params $ff ) ; ff=$f ; done ) normal params 1 normal params 2 normal params 3 4 alternate params here

【讨论】:

    【解决方案2】:

    以@cdarke (https://stackoverflow.com/a/12298757/415523) 的当前投票最高的答案为基础,如果查看一般的值数组(而不是磁盘上的特定文件),循环代码如下:

    declare -a array
    declare -i length current
    
    array=( a b c d e c )
    length=${#array[@]}
    current=0
    
    for VALUE in "${array[@]}"; do 
      current=$((current + 1))
    
      if [[ "$current" -eq "$length" ]]; then
         echo "$VALUE is the last" 
      else 
         echo "$VALUE"
      fi 
    done
    

    这会产生输出:

    a
    b
    c
    d
    e
    c is the last
    

    这确保只有数组中的 last 项触发替代操作,并且如果数组中的任何其他项与最后一个值重复,则替代操作是 not 调用较早的副本。

    如果是特定目录中文件的路径数组,例如

    array=( $DIR/* )
    

    ...这可能不是一个问题,因为同一目录中的各个文件名几乎肯定是唯一的(除非您有一个 真正 奇怪的文件系统!)

    【讨论】:

    • 虽然不是很干净,但是一个简单的解决方案可以工作,即使是不连续的(索引之间的洞)数组。谢谢你。
    【解决方案3】:

    调用什么来检查当前文件是否是数组中的最后一个?

    首先,您没有使用数组。如果你是,那就很容易了:

    declare -a files
    files=($DIR/*)
    pos=$(( ${#files[*]} - 1 ))
    last=${files[$pos]}
    
    for FILE in "${files[@]}"
    do 
      if [[ $FILE == $last ]]
      then
         echo "$FILE is the last" 
         break
      else 
         echo "$FILE"
      fi 
    done 
    

    【讨论】:

    • 在 4.x 系列的某个时候,bash 开始允许负数组偏移量。
    • 我认为当数组中有两个相等的条目时,此解决方案将不起作用。最好迭代索引,即使用for i in `seq 0 $pos`,然后通过FILE=${files[$i]}检索文件。
    • 既然您提到数组“这很容易”,我可能会指出,这种技术技术上不适用于非唯一项的数组。 if [[ $FILE == $last ]] 子句将匹配与最后一个值具有相同值的 所有 项。现在,由于 OP 显式处理 files,我们可以合理地确定具有唯一值的数组(至少在单个目录中);但对于通用数组,您可能希望在循环中增加一个计数器,并与$pos... 进行比较
    • 添加break 并不能确保它是数组中的最后一项;它只是意味着第一次遇到与数组中最后一个值相同的值时,循环将终止
    • 考虑一个数组ARR=( a b c d e c ):使用您之前的代码(即没有break),这将导致a, b, c is the last, d, e, c is the last 会被回显两次。使用您当前的代码(即使用break),它将导致a, b, c is the last,但不会打印d, e。对于 general 数组,这两种结果在技术上都是错误的(尽管对于 files 不一定)
    【解决方案4】:

    您可以使用 find 来查找文件总数。 然后当你在循环中计数到总数并在总数等于计数时执行你的任务,即最后一个文件。

     f=0
    tot_files=`find  . -iname '*.txt' | wc -l`
     for FILE in $DIR/* 
    do 
    f=($f+1)
    if [[ $f == $tot_files ]];then
    carryout your task
    fi
    done
    

    【讨论】:

      【解决方案5】:

      是什么让文件成为最后一个?它有什么特别之处吗?按名称排序时,它是名称最大的文件吗?

      也许您可以将文件名向后。然后,它是您要特殊处理的第一个文件,而不是最后一个。找出第一个比做最后一个要容易得多:

      for file in $(ls -r1 $dir)
      do
          if [ ! $processedLast ] 
          then
              doSomethingSpecial($file)
              processedLast=1
          else
              doSomethingRegular($file)
          fi
      done
      

      不需要数组。其实,我喜欢chepner关于使用位置参数的回答。

      【讨论】:

        【解决方案6】:

        您可以滥用位置参数,因为它们的作用类似于数组, 但更容易操作。您应该保存旧的位置 参数,或在子shell中执行。

        # Method 1: use a subshell. Slightly cleaner, but you can't always
        # do this (for example, you may need to affect variables in the current
        # shell
        files=( $DIR/* )
        
        (
            set -- "${files[@]}"
            until (( $# == 1 )); do
                doSomethingRegular "$1"
                shift
            done    
            doSomethingSpecial "$1"
        )
        
        # Method 2: save the positional parameters. A bit uglier, but
        # executes everything in the same shell.
        
        files=( $DIR/* )
        oldPP=( "$@" )
        set -- "${files[@]}"
        until (( $# == 1 )); do
            doSomethingRegular "$1"
            shift
        done    
        doSomethingSpecial "$1"
        set -- "${oldPP[@]}"
        

        【讨论】:

          【解决方案7】:

          试试这个

          LAST_FILE=""
          for f in *
          do
                  if [ ! -z $LAST_FILE ]
                  then
                          echo "Process file normally $LAST_FILE"
                  fi
                  LAST_FILE=$f
          done
          if [ ! -z $LAST_FILE ]
          then
                  echo "Process file as last file $LAST_FILE"
          fi
          

          生产

          bash[1051]: ls
          1  2  3  4
          bash[1052]: sh ../last_file.sh
          Process file normally 1
          Process file normally 2
          Process file normally 3
          Process file as last file 4
          

          【讨论】:

          • 这只会正常处理第一个文件。
          • 对不起,我没有仔细阅读您的代码。我想我错过了你不是直接在$f 上工作,而是在$LAST_FILE 上工作。
          • @GregReynolds:我喜欢这个解决方案——只需使用“相移”变量。它简单,便携,可以用作单线:) 感谢分享。为了完整起见,我的一个班轮:( ff="" ; for f in * ; do [ -n "$ff" ] &amp;&amp; echo normal: $ff ; ff=$f ; done ; echo last: $ff )
          【解决方案8】:

          我不知道您正在处理 for 循环中列表的最后一个元素。但是,您可以使用数组,遍历除最后一个元素之外的所有元素,然后在循环外处理最后一个元素:

          files=($DIR/*)
          for file in "${files[@]::${#files[@]}-1}" ; do
              doSomethingRegular "$file"
          done
          doSomethingSpecial "${files[@]: -1:1}"
          

          ${files[@]:offset:length} 的扩展求值为 length 元素的所有以 offset 开头(如果为空,则为开头)的元素。 ${#files[@]}-1 是数组中的元素个数减 1。

          ${files[@]: -1:1} 计算为最后一个元素 - 从末尾算起 -1,长度为 1。空格是必要的,因为 :- 的处理方式与 : - 不同。

          【讨论】:

          • bash 4.x 开始(不确定是4.0 还是更高版本),您可以使用${files[-1]} 代替${files[@]: -1:1}
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多