根据文件名中的日期戳移动文件

Posted

技术标签:

【中文标题】根据文件名中的日期戳移动文件【英文标题】:Move Files Based on Date Stamp in File Name 【发布时间】:2019-03-21 00:00:09 【问题描述】:

不幸的是,我是 PowerShell 的新手,我有许多文件希望每月使用 PowerShell 存档。每个文件的文件名中都有一个 YYYYMM 日期戳。我想移动日期戳超过 24 个月的文件。

例子:

文件1_201903.txt file2_201902.txt ... file3_201703.txt(应该存档) file4_201702.txt(应该存档)

请注意,源文件位于包含多个子文件夹的目录中。我希望脚本检查所有子文件夹。这些文件夹不需要在目标中复制。

这是我迄今为止尝试过的:

$SourceDir = 'C:\source'
$DestDir   = 'C:\destination'
$YearsAgo  = 2

$Then = (Get-Date).AddYears(-$YearsAgo).Date

Get-ChildItem -Path $SourceDir |
    Where-Object 
        $DatePart = ($_.BaseName -split '_')[1]

        $FileDate = [DateTime]::ParseExact($DatePart, 'yyyyMMdd', [CultureInfo]::CurrentCulture)

        $FileDate -lt $Then
     |
    Move-Item -Destination $DestDir

【问题讨论】:

由于 SO 不是代码编写服务,您必须自己完成。你已经尝试过什么?请显示您的代码并解释您卡在哪里或没有按预期工作或您遇到什么错误。您可能会(再次)阅读以下帮助主题:the Tour、How to create a Minimal, Complete, and Verifiable example 和 How do I format my posts using Markdown or html?。 帖子更新了我迄今为止尝试过的格式和代码。 【参考方案1】:

文件名中的日期部分没有日期值。因此格式应该是yyyyMM,而不是yyyyMMdd

由于格式是可排序的字符串,您不必转换为 DateTime 对象,可以继续比较字符串:

$SourceDir = 'C:\source'
$DestDir   = 'C:\destination'
$YearsAgo  = -2
$Then      = '0:yyyyMM' -f (Get-Date).AddYears($YearsAgo)  # returns a String "201703"

Get-ChildItem -Path $SourceDir | ForEach-Object 
    $DatePart = ( $_.BaseName -split '_' )[1]
    # comparing sortable date strings
    if ($DatePart -lt $Then) 
        $_ | Move-Item -Destination $DestDir
     

如果您确实想比较 DateTime 对象,应该这样做:

$SourceDir = 'C:\source'
$DestDir   = 'C:\destination'
$YearsAgo  = -2

$RefDate   = ('0:yyyyMM' -f (Get-Date).AddYears($YearsAgo))  # returns a String "201703"
# convert this string into a DateTime object
$Then      = [DateTime]::ParseExact( $RefDate, 'yyyyMM', [cultureinfo]::CurrentCulture )

Get-ChildItem -Path $SourceDir | ForEach-Object 
    $DatePart = ( $_.BaseName -split '_' )[1]
    $FileDate = [DateTime]::ParseExact( $DatePart, 'yyyyMM', [cultureinfo]::CurrentCulture )
    # comparing DateTime objects
    if ($FileDate -lt $Then) 
        $_ | Move-Item -Destination $DestDir
     

【讨论】:

您的字符串比较代码运行没有错误,但它会移动所有文件,而不是仅移动文件名超过 2 年的文件。它还将源中的子文件夹移动到目标。如果它们被复制而不是移动就可以了。 @nate_ak 两种变体都可以按预期使用您的示例文件,错误似乎就在您身边。 @Theo 我的进一步测试证实,如果它不必考虑源目录中的子文件夹,它就可以工作。我希望脚本在指定源的子文件夹中查找文件。 @nate_ak 在这种情况下,我建议您尝试将-File -Recurse 开关添加到Get-ChildItem cmdlet。您也可以考虑在ForEach-Object ... 之前添加| Where-Object $_.BaseName -match '\d6$' ,以仅获取名称以扩展名前6 位数字结尾的文件。 @Theo 感谢您将我推向正确的方向,因为我对 Powershell 完全陌生。在这里快速学习。添加-File Recurse` 肯定有帮助。但是,我改为使用子字符串来计算 $DatePart 变量,因为我的文件名中的下划线数量不一致。但日期戳始终是名称的最后 6 个字符。所以我用$DatePart = ( $_.name.substring($_.name.length-10, 6) )【参考方案2】:

实际上看起来并没有那么糟糕。 ;-) 但是您的Where-Object 中的-FilterScript 块需要稍作调整:

$SourceDir = 'C:\source'
$DestDir   = 'C:\destination'
$YearsAgo  = -2
$Then = ( Get-Date ).AddYears( $YearsAgo ).Date

Get-ChildItem -Path $SourceDir -Recurse |
    Where-Object 
        [datetime]::ParseExact( $(( $_.BaseName -split '_' )[1]), 'yyyyMMdd', [cultureinfo]::CurrentCulture ) -lt $Then
     |
        Move-Item -Destination $DestDir

您可以对Foreach-Object 使用更具描述性的方式。有时这更容易阅读/理解/遵循:

Get-ChildItem -Path $SourceDir -Recurse |
    ForEach-Object
        $DatePart = ( $_.BaseName -split '_' )[1]
        $FileDate = [datetime]::ParseExact( $DatePart, 'yyyyMMdd', [cultureinfo]::CurrentCulture )
        if ($FileDate -lt $Then) 
            Move-Item -Path $_.FullName -Destination $DestDir
         
    

很遗憾,我目前无法测试。所以请尝试让我知道。 ;-)

【讨论】:

感谢您的帮助,但您的代码产生了以下错误。我的想法是这是由于文件名(yyyyMM)中的日期格式与ParseExact 函数中指定的格式不匹配。我相应地更新了代码,并且脚本运行没有错误。但是,它不是只检索 2 年或更早的文件,而是检索所有文件。它还移动了整个文件夹结构,没有留下任何东西。我想保留源中的文件夹结构。 Exception calling "ParseExact" with "3" argument(s): "String was not recognized as a valid DateTime. 再次感谢您的帮助。这为我指明了正确的方向。上面 Theo 的代码(有一些我自己的小调整)是我最终用于我的解决方案的代码。显然我没有足够的声誉将您的解决方案标记为有用。但它是! @nate_ak,别担心。我很高兴它有所帮助。

以上是关于根据文件名中的日期戳移动文件的主要内容,如果未能解决你的问题,请参考以下文章

sh Bash脚本根据exif数据和文件时间戳移动图像

遍历目录树并将日期戳附加到文件名

根据修改日期移动文件夹的脚本

sh 根据样本间隙移动文件的日期时间

将项目移动到 Outlook 中的文件夹时设置自定义值

使用批处理文件按日期清理目录