【问题标题】:Improving Batch File for loop with start subcommand使用 start 子命令改进循环的批处理文件
【发布时间】:2016-12-03 22:34:10
【问题描述】:

我目前有一个非常简单的脚本,可以 ping 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
来自 192.168.0.101 的回复:字节=32 时间=294ms TTL=64
来自 192.168.0.104 的回复:bytes=32 time=1ms TTL=64

但是,它非常缓慢,而且肯定是按顺序发生的。当我使用 PowerShell Measure-Command 运行它时,我得到了以下结果:

PS C:\Users\derpherp> 测量命令 {start-process test.bat -Wait}

天数:0
小时 : 0
分钟:0
秒数:23
毫秒:107
滴答声:231074173
总天数:0.000267446959490741
总小时数:0.00641872702777778
总分钟数:0.385123621666667
总秒数:23.1074173
总毫秒数: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

结果在很多方面都不同:

  • 结果并不总是连续的,这意味着它实际上更异步地启动了命令:

    root@kali:~/vids/bash_scripting# ./test.sh
    192.168.0.104
    192.168.0.100
    192.168.0.103
    192.168.0.101
  • 它显示所有阳性结果的时间通常不到一秒,这可以扩展到更大的数字集。

那么,我的问题是,这是批处理脚本的限制吗?我很难相信 bash 在如此简单的任务中的性能比 Windows CMD 解释器好 100 倍?

如果这是有限的,PowerShell 是否提供了一种有竞争力的方式来创建任务?我在 foreach 循环中使用了Start-Job,但对于大量任务(即 /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 的唯一窗口,并且需要同样长的时间才能完成。

【问题讨论】:

标签: windows powershell batch-file cmd parallel-processing


【解决方案1】:

异步并不容易,但使用 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;

我在 3 秒内完成了对大约 250 台主机的上述操作。

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

【讨论】:

  • 谢谢先生!我一直在使用-AsJob 一段时间,但最终遇到了输出问题-> 只是在学习并最终做了Get-Job | Receive-Job | out-file -append filename.txt 之类的事情,或者放弃了-AsJob 并使用Start-Job -ScriptBlock 和各种选项。非常感谢您的帮助!
  • 我现在制作了我的新 PS 状态检查工具!获取所有 AD 计算机,过滤操作系统类型,收集某些主机上的某些数据,使用此处的测试连接部分仅尝试从在线内容中收集 :) 100 台计算机在
【解决方案2】:

似乎我找到了一个纯粹的 解决方案。基本上,该脚本会同时触发多个ping 命令,这些命令都将其结果写入单独的日志文件;监视这些文件的写访问,因为只要各自的ping 进程正在进行,这些文件就会被写锁定;一旦授予写访问权限,包含 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

【讨论】:

    【解决方案3】:

    你不需要start 命令。你关闭它的所有功能。然后你告诉它为CMD 启动一个新进程,这是缓慢且不必要的。所以-ping -n 1 192.168.0.%%i |find "time="

    在此处查看我关于如何启动程序的摘要 - What do all commands in batch mean and do?

    您可以使用 start 并行运行 ping,但您需要删除 /w 这意味着等待(但它被忽略了,因为)和 /b 表示不要在新生成的进程中运行命令但要按顺序进行。

    由于您仍在创建Ping 的多个进程,您可以使用 WMIC 来避免这种情况,并在一个进程创建中测试所有计算机。

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

    所以我们已经从 20 个进程创建到 1 个。

    在编程中,我们必须支付系统开销税。 Windows 在 Process Creation 和 Window Creation 中收取大部分税款。这是为了允许在没有太多系统开销的情况下进行其他操作。例如,Windows 保留正在运行的进程表 - 在创建进程时使用资源进行更新,但允许快速查找其他操作。

    【讨论】:

    • 刚刚在第 3 行进行了以下更改 START ping -n 1 192.168.0.%%i |find "time=" 我现在没有得到任何输出(一切都在窗口中打开然后消失),并且 measure-command 中的执行时间仍然是 23+ 秒。
    • @Abraxas remove start - 这是不必要的,会造成部分麻烦。您是否尝试过 Noodles 在这里给您的建议?
    • CMD 做管道。因此,如果没有 CMD,您将无法使用管道。删除 start 意味着当前 cmd.exe 将处理管道。
    • @alroc - 它现在将所有内容输出到一个窗口,但仍需要约 23 秒以上
    • @Noodles 我只指第 3 行 - 一切都在 do 语句中。使用其他尝试的扫描和结果编辑主要帖子
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 2020-09-29
    • 2018-11-12
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    • 2015-03-10
    相关资源
    最近更新 更多