扫描1000万个文件夹,仅压缩无子文件夹

时间:2014-05-10 01:28:37

标签: powershell zip

我的情况是我有3亿封电子邮件,在外部QNAP NAS上的1000万个文件夹中排序。文件只存储在最下面的无子文件夹中,通常是6个文件夹深。我现在的问题是我需要将每个无子文件子文件夹移出NAS,压缩它,然后将其作为自己命名的zip移回。

尝试使用powershell做到这一点,但不幸的是服务器是一只老狗,需要采用旧的yeller风格,并运行win 2k3。如果可以在QNAP的Linux系统上更容易或更有效地完成,那将会很酷。但说实话,安装Samba以连接到Windows共享对我来说是一个挑战。 NAS目前有0个字节空闲。 :)

我写了这个powershell脚本,但是它仍在排队子项目文件夹,目前为3GB内存。我预计它会达到极限并失败。

#Script to clean up the OrchestriaCache NAS

$pwdZ = 'c:\temp\test\'
$zip = 'c:\temp\7z.exe'
$dest = 'c:\temp\zip'

$a = get-childitem $pwdZ -recurse | where-object {$_.PSISContainer -eq $true}
$b = $a | where-object {$_.GetFiles().Count -ge 1} #| select-object FullName

write-host $b.FullName
$ctr = 0
$cnt = $b.Count
$b | foreach-object { 
    $ctr++
    write-host $('[' + $ctr + '\' + $cnt + '] Zipping: ' + $_.fullname)     -foregroundcolor red
move-item -path $($_.Fullname + '\*.*') -force -destination $dest
cmd /c $('"c:\temp\7z.exe a ' + $($_.FullName + '\' + $_.Name + '.zip') + ' '+ $($dest + '\*"'))
}

如何改进?我正在考虑为所有400多个根子文件夹启动一个外部PowerShell进程,但这会对NAS造成太多的IO争用。

编辑:出现了内存错误,我很害怕:

Where-Object : Exception of type 'System.OutOfMemoryException' was thrown.
At D:\BKeys\CacheCleanup.ps1:7 char:48
+ $a = get-childitem $src -recurse | where-object <<<<  {$_.PSISContainer -eq $
true}
    + CategoryInfo          : NotSpecified: (:) [Where-Object], OutOfMemoryExc
   eption
    + FullyQualifiedErrorId : System.OutOfMemoryException,Microsoft.PowerShell
   .Commands.WhereObjectCommand

You cannot call a method on a null-valued expression.
At D:\BKeys\CacheCleanup.ps1:8 char:36
+ $b = $a | where-object {$_.GetFiles <<<< ().Count -ge 1} #| select-object Ful
lName
    + CategoryInfo          : InvalidOperation: (GetFiles:String) [], RuntimeE
   xception
    + FullyQualifiedErrorId : InvokeMethodOnNull


[1\] Zipping:

2 个答案:

答案 0 :(得分:0)

未经测试,但在内存加载方面应该更容易:

#Script to clean up the OrchestriaCache NAS

$pwdZ = 'c:\temp\test\'
$zip = 'c:\temp\7z.exe'
$dest = 'c:\temp\zip'

#$a = get-childitem $pwdZ -recurse | where-object {$_.PSISContainer -eq $true}

#$b = $a | where-object {$_.GetFiles().Count -ge 1} #| select-object FullName

$b = cmd /c dir $pwdZ /b /s /ad |
      where-object {([IO.Directory]::GetFiles($_)).Count -ge 1} 


#write-host $b.FullName
write-host $b

$ctr = 0
$cnt = $b.Count

$b | foreach-object {
    $ctr++
    write-host $('[' + $ctr + '\' + $cnt + '] Zipping: ' + $_)     -foregroundcolor red
    $Name = $_.split('\')[-1]

move-item -path $($_ + '\*.*') -force -destination $dest
cmd /c $('"c:\temp\7z.exe a ' + $($_ + '\' + $Name + '.zip') + ' '+ $($dest + '\*"'))
}

对于大型目录结构,legacy dir命令比get-childitem快得多且内存密集度更低。 / b / s / ad开关将使其递归并仅返回目录的fullname字符串。

通过过滤不包含文件的文件夹来消除保存完整目录列表的中间变量$ a。通过拆分反斜杠并获取最后一个元素来解析名称。您可以通过使zip例程成为过滤器或管道功能来进一步优化这一点,以便在找到包含文件的目录时立即开始压缩文件。如果您添加其他代码以保存已经压缩的目录列表,或者检查目录中是否存在.zip文件,则可以使其重新启动。

答案 1 :(得分:0)

你问了一个powershell脚本,但我想你只想完成工作。

如果您为前几个拉链准备并留出足够的空间,那么批量脚本可能会相当容易并且规格较低。

将7-zip命令添加到内部循环,并添加* .eml或任何文件。

@echo off
pushd "\\server\share" || goto :EOF
   for /d /r %%a in (*) do (
     echo processing "%%a"
       pushd "%%a"
         dir /b /ad 2>nul | findstr "^" >nul || (
             echo Folder "%%a" has no subdirectories
             echo 7zip with *.eml 
             echo and then nuke the files
             )
       popd
  )
popd
echo done
pause