我的情况是我有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:
答案 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