【问题标题】:Get the newest file based on timestamp根据时间戳获取最新文件
【发布时间】:2010-08-16 17:25:42
【问题描述】:

我是 shell 脚本的新手,所以我需要一些帮助来解决这个问题。

我有一个目录,其中包含以下格式的文件。这些文件位于一个名为 /incoming/external/data 的目录中

AA_20100806.dat
AA_20100807.dat
AA_20100808.dat
AA_20100809.dat
AA_20100810.dat
AA_20100811.dat
AA_20100812.dat

如您所见,文件的文件名包含时间戳。即 [RANGE]_[YYYYMMDD].dat

我需要做的是使用文件名上的时间戳而不是系统时间戳找出这些文件中的哪些具有最新日期,并将文件名存储在变量中并将其移动到另一个目录并将其余的移动到另一个目录.

【问题讨论】:

  • [RANGE] 可以是两个字符的任意组合吗?这有很大的不同,您可能会注意到已经给出的答案。
  • 是的,它们可以不同。而且同一个文件夹将包含其他类型的文件,其名称与上面显示的不同。

标签: bash unix shell


【解决方案1】:

对于那些只想要答案的人,这里是:

ls | sort -n -t _ -k 2 | tail -1

这是引导我来到这里的思考过程。

我假设 [RANGE] 部分可以是任何东西。

从我们所知道的开始。

  • 工作目录:/incoming/external/data
  • 文件格式:[RANGE]_[YYYYMMDD].dat

我们需要在目录中找到最新的 [YYYYMMDD] 文件,并且我们需要存储该文件名。

可用的工具(我只列出了解决这个问题的相关工具......通过练习识别它们变得更容易):

我想我们不需要 sed,因为我们可以使用 ls 命令的整个输出。使用 ls、awk、sort 和 tail,我们可以像这样得到正确的文件(请记住,您必须根据您的操作系统接受的内容检查语法):

NEWESTFILE=`ls | awk -F_ '{print $1 $2}' | sort -n -k 2,2 | tail -1`

那么只要把下划线放回去就行了,应该不会太难。

编辑:我有一点时间,所以我开始修复命令,至少在 Solaris 中使用。

这是令人费解的第一遍(假设目录中的所有文件都采用相同的格式:[RANGE]_[yyyymmdd].dat)。我打赌有更好的方法可以做到这一点,但这适用于我自己的测试数据(事实上,我刚刚找到了更好的方法;见下文):

ls | awk -F_ '{print $1 " " $2}' | sort -n -k 2 | tail -1 | sed 's/ /_/'

...在写这个的时候,我发现你可以这样做:

ls | sort -n -t _ -k 2 | tail -1

我会把它分解成几部分。

ls

很简单...获取目录列表,只是文件名。现在我可以将它通过管道传递到下一个命令中。

awk -F_ '{print $1 " " $2}'

这是 AWK 命令。它允许您获取输入行并以特定方式对其进行修改。在这里,我所做的只是指定 awk 应该在任何有下划线 (_) 的地方中断输入。我使用 -F 选项执行此操作。这给了我每个文件名的两半。然后我告诉 awk 输出前半部分 ($1),后跟一个空格 (" ") ,然后是下半场(2 美元)。请注意,空格是我最初建议中缺少的部分。此外,这是不必要的,因为您可以在下面的排序命令中指定分隔符。

现在输出在每一行被拆分为 [RANGE] [yyyymmdd].dat。现在我们可以对其进行排序:

sort -n -k 2

这将获取输入并根据第二个字段对其进行排序。 sort 命令默认使用空格作为分隔符。在编写此更新时,我找到了 sort 的文档,它允许您指定分隔符,因此不需要 AWK 和 SED。获取 ls 并通过以下排序对其进行管道传输:

sort -n -t _ -k 2

这实现了相同的结果。现在你只想要最后一个文件,所以:

tail -1

如果您使用 awk 分隔文件(这只是增加了额外的复杂性,所以不要这样做 sheepish),您可以使用 sed 再次将空格替换为下划线:

sed 's/ /_/'

这里有一些很好的信息,但我相信大多数人不会像这样深入阅读。

【讨论】:

  • 我试过了,但没用。你能解释一下它到底在做什么。谢谢
  • 嗯,我测试后更新了。我不得不在我的 awk 命令中修复一些东西,然后发现它实际上是不需要的。解决方案在最上面,解释很长而且没有必要,但我喜欢写它。
  • 为我工作。请生下我的孩子。
  • 我在 SO 上遇到的最佳答案之一。谢谢。
【解决方案2】:

这应该可行:

newest=$(ls | sort -t _ -k 2,2 | tail -n 1)
others=($(ls | sort -t _ -k 2,2 | head -n -1))

mv "$newest" newdir
mv "${others[@]}" otherdir

如果文件名中有空格,则它不会起作用,尽管您可以修改 IFS 变量来影响它。

【讨论】:

  • 嗨,圆括号是干什么用的?
  • @ziggy:你是指第二行的外部集合吗?他们创建了一个用于最后一行的数组。
  • 嗨丹尼斯,我指的是内圆括号和外圆括号。我尝试运行上述内容,但括号导致语法错误。我正在使用 bourne shell。这些是 korn shell 特定的构造吗?
  • @ziggy:内括号(实际上是$())用于命令替换。它们比反引号工作得更好,但功能与反引号相同。我展示的语法适用于 Bash,您标记了您的问题。它也应该与 ksh 一起使用。 $() 应该在 sh 中工作,但数组语法不能,因为 Bourne shell 没有数组。
  • 我喜欢这个解决方案。如果 ziggy 想在脚本中执行此操作,他总是可以在脚本开头指定 #!/usr/sh ,不是吗? Bourne shell 中没有什么是不能在 sh 中完成的。
【解决方案3】:

试试:

$ ls -lr

希望对你有帮助。

【讨论】:

  • 您好,难道不使用文件的系统时间戳对其进行排序吗?我对实际文件名的时间戳感兴趣。谢谢
  • 不,它会根据您的语言环境按名称对文件进行排序。如果要按系统时间戳排序,则需要 -t 标志。
【解决方案4】:

用途:

ls -r -1 AA_*.dat | head -n 1

(假设没有其他文件匹配AA_*.dat

【讨论】:

    【解决方案5】:
    ls -1 AA* |sort -r|tail -1
    

    【讨论】:

      【解决方案6】:

      由于文件的命名约定,字母顺序与日期顺序相同。我很确定在 bash '*' 按字母顺序展开(但在手册页中找不到任何证据),ls 当然可以,所以文件带有最新日期,按字母顺序排列为最后一个。

      因此,在 bash

      mv $(ls | tail -1) first-directory
      mv * second-directory
      

      应该做的伎俩。

      如果您想更具体地选择文件,请将 * 替换为其他内容 - 例如 AA_*.dat

      【讨论】:

      • 这也有效,但我试图避免依赖系统为我进行排序(即通过 ls cmd)。谢谢
      • 你为什么不想依赖ls——你说的“系统”是什么意思?
      【解决方案7】:

      我对此的解决方案与其他人类似,但更简单一些。

      ls -tr | tail -1
      

      实际上是依靠 ls 对输出进行排序,然后使用 tail 获取最后列出的文件名。

      如果您需要的文件名有前导点(例如 .profile),此解决方案将不起作用。

      如果文件名包含空格,此解决方案确实有效。

      【讨论】:

        猜你喜欢
        • 2022-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-14
        • 1970-01-01
        • 1970-01-01
        • 2021-06-05
        相关资源
        最近更新 更多