2016-12-03 60 views
1

我現在有一個非常簡單的腳本,坪10個IP地址:與啓動子循環改善批處理文件

@ECHO OFF 
for /L %%i in (100, 1, 110) DO (
    START /W /B cmd /c ping -n 1 192.168.0.%%i | find "time=" 
) 

如預期的輸出:

C:\Users\herpderp>test.bat 
Reply from 192.168.0.101: bytes=32 time=294ms TTL=64 
Reply from 192.168.0.104: bytes=32 time=1ms TTL=64

然而,這是非常緩慢而且絕對會發生。當我運行這個用PowerShell的Measure-Command我得到這些結果:

PS C:\Users\derpherp> measure-command {start-process test.bat -Wait} 

Days    : 0 
Hours    : 0 
Minutes   : 0 
Seconds   : 23 
Milliseconds  : 107 
Ticks    : 231074173 
TotalDays   : 0.000267446959490741 
TotalHours  : 0.00641872702777778 
TotalMinutes  : 0.385123621666667 
TotalSeconds  : 23.1074173 
TotalMilliseconds : 23107.4173

所以我們看到它正在〜23日秒鐘的執行。現在

,如果我是一個Linux系統上,想要做同樣的事情,我可以做到以下幾點:

#!/bin/bash 

for ip in $(seq 100 110); do 
    ping -c 1 192.168.0.$ip | grep "bytes from" | cut -d" " -f4 | cut -d ":" -f1 & 
done 

結果是多種方式不同:

  • 結果並不總是順序的,這意味着它實際上更異步地啓動命令:

    [email protected]:~/vids/bash_scripting# ./test.sh 
    192.168.0.104 
    192.168.0.100 
    192.168.0.103 
    192.168.0.101
  • 它顯示所有積極結果的時間通常小於一秒,這可以擴展到更大的數字集。

所以,我的問題是,這是批量腳本的限制嗎?我發現很難相信bash比Windows CMD解釋器在執行這樣一個簡單任務時的字面好幾百倍?

