【问题标题】:Find highest numbered filename in a directory where names start with digits在名称以数字开头的目录中查找编号最高的文件名
【发布时间】:2010-12-07 21:46:49
【问题描述】:

我有一个目录,其中的文件如下所示:

001_something.php  002_something_else.php
004_xyz.php        005_do_good_to_others.php

我最终想创建一个新的空 PHP 文件,其名称以系列中的下一个数字开头。

LIST=`exec ls $MY_DIR | sed 's/\([0-9]\+\).*/\1/g' | tr '\n' ' '`

前面的代码给了我一个这样的字符串:

LIST='001 002 004 005 '

我想抓住那个 005,加一,然后用那个数字生成新的文件名。如何在 BASH 中做到这一点?

【问题讨论】:

  • 这是作为 Web 应用程序的一部分运行的吗?如果 Web 服务器同时接受多个请求(大多数都接受),当进程 A 和 B 同时进行检查然后创建具有相同编号的文件时,您将如何防止竞争条件?
  • 不,这是一个 shell 脚本的一部分,用于模拟 rails 的“生成迁移”工具,用于使用数据库迁移模式的红头继子实现的 php 应用程序。

标签: bash shell sed ls


【解决方案1】:

我发布这个答案是为了提出核心问题“在名称以数字开头的目录中查找编号最高的文件名”的最小解决方案:

ls -1 "$MyDir" | sort -hr | head -n 1

第一个语句将 MyDir 中的文件作为一个带有 -1 选项的衬里。这通过管道传递给 sort 函数,该函数使用 -h 以人类数字顺序排序,使用 -r 以相反的顺序(最高数字在前)排序。这通过管道传递给 head 函数,该函数仅提供 -n 最重要的结果(示例中为 1)。 答案考虑了人类数字排序顺序,即 221 高于 67。它是一个 POSIX shell 解决方案,兼容性非常好。

【讨论】:

    【解决方案2】:
    1. touch "newfile_$(printf "%03d" $(echo $(ls 00?_*.php|sed 's/_.*//'|sort -rn|head -1)+1|bc))"

      2.

      num=$(ls 00?*.php|sed 's/.*//'|sort -rn|head -1)
      触摸 $(printf "newfile_%03d" $((num+1)))
      
      

    【讨论】:

      【解决方案3】:

      这似乎更简单。

      ls [0-9]* | sort -rn | awk '{FS="_"; printf "%03d_new_file.php\n",$1+1;exit}'
      

      【讨论】:

        【解决方案4】:

        仅使用标准工具,以下将为您提供新文件的前缀 (006):

        ls [0-9]* | sed 's/_/ _/' | sort -rn | awk '{printf "%03d", $1 + 1; exit}'

        【讨论】:

        • 如果001文件不存在,这个命令不应该打印出001吗?
        • 这不适用于包含很多很多文件的目录 - 输出是:bash: /bin/ls: Argument list too long
        • @John:只需将ls [0-9]* 替换为ls | grep ^[0-9]
        【解决方案5】:

        快速、无子外壳和无循环(也处理带空格的文件名)*:​​

        list=([0-9]*)
        last=${list[@]: -1}
        nextnum=00$((10#${last%%[^0-9]*} + 1))
        nextnum=${nextnum: -3}
        touch ${nextnum}_a_new_file.php
        

        给定您的示例文件,输出将是:

        006_a_new_file.php
        

        【讨论】:

          【解决方案6】:
          $ LIST=($LIST)
          $ newfile=`printf %03d-whatever $((10#${LIST[${#LIST}]}+1))`
          $ echo $newfile
          006-whatever
          

          所以,这是特定于 bash 的。下面是一个 any-posix-shell-include-bash 解决方案,我想更简单的方法是可能的。

          $ cat /tmp/z
          f () {
              eval echo \${$#} | sed -e 's/^0*//'
          }
          LIST='001 002 004 005 '
          newname=`printf %03d-whatever $(($(f $LIST) + 1))`
          echo $newname
          $ sh /tmp/z
          006-whatever
          $ 
          

          【讨论】:

          • 那是因为以“0”开头的数字是八进制
          • +1。我会坚持使用 bash 解决方案,因为这是一个特定的标签。
          【解决方案7】:

          您需要整个 LIST 吗?

          如果没有

          LAST=`exec ls $MY_DIR | sed 's/\([0-9]\+\).*/\1/g' | sort -n | tail -1`
          

          只会给你005部分和

          printf "%03d" `expr 1 + $LAST`
          

          将打印序列中的下一个数字。

          【讨论】:

          • 或者:printf "%03d" $(( 1 + $LAST ))
          • 我不需要exec 部分
          【解决方案8】:
          $ for file in *php; do last=${file%%_*} ; done
          $ newfilename="test.php"
          $ printf "%03d_%s" $((last+1)) $newfilename
          006_test.php
          

          我让你来创建新文件

          【讨论】:

            【解决方案9】:

            这是一个解决方案(你需要 bc):

            #!/bin/bash
            
            LIST='001 002 008 005 004 002 '
            a=0
            
            for f in $LIST
            do
                t=$(echo $f + 1|bc)
                if [ $t -ge $a ]
                then
                    a=$t
                fi
            done
            
            printf "%03d\n" $a
            

            【讨论】:

            • 你也可以使用dc: t=$(echo $f 1+pq | dc),或者expr: t=$(expr $f + 1),或者bash数学: t=$(($f + 1))
            猜你喜欢
            • 2017-08-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多