使用 start 子命令改进循环的批处理文件 [英] Improving Batch File for loop with start subcommand

查看:27
本文介绍了使用 start 子命令改进循环的批处理文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前有一个非常简单的脚本,可以 ping 10 个 IP:

@ECHO 关闭对于/L %%i 在 (100, 1, 110) DO (开始/W/B cmd/c ping -n 1 192.168.0.%%i |找到时间=")

输出符合预期:

C:Usersherpderp>test.bat192.168.0.101回复:bytes=32 time=294ms TTL=64192.168.0.104回复:bytes=32 time=1ms TTL=64

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

PS C:Usersderpherp> measure-command {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对于 $(seq 100 110) 中的 ip;做ping -c 1 192.168.0.$ip |grep "来自的字节" |剪切 -d" " -f4 |cut -d ":" -f1 &完毕

结果以多种方式不同:

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

    root@kali:~/vids/bash_scripting# ./test.sh192.168.0.104192.168.0.100192.168.0.103192.168.0.101

  • 它显示所有阳性结果的时间通常不到一秒,并且可以扩展到更大的数字集.

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

如果这是有限的,PowerShell 是否提供了一种有竞争力的方式来创建任务?我在 foreach 循环中使用了 Start-Job 但这似乎对大量任务(即/16 网络上的 Test-Connection)不可行.

编辑 1

@ECHO 关闭对于/L %%i 在 (100, 1, 110) DO (ping -n 1 192.168.0.%%i |找到时间=")

这会输出当前窗口并花费与初始变体一样长的时间

@ECHO 关闭对于/L %%i 在 (100, 1, 110) DO (开始 ping -n 1 192.168.0.%%i |找到时间=")

这会输出到每个 IP 的唯一窗口,并且需要同样长的时间才能完成.

解决方案

异步并非易事,但借助 PowerShell,您可以轻松完成任务.

这段代码应该像你的 bash 代码那样运行,返回所有响应没有错误的主机的 IP 地址:

$IPAddresses = 100..110 |ForEach-Object { "192.168.0.$_";}$JobList = $IPAddresses |ForEach-Object {测试连接 -ComputerName $_ -Count 1 -AsJob;}Receive-Job -Job $JobList -AutoRemoveJob -Wait |`Where-Object { $_.StatusCode -eq 0 } |`Select-Object -ExpandProperty 地址;

以上对我来说在 3 秒内完成了大约 250 台主机.

Test-Connection 本身声称最多使用 32 个同时连接(可通过 -ThrottleLimit 设置进行配置)但它确实看起来像 -Delay 如果您的目标范围很广,则该选项会完全覆盖该设置.

您可能会遇到 PowerShell v4 及更早版本的问题,因为它的行为可能略有不同.Test-Connection 特别是在不同版本的 Windows 或 PowerShell 上似乎是特殊的.至少,我一直记得它抛出一个错误,而不是在以前的版本中返回错误状态代码.

如果您想对 ping 进行比 Test-Connection 提供的更细粒度的控制——例如,它默认为 1 秒超时,因此当任何主机处于down 是 1 秒——您可能必须恢复到直接调用 Get-WMIObject -Query "SELECT * FROM Win32_PingStatus WHERE Address = '$IP' AND TimeOut = 200" -AsJob.

I've currently got a very simple script which pings 10 IPs:

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

The output is as expected:

C:Usersherpderp>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

However, it is VERY slow and definitely happening sequentially. When I run this with a PowerShell Measure-Command I get these results:

PS C:Usersderpherp> 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

So we see it is taking ~23 seconds to execute.

Now, if I were on a Linux system and wanted to do this same thing, I can do the following:

#!/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

The result is different in a variety of ways:

  • The results aren't always sequential, meaning it actually started the commands more asynchronously:

    root@kali:~/vids/bash_scripting# ./test.sh
    192.168.0.104
    192.168.0.100
    192.168.0.103
    192.168.0.101

  • The time for it to display all of the positive results is typically less than a second and this scales to much larger sets of numbers.

So, my question, is this a limitation of batch scripting? I find it hard to believe that bash performs literally 100s of times better than Windows CMD interpreter for such a simple task?

If this is limited, does PowerShell offer a competitive way to create tasks? I've used Start-Job in a foreach loop but that seems to become unworkable for large numbers of tasks (ie Test-Connection on a /16 network)

Edit 1

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

This outputs the current window and takes just as long as the initial variant

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

This outputs to unique windows per IP and takes just as long to complete.

解决方案

Asynchronous isn't easy, but with jobs you can get close pretty easily with PowerShell.

This code should behave how it looks like your bash code does, returning the IP address of all hosts that respond without error:

$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;

The above completes for me against ~250 hosts in 3 seconds.

Test-Connection itself claims to use up to 32 simultaneous connections (configurable with -ThrottleLimit setting) but it sure seems like the -Delay option completely overrides that setting if you're targeting a wide range.

You may have issues with PowerShell v4 and earlier, as it may behave slightly differently. Test-Connection in particular seems to be idiosyncratic on different versions of Windows or PowerShell. At least, I always remember it throwing an error instead of returning an error status code in previous versions.

If you want more fine grain control over the ping than Test-Connection gives you -- for example, it defaults to a 1 second timeout so the minimum time you'll wait when any host is down is 1 second -- you'll probably have to revert to directly calling Get-WMIObject -Query "SELECT * FROM Win32_PingStatus WHERE Address = '$IP' AND TimeOut = 200" -AsJob.

这篇关于使用 start 子命令改进循环的批处理文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