列出文件的最新版本的Bash/DOS/PowerShell脚本?

5
我们有一个包含50个报告的列表,这些报告根据一定的条件被倒入到不同的文件夹中。所有报告都有标准名称,例如D099C.LIS,D18A0.LIS等。
有时一个报告会存在于多达5个不同的位置,我需要生成每个报告最新版本的所有位置的列表。
我可以很容易地使用代码来完成,或者将“dir”或“ls”输出重定向到文本文件中,然后在Excel中进行操作,但我更喜欢使用DOS、Bash或PowerShell中更简单(希望是一行)的解决方案。
到目前为止,在PowerShell中,我想到的最好的解决方案是:
ls -r -fi *.lis | sort @{expression={$_.Name}}, @{expression={$_.LastWriteTime};Descending=$true} | select Directory, Name, lastwritetime

这将递归列出所有扩展名为*.lis的文件,然后按名称(升序)和日期(降序)进行排序,并显示目录、名称和日期。

这将产生以下输出:

C:\reports\LESE            D057A.LIS                  28/01/2009 09:00:43
C:\reports\JCSW            D057A.LIS                  27/01/2009 10:50:21
C:\reports\ALID            D075A.LIS                  04/02/2009 12:34:12
C:\reports\JCSW            D075B.LIS                  05/02/2009 10:07:15
C:\reports\ALID            D075B.LIS                  30/01/2009 09:14:57
C:\reports\BMA3            D081A.LIS                  01/09/2008 14:51:36

我现在需要做的显然是删除那些不是最新版本的文件,以便输出结果看起来像这样(目前格式不是我所担心的问题):
C:\reports\LESE            D057A.LIS                  28/01/2009 09:00:43
C:\reports\JCSW            D075B.LIS                  05/02/2009 10:07:15
C:\reports\BMA3            D081A.LIS                  01/09/2008 14:51:36

有人有任何想法吗?

[编辑] 对于这个问题,有一些好的想法和答案。不幸的是,我不能将所有答案都标记为已接受,但EBGreen(已编辑)的答案可以直接使用。我会在验证后将可行的解决方案添加到此处。

bash:

 ls -lR --time-style=long-iso | awk 'BEGIN{OFS="\t"}{print $5,$6,$7,$8}' | grep ".LIS" | sort -k4 -k2r -k3r | uniq -f3
 ls -lR --time-style=long-iso | awk 'BEGIN{OFS="\t"}{print $5,$6,$7,$8}' | grep ".LIS" | sort -k4 -k2r -k3r | awk '!x[$4]++'

PowerShell:

  ls -r -fi *.lis | sort @{expression={$_.Name}}, @{expression={$_.LastWriteTime};Descending=$true} | select Directory, Name, lastwritetime | Group-Object Name | %{$_.Group | Select -first 1}
  ls -r . *.lis | sort -desc LastWriteTime | group Name | %{$_.Group[0]} | ft Directory,Name,LastWriteTime
  ls -r -fi *.lis | sort @{expression={$_.Name}}, @{expression={$_.LastWriteTime};Descending=$true} | unique | ft Directory,Name,LastWriteTime
8个回答

8
ls -r -fi *.lis | sort @{expression={$_.Name}}, @{expression={$_.LastWriteTime};Descending=$true} | select Directory, Name, lastwritetime | Group-Object Name | %{$_.Group | Select -first 1}

太棒了!我以前从未在排序中使用过@{Name;Expression}类型的哈希表。 - Peter Seale

2
在bash中,您可以通过uniq将答案进行管道传输。我不确定您的bash 1-liner结果的确切结构,但是正确的-w N和-s N参数应该可以做到这一点。

运行成功,谢谢: ls -lR --time-style=long-iso | awk 'BEGIN{OFS="\t"}{print $5,$6,$7,$8}' | grep ".LIS" | sort -k4 -k2r -k3r | uniq -f3 - ilitirit
很酷。你可能可以用awk完成所有操作(它有关联数组),但你刚才评论的代码同样易读。 - Nick Fortescue

2

PowerShell 中的另一种选择,更像是“脚本”:

ls -r . *.lis | sort LastWriteTime | %{$f=@{}} {$f[$_.Name]=$_} {$f.Values} | ft Directory,Name,LastWriteTime
  1. 递归获取文件
  2. 按最后写入时间升序排序
  3. 初始化一个哈希映射(关联数组)
  4. 对于每个文件,使用名称作为键进行分配 - 后面的条目将覆盖前面的条目
  5. 获取哈希映射的值(不包括键)
  6. 格式化为表格

注意,FileInfo对象在整个流程中保留。您仍然可以访问任何属性/方法的对象或以任何方式格式化它们。


1
问题似乎在于根据特定字段找到唯一值。可以使用awk来解决这个问题。看到了这篇博客文章,其中提供了一种方法。 例如,在bash中可以执行以下操作:

find . -name "*.lis" -print | xargs ls -tr | awk -F/ '!x[$NF]++'


我发现直接使用ls比从find中流式传输结果更容易,因为数据的排序方式不同。不过,我已经考虑了您提出的使用awk作为可能的解决方案。 - ilitirit

1

Powershell:

ls -r . *.lis | sort -desc LastWriteTime | sort -u Name | ft Directory,Name,LastWriteTime

说明:

  1. 递归获取文件
  2. 按最后写入时间降序排序文件
  3. 按名称排序文件,选择唯一文件(仅第一个)。
  4. 使用目录、名称和时间格式化生成的 FileInfo 对象表格

不依赖于 sort 稳定性的替代方案:

ls -r . *.lis | sort -desc LastWriteTime | group Name | %{$_.Group[0]} | ft Directory,Name,LastWriteTime
  1. 递归获取文件
  2. 按LastWriteTime降序排序文件
  3. 按名称对文件进行分组
  4. 对于每个组,选择该组的第一个(索引为零)项
  5. 使用目录、名称和时间格式化生成的FileInfo对象表格

这个命令不起作用,“sort -u Name”重新排序了数据。它没有遵守原始的按日期排序。这就是为什么我使用了“sort @{expression={$.Name}}, @{expression={$.LastWriteTime};Descending=$true}”。 - ilitirit
啊,但是“sort”是稳定的,即如果排序键相等,则保留它们的初始顺序。然而,我必须承认这是经验性的,我无法从文档中验证。我已经编辑并添加了一种替代方案。 - Uffe
我已经测试了原始版本,但不幸的是它无法工作。稍后我会尝试你的第二个版本。 - ilitirit
官方文档中的至少一个示例提到group是稳定的(在分组之前进行排序)。 - Uffe

0

你会使用Perl吗?类似这样:

你的命令 | perl 'while (<STDIN>) { ($dir,$name,$date) = split; $hash{$name} = ($dir,$date);} foreach (keys %hash) { print "$hash{$}[0] $ $hash{$_}[1]\n"; }'

细节可能有误(我已经太久没用过Perl了),但基本思路是保持一个以文件名为键的结果哈希表,并在遇到新条目时始终覆盖先前的条目。只要输入行的顺序正确,你将只得到最近被访问的文件。


0

ls -ARFlrt | awk '{print $6,$7,$8}'|grep 2010|sort -n

我在寻找类似的东西。上面的命令帮助我在bash中得到了我需要的清单。 grep是可选的(当然)。 \谢谢


-1

$f = ls -r -fi *.lis | sort name,lastWriteTime -desc

# 删除文件请移除 -whatIf

$f[1..$f.length] | Remove-Item -whatIf


这只是列出所有文件(然后删除它们)。我不想看到所有文件,只想看到最新版本。 - ilitirit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接