如果這是有限的,PowerShell是否提供了一種有競爭力的方式來創建任務?我用Start-Job在foreach循環,但似乎變得不可行了大量的任務(即A/16網絡上Test-Connection

編輯1

@ECHO OFF 
for /L %%i in (100, 1, 110) DO (
    ping -n 1 192.168.0.%%i | find "time=" 
) 

它輸出當前窗口只要初始變體

@ECHO OFF 
for /L %%i in (100, 1, 110) DO (
    START ping -n 1 192.168.0.%%i | find "time=" 
) 

這將輸出到每個IP的唯一窗口,並且只需要很長時間就可以完成。

+0

[?.NET DNS類PowerShell後臺作業可能(的可能的複製http://stackoverflow.com/questions/36205922/net-dns -class-powershell-background-job-possible)和[並行作業和隊列系統](http://stackoverflow.com/questions/244712​​09/how-to-implement-a-parallel-jobs-and-queues-system)和[並行運行命令](http://stackoverflow.com/questions/4016451/can-powershell-run-commands-in-parallel)和[多線程PowerShell ping腳本](http://stackoverflow.com/questions/ 37954004/how-to-multithread-powershell-ping-script) – TessellatingHeckler

+0

[ping IP地址範圍](http://stackoverflow.com/questions/34378793/ping-range-of-ip-adresses) - [ping併發] (http://stackoverflow.com/ques tions/24930714/pinging-an-array-of-ip-addresses-concurrently-in-powershell) - [同時運行多個腳本塊](http://stackoverflow.com/questions/9601118/running-multiple-scriptblocks - 在同一時間與開始工作,而不是循環)相關的http://ramblingcookiemonster.github.io/Invoke-Ping/和[快速ping多臺機器](http:// codereview.stackexchange。com/questions/97726/powershell-to-quickly-ping-a-number-of-machines) – TessellatingHeckler

+1

bash在你的場景中勝過CMD的原因是因爲你的bash腳本在後臺運行進程,所以他們沒有運行順序但平行。 CMD並不真的支持這一點。要在CMD中做類似的事情,你必須在新的CMD實例中啓動每個「ping」,這不允許你在一個窗口中獲得輸出。 –

回答

4

異步並不容易,但使用PowerShell可以輕鬆完成任務。

此代碼的行爲應該怎麼看起來像你的bash代碼執行,返回,沒有錯誤響應的所有主機的IP地址:

$IPAddresses = 100..110 | ForEach-Object { "192.168.0.$_"; } 

$JobList = $IPAddresses | ForEach-Object { 
    Test-Connection -ComputerName $_ -Count 1 -AsJob; 
} 

Receive-Job -Job $JobList -AutoRemoveJob -Wait | ` 
    Where-Object { $_.StatusCode -eq 0 } | ` 
    Select-Object -ExpandProperty Address; 

以上,結束了我對〜250臺主機在3秒。

Test-Connection本身聲稱使用了多達32個併發連接(可配置與-ThrottleLimit設置),但它肯定好像-Delay選項完全覆蓋該設置,如果你的目標範圍廣。

您可能在PowerShell v4及更早版本中遇到問題,因爲它的行爲可能略有不同。 Test-Connection在不同版本的Windows或PowerShell中似乎特別特別。至少,我總是記得它拋出錯誤,而不是在以前的版本中返回錯誤狀態碼。

如果你想通過ping比Test-Connection提供更好的紋理控制 - 例如,它默認爲1秒超時,所以當任何主機關閉時等待的最短時間爲1秒 - 您可能必須恢復爲直接撥打Get-WMIObject -Query "SELECT * FROM Win32_PingStatus WHERE Address = '$IP' AND TimeOut = 200" -AsJob

+0

謝謝先生!我一直在使用'-AsJob'一段時間,但最終遇到了輸出問題 - >只是學習,最後做了一些事情,比如'Get-Job |接收作業| out-file -append filename.txt「或放棄-AsJob並使用」Start-Job-ScriptBlock「和各種選項。 非常感謝您的幫助人! – Abraxas

+0

我現在已經制作了我的新PS狀態檢查工具!獲取所有AD計算機,在操作系統類型上過濾,在某些主機上收集某些數據,在這裏使用測試連接部分,只嘗試收集來自在線的東西:) 100秒的計算機在<5秒^ _ ^測試。我喜歡powershell的作品。 – Abraxas

1

您不需要命令start。你關掉所有的功能。然後你告訴它開始一個CMD的新過程,這是一個緩慢而不必要的過程。所以 - ping -n 1 192.168.0.%%i |find "time="

請參閱我的關於如何啓動程序的總結 - What do all commands in batch mean and do?

您可以使用開始並行運行ping,但你需要刪除/w這意味着等待(但它正在因爲忽略)和/b它說不要運行在一個新的衍生進程的命令,但做到這一點順序。

由於您還在做多個流程創建Ping您可以使用WMIC來避免這一點,並在一個進程中測試所有計算機的創建。

wmic /node:@"c:\computerlist.txt" /append:"textfile.txt" path win32_pingstatus where "address='127.0.0.1' and responsetime > 100" get responsetime,timestamprecord

因此,我們已經走了20個進程創建爲1

在編程中,我們要付出的系統開銷稅。 Windows在創建流程和創建窗口時收取大部分稅款。這是爲了允許其他操作在沒有太多系統開銷的情況下進行。 Windows保留正在運行的進程表的一個示例 - 在創建進程時使用資源進行更新,但允許快速查找其他操作。

+0

剛剛在第3行啓動了以下更改'START ping -n 1 192.168.0。%% i | find「time =」'我現在沒有得到任何輸出(所有內容都會在窗口中打開然後消失)和執行時間在測量命令仍然23 +秒。 – Abraxas

+0

@Abraxas刪除'開始' - 這是沒有必要的,並導致你的一部分麻煩。你嘗試過麪條給你的建議嗎? – alroc

+0

CMD做管道。所以你不能沒有CMD的管道。刪除開始意味着當前cmd.exe將處理管道。 – 2016-12-03 23:46:34

1

似乎我找到了一個純粹的解決方案。基本上,腳本同時觸發幾個命令,這些命令都將其結果寫入單獨的日誌文件;監視這些文件的寫入訪問權限,因爲只要相應的進程正在進行,文件就會被寫入鎖定;只要授予寫入訪問權限,包含IP地址的日誌文件的第一行就會被複制到摘要日誌文件中。因此,這裏的代碼 - 看到所有的解釋性rem備註:

@echo off 
setlocal EnableExtensions DisableDelayedExpansion 

rem // Predefine IP addresses as an array: 
for /L %%J in (0,1,5) do (
    set "IP[%%J]=127.0.0.%%J" 
) 

rem // Ping IP addresses simultaneously, create individual log files: 
for /F "tokens=2,* delims=[=]" %%J in ('set IP[') do (
    start "" /B cmd /C ping -n 2 %%K ^| find "TTL=" ^> "%~dpn0_%%J.log" 
) 

rem // Deplete summary log file: 
> "%~dpn0.log" rem/ 

rem /* Polling loop to check whether individual log files are accessible; 
rem this works, because a pinging process is not yet finished, the 
rem respective log file is still opened and therefore write-locked: */ 
:POLL 
rem // Give processor some time: 
> nul timeout /T 1 /NOBREAK 
rem /* Loop through all available array elements; for every accomplished 
rem pinging process, the related array element becomes deleted, so 
rem finally, there should not be any more array elements defined: */ 
for /F "tokens=2 delims=[=]" %%J in ('2^> nul set IP[') do (
    rem // Suppress error message in case log file is write-locked: 
    2> nul (
     rem // Try to append nothing to the log file: 
     >> "%~dpn0_%%J.log" rem/ && (
      rem /* Appending succeeded, hence log file is no longer locked 
      rem and the respective pinging process has been finished; 
      rem therefore read the first line containing the IP: */ 
      set "FILE=" & set "IP=" 
      < "%~dpn0_%%J.log" set /P IP="" 
      rem // Copy the read line to the summary log file: 
      if defined IP >> "%~dpn0.log" call echo(%%IP%% 
      rem // Undefine related array element: 
      set "IP[%%J]=" 
      rem // Store log file path for later deletion: 
      set "FILE=%~dpn0_%%J.log" 
     ) 
     rem // Delete individual log file finally: 
     if defined FILE call del "%%FILE%%" 
    ) 
) 
rem // Jump to polling loop in case there are still array elements: 
> nul 2>&1 set IP[ && goto :POLL 

endlocal 
exit /B