给批处理/PowerShell添加超时功能

14
$fullnamexp = ((net user $winxp /domain | Select-String "Full Name") -replace "Full Name","").Trim();

如果无法找到 $winxp,命令将会挂起,是否有一个超时选项可以在5-10秒后继续执行?不确定应该放在哪里。
编辑:我使用此命令来获取用户名。
$reg  = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $tag1)
$key  = $reg.OpenSubKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon')
$winxp = $key.GetValue('DefaultUserName') -replace '^.*?\\'

$winxp是一个登录名,比如ajstepanik然后我把它放进去:$fullnamexp = ((net user $winxp /domain | Select-String "Full Name") -replace "Full Name","").Trim();

2014年1月21日更新

 $timeoutSeconds = 5
$code = {
    ((net user $winxp /domain | Select-String "Full Name") -replace "Full Name","").Trim(); # your commands here, e.g.
}
$j = Start-Job -ScriptBlock $code
if (Wait-Job $j -Timeout $timeoutSeconds) { $fullnamexp = Receive-Job $j }
Remove-Job -force $j

这是你只做一次实例,还是在循环中进行多个不同用户的迭代? - mjolinor
我在一个PowerShell脚本中两次使用上述命令,一次用于Windows XP,一次用于Windows 7,该脚本只能为其中之一工作,因此其中一个将挂起,我希望它能更快地超时。更大的脚本提取登录名,我在这里发布的这行代码将该用户名转换为人员的全名,但由于我让更大的脚本检查winxp和win7的注册表,它只会找到其中一个,这就是为什么我希望如果可能的话进行超时的原因。 - Aaron
6个回答

24

虽然 @mjolinor 可能为您提供了替代方法,但这里是对您一般问题的直接回答:如何在PowerShell中强制执行超时?

将您想要限时的内容包装在脚本块中,作为一个作业运行,然后使用 Wait-Job cmdlet 来限制操作时间。当超时时间结束或脚本块完成时,Wait-Job 将会返回。在 Wait-Job 返回后,如果有必要,您可以检查作业状态 ($j.state),以确定它是否被中断。

$timeoutSeconds = 5 # set your timeout value here
$j = Start-Job -ScriptBlock {
    # your commands here, e.g.
    Get-Process
    }
"job id = " + $j.id # report the job id as a diagnostic only
Wait-Job $j -Timeout $timeoutSeconds | out-null
if ($j.State -eq "Completed") { "done!" }
elseif ($j.State -eq "Running") { "interrupted" }
else { "???" }
Remove-Job -force $j #cleanup

2014.01.18 更新

这里有一个更加简化的方法,还包括了实际步骤——使用Receive-Job从脚本块中获取信息,假设你想要的内容已经生成在标准输出中:

$timeoutSeconds = 3
$code = {
    # your commands here, e.g.
    Get-ChildItem *.cs | select name
}
$j = Start-Job -ScriptBlock $code
if (Wait-Job $j -Timeout $timeoutSeconds) { Receive-Job $j }
Remove-Job -force $j

所以在第三行,我只需放置我原帖中的命令吗?(帖子中的第一件事) - Aaron
1
@Aaron:没错,但有一点需要注意。我已经更新了我的答案,使其对您更加有用。在更新中,将$code块填入您的命令,除了将输出分配给$fullnamexp的实际赋值之外。在看到Receive-Job的地方,进行赋值,即$fullnamexp = Receive-Job $j - Michael Sorens
嗨,我在原帖中添加了一个更新,你能告诉我这样做是否正确吗?我已经按照那样做了,但它没有输出任何内容,但是它确实等待我指定的秒数。 - Aaron
1
关闭;您需要进行两个更正:(1)分配应该在“Receive-Job”上而不是“Remove-Job”上。 (2)文本“Get-ChildItem *.cs | select name”仅仅是我的示例--从您的代码中删除它。 - Michael Sorens
嘿,我已经更新了我的帖子,并修复了代码。你能验证它是否正确吗?我已经使用那段代码进行了测试,但是我只是想确认它的准确性,因为我没有看到任何输出。 - Aaron
显示剩余2条评论

5
您可以使用Start-Sleep来暂停脚本:
Start-Sleep -s 5

虽然这个答案在陈述上是正确的,但它并没有解答问题。它只是一个休眠,而不是一个超时。 - undefined

1
这个解决方案对我不起作用。在这个例子中,remove-job -force $j 花费超过5秒的时间。
$timeoutseconds = 1

$start = get-date
$j = start-job -scriptblock { Resolve-DnsName  1.1.1.1 }
if (wait-job $j -timeout $timeoutseconds) { $fullnamexp = receive-job $j } 
remove-job -force $j 
(get-date) - $start


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 5
Milliseconds      : 342
Ticks             : 53426422
TotalDays         : 6.18361365740741E-05
TotalHours        : 0.00148406727777778
TotalMinutes      : 0.0890440366666667
TotalSeconds      : 5.3426422
TotalMilliseconds : 5342.6422

这是一个简单的超时示例,使用记事本:
notepad
if (-not $(wait-process notepad 10; $?)) { stop-process -name notepad }

1

网络编程在操作上没有明确的超时设置,但你可以查看这个链接关于如何更改套接字的IPv4超时时间:

http://www.cyberciti.biz/tips/linux-increasing-or-decreasing-tcp-sockets-timeouts.html

我能想到的唯一其他方法是生成一个工作线程,但我甚至不知道在bash中是否可能,我的熟练程度还不足以回答这个问题;此外,这会让您面临同步问题和各种多线程问题,超出了您试图快速完成的bash脚本的范围!:P


1
这有帮助吗?
$query = (dsquery user -samid $winxp) 
if ($query) {$fullnamexp = ($query | dsget user -display)[1].trim()}
$fullnamexp

我需要用这个替换我现有的内容吗? - Aaron
1
是的。我相信它会产生相同的结果(返回用户的全名),但如果找不到用户,它不会挂起。 - mjolinor
$winxp变量例如是“ajstepanik”,我的OP中的那行代码将把它转换成我的名字(Stepanik,Aaron J.等等),并在找到时执行该操作。但似乎在您的代码中无法实现这一点:( 我会继续尝试的。 - Aaron
是的,我无法让它工作。除了一个空格之外,没有任何输出。输入只是用户名,应该可以工作... - Aaron
我不知道问题出在哪里。我可以将$winxp设置为samaccountname,在我的系统上运行良好。 - mjolinor
什么是dsquery和dsget? - AnneTheAgile

0
    $watchdog = 10 #seconds
    $start_time = Get-Date
    $j = Start-Job -ScriptBlock{
        #timeout command
        if ($true) {
            $i = 0
            while($true) {
                Write-Host "Count: $i"
                Start-Sleep -Milliseconds 100
                $i++
            }      
        }
        write-host "Hello"
    }
    while($true) {
        if ($j.HasMoreData) {
            Receive-Job $j
            Start-Sleep -Milliseconds 200
        }
        $current = Get-Date
        $time_span = $current - $start_time
        if ($time_span.TotalSeconds -gt $watchdog) {
            write-host "TIMEOUT!"
            Stop-Job $j
            break
        }
        if (-not $j.HasMoreData -and $j.State -ne 'Running') {
            write-host "Finished"
            break
        }
    }
    Remove-Job $j

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接