我有一个服务器,有很多媒体驱动器~43TB。areca 1882ix-16被设置为在30分钟不活动后旋转驱动器,因为大多数日子单个驱动器甚至不使用。这很好地防止了不必要的电力和热量。在这种情况下,驱动器仍然显示在windows资源管理器中,但当你点击访问它们时,文件夹列表需要大约10秒才能显示,因为它必须等待驱动器启动。
对于管理工作,我需要启动所有的驱动器,以便能够在其中进行搜索。在windows资源管理器中单击每个驱动器,然后在单击下一个驱动器之前等待它启动是非常繁琐的。显然,多个资源管理器窗口使它更快,但它仍然是乏味的。我想一个powershell脚本可以减轻痛苦。
所以我从下面开始:
$mediaDrives = @('E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:', 'L:',
'M:','N:', 'O:', 'P:', 'Q:', 'R:', 'S:')
get-childitem $mediaDrives | foreach-object -process { $_.Name }
这只是请求数组中的每个驱动器都列出其根文件夹名称。这可以唤醒驱动器,但它还是一个线性函数。脚本在打印之前暂停每个驱动器。寻找如何同时唤醒每个驱动器的解决方案。是否有办法多线程或其他东西?
这里有一个脚本可以做你想做的事情,但是它必须在powershell下使用MTA线程模式运行(这是powershell.exe 2.0的默认模式,但是powershell.exe 3.0必须使用-MTA开关启动)
#require -version 2.0
# if running in ISE or in STA console, abort
if (($host.runspace.apartmentstate -eq "STA") -or $psise) {
write-warning "This script must be run under powershell -MTA"
exit
}
$mediaDrives = @('E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:', 'L:',
'M:','N:', 'O:', 'P:', 'Q:', 'R:', 'S:')
# create a pool of 8 runspaces
$pool = [runspacefactory]::CreateRunspacePool(1, 8)
$pool.Open()
$jobs = @()
$ps = @()
$wait = @()
$count = $mediaDrives.Length
for ($i = 0; $i -lt $count; $i++) {
# create a "powershell pipeline runner"
$ps += [powershell]::create()
# assign our pool of 8 runspaces to use
$ps[$i].runspacepool = $pool
# add wake drive command
[void]$ps[$i].AddScript(
"dir $($mediaDrives[$i]) > `$null")
# start script asynchronously
$jobs += $ps[$i].BeginInvoke();
# store wait handles for WaitForAll call
$wait += $jobs[$i].AsyncWaitHandle
}
# wait 5 minutes for all jobs to finish (configurable)
$success = [System.Threading.WaitHandle]::WaitAll($wait,
(new-timespan -Minutes 5))
write-host "All completed? $success"
# end async call
for ($i = 0; $i -lt $count; $i++) {
write-host "Completing async pipeline job $i"
try {
# complete async job
$ps[$i].EndInvoke($jobs[$i])
} catch {
# oops-ee!
write-warning "error: $_"
}
# dump info about completed pipelines
$info = $ps[$i].InvocationStateInfo
write-host "State: $($info.state) ; Reason: $($info.reason)"
}
例如,保存为warmup.ps1
,然后运行:powershell -mta c:scriptswarmup.ps1
要了解更多关于运行空间池和上述一般技术的信息,请查看我关于runspacepools的博客文章:
http://nivot.org/blog/post/2009/01/22/CTP3TheRunspaceFactoryAndPowerShellAccelerators我选择了8作为并行因子,这几乎是任意的——你可以用更低或更高的数字来做实验。
为每个驱动器启动一个单独的powershell实例或使用powershell 3.0中的工作流。无论如何,您可以直接将驱动器传递给Path参数,并一起跳过Foreach-Object:
Get-ChildItem $mediaDrives
您是否考虑过使用Start-Job cmdlet来解决这个问题:
$mediaDrives = @('E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:')
$mediaDrives | ForEach-Object {
Start-Job -ArgumentList $_ -ScriptBlock {param($drive)
Get-ChildItem $drive
}
}
唯一聪明的部分是您需要在Start-Job cmdlet上使用-ArgumentList参数来为每次迭代传递正确的值。这将创建一个与脚本执行并行运行的后台任务。如果你很好奇
如果你不想等待,那就不要等待:在后台启动那些唤醒电话。
在bash
中可以写
foreach drive ($mediadrives) {tickle_and_wake $drive &}
(注意&符号,这意味着:在后台启动命令,不要等待它完成)
在PowerShell中会转换成类似
的内容foreach ($drive in $mediadrives) {
Start-Job {param($d) tickle_and_wake $d} -Arg $drive
}
如果您想确认所有后台任务已经完成,在Powershell
bash
或Wait-Job
中的wait
。