Powershell to wake up multiple media drives simultaneously

独自空忆成欢 提交于 2019-12-23 12:36:30

问题


I have a server with lots of media drives ~43TB. An areca 1882ix-16 is set to spin the drives down after 30 minutes of inactivity since most days an individual drive is not even used. This works nicely to prevent unnecessary power and heat. In this case the drives still show up in windows explorer but when you click to access them it takes about 10 seconds for the folder list to show up since it has to wait for the drive to spin up.

For administrative work I have a need to spin up all the drives to be able to search among them. Clicking on each drive in windows explorer and then waiting for it to spin up before clicking the next drive is very tedious. Obviously multiple explorer windows makes it faster but it is still tedious. I thought a powershell script may ease the pain.

So I started with the following:

$mediaDrives = @('E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:', 'L:',
    'M:','N:', 'O:', 'P:', 'Q:', 'R:', 'S:')
get-childitem $mediaDrives  | foreach-object -process { $_.Name }

This is just requesting that each drive in the array have its root folder name listed. That works to wake the drive but it is again a linear function. The script pauses for each drive before printing. Looking for a solution as to how to wake each drive simultaneously. Is there a way to multi-thread or something else?


回答1:


Here's a script that will do what you want, but it must be run under powershell using the MTA threading mode (which is the default for powershell.exe 2.0, but powershell.exe 3.0 must be launched with the -MTA switch.)

#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)"  
}

So, for example, save as warmup.ps1 and run like: powershell -mta c:\scripts\warmup.ps1

To read more about runspace pools and the general technique above, take a look at my blog entry about runspacepools:

http://nivot.org/blog/post/2009/01/22/CTP3TheRunspaceFactoryAndPowerShellAccelerators

I chose 8 pretty much arbitrarily for the parallelism factor - experiment yourself with lower or higher numbers.




回答2:


Spin up a separate powershell instance for each drive or use workflows in PowerShell 3.0. Anyhow, you can pass drives directly to the Path parameter and skip Foreach-Object all togeteher:

Get-ChildItem $mediaDrives



回答3:


Have you considered approaching this with the Start-Job cmdlet:

$mediaDrives = @('E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:')
$mediaDrives | ForEach-Object {
  Start-Job -ArgumentList $_ -ScriptBlock {param($drive)
    Get-ChildItem $drive
  }
}

The only clever part is that you need to use the -ArgumentList parameter on the Start-Job cmdlet to pass the correct value through for each iteration. This will create a background task that runs in parallel with the execution of the script. If you are curious




回答4:


If you don't want to wait, well, don't wait: start those wake-up calls in the background.

In bash one would write

foreach drive ($mediadrives) {tickle_and_wake $drive &}

(note the ampersand, which means: start the command in the background, don't wait for it to complete)

In PowerShell that would translate to something like

foreach ($drive in $mediadrives)  {
   Start-Job {param($d) tickle_and_wake $d} -Arg $drive 
}

If you want confirmation that all background jobs have completed, use wait in bash or Wait-Job in Powershell



来源:https://stackoverflow.com/questions/14050190/powershell-to-wake-up-multiple-media-drives-simultaneously

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!