【问题标题】: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

【问题讨论】:

标签: powershell


【解决方案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 '\d{6}$' },以仅获取名称以扩展名前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,别担心。我很高兴它有所帮助。
猜你喜欢
  • 1970-01-01
  • 2018-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-09
  • 1970-01-01
相关资源
最近更新 更多