PowerShell在变量和文件上运行Select-String的区别

4
我正在尝试使用PowerShell脚本从ipconfig的输出中提取我的本地网络IP地址。我已经使用临时输出文件使其工作,但是希望消除该步骤并仅在脚本内部使用变量。我无法弄清楚为什么不使用临时输出文件的脚本无法工作。非常感谢您的帮助。
以下是两个脚本。首先是有效版本:
$input_path = 'C:\Users\Will\Code\Scripts\tmp.txt'
$regex = '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'

ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56." > tmp.txt

$ipAddress = select-string -Path $input_path -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

# Pop up an alert window with the IP address
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup($ipAddress,0,"Done",0x1)

# Copy the IP address to the clipboard
$ipAddress | CLIP

现在是使用变量的几乎相同但不起作用的版本:

$regex = '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'

$input = ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56."

#$input > inputVar.txt

$ipAddress = select-string -inputObject $input -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

# Pop up an alert window with the IP address
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup($ipAddress,0,"Done",0x1)

# Copy the IP address to the clipboard
$ipAddress | CLIP

我正在使用 select-string 查找包含“IPv4”的行,并将其输入到另一个调用 select-string 的命令中,以消除包含虚拟机 IP 地址的行。如果我在不起作用的脚本的第 6 行启用测试输出,并将其与工作脚本的“tmp.txt”文件进行比较,我可以看到截至此时,两个脚本的工作方式完全相同。
此外,两个脚本中的第 10-15 行是相同的。因此,我认为唯一需要考虑的是第 8 行。
我已经尝试了所有我能想到的方法,但始终获得相同的结果(“IPv4”而不是 IP 地址)。第一个脚本按预期工作并给出了 IP。
这两个选项似乎都应该起作用(并且可能是我尝试过的所有选项中最合逻辑的),但是没有一个给我 IP:
$ipAddress = select-string -inputObject $input -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

并且。
$ipAddress = $input | select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

我甚至尝试了消除所有额外步骤,只制作一个长管道,就像这样:
$ipAddress = ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56." | select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

并且可以像这样简单地复制到剪贴板:

ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56." | select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value } | CLIP

但是似乎什么都不起作用。

我在哪里做错了呢?

谢谢!


更新 03/13/2015 6:30pm

我正在运行 Windows 7 64位。

这是 $PSVersionTable 的输出:

Name Value
---- -----
CLRVersion 2.0.50727.5485
BuildVersion 6.1.7601.17514
PSVersion 2.0
WSManStackVersion 2.0
PSCompatibleVersions {1.0, 2.0}
SerializationVersion 1.1.0.1
PSRemotingProtocolVersion 2.1

是的,我的最终目标只是获取 IP。实际上,我只是想找到一个快速的方法来做到这一点。我想我在使用它时技术上并不需要 IP,但是一旦我开始研究它,我就对试图弄清楚其余部分产生了浓厚的兴趣。

感谢所有的帮助!

我在 ~10 年的休息后又回到编码中,我非常生疏。

如果我的不工作版本对我而言不起作用,但对您而言却起作用,我不确定为什么。当我运行它时,我在弹出窗口和剪贴板中得到的是 "IPv4",而不是 IP(我在使用临时文件的工作版本中得到了 IP)。

我将把这些正则表达式更改集成到我的当前脚本中,并尝试使用您的 gwmi 解决方案,Matt。初始运行对我没有返回任何内容,但我会玩弄它并让您知道。

如果您对为什么变量版本在我的情况下无法正确工作有更多想法,请告诉我。这对我来说完全令人困惑。

再次感谢!


更新 03/13/2015 10:27pm

这是更新后的 $PSVersionTable 的输出:

Name Value
---- -----
PSVersion 4.0
WSManStackVersion 3.0
SerializationVersion 1.1.0.1
CLRVersion 4.0.30319.18444
BuildVersion 6.3.9600.16406
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion 2.2


如果我在 PowerShell 中逐个运行管道命令,会发生以下情况。正如您所看到的,它似乎在第 14 行崩溃了: $ipAddress = $ipAddress | % { $_.Matches }

无论如何,希望这有所帮助:

PS C:\Users\Will\Code> $regex = '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
PS C:\Users\Will\Code> $inputVar = ipconfig | select-string "IPv4" | select-string -notmatch "192\.168\.56\."
PS C:\Users\Will\Code> $inputVar

   IPv4 Address. . . . . . . . . . . : 192.168.0.105


PS C:\Users\Will\Code> $ipAddress = select-string -inputObject $inputVar -Pattern $regex -AllMatches
PS C:\Users\Will\Code> $ipAddress

   IPv4 Address. . . . . . . . . . . : 192.168.0.105


PS C:\Users\Will\Code> $ipAddress = $ipAddress | % { $_.Matches }
PS C:\Users\Will\Code> $ipAddress


Groups   : {IPv4}
Success  : True
Captures : {IPv4}
Index    : 3
Length   : 4
Value    : IPv4



PS C:\Users\Will\Code> $ipAddress = $ipAddress | % { $_.Value }
PS C:\Users\Will\Code> $ipAddress
IPv4
PS C:\Users\Will\Code>

注意:您在正则表达式匹配字符串中经常使用 192.168.56.。除了其他可能存在的问题之外,您应该使用 192\.168\.56\.,因为句点是正则表达式中的控制字符,它匹配除换行符以外的任何字符*。虽然您现在使用的仍然可以匹配句点,但重要的是要知道您在做什么。 - Matt
你的脚本在我这里运行得非常好。具体来说,它在你那里有什么问题? - Matt
Daniel 我已经更新了原始帖子,包含你所要求的信息。Matt,我在我的代码中修复了那个正则表达式。谢谢!回答你的问题,这里出现问题的是,不知何故非工作版本将“IPv4”复制到我的剪贴板并在弹出窗口中显示它,而不是 IP 地址。 - will
谢谢,Booga Roo。我甚至没有意识到我没有更新。我已经更新了所有内容,并将在我的原始帖子末尾发布新的$PSVersionTable。但不幸的是,我仍然遇到同样的问题。 - will
在“我甚至尝试消除所有额外的步骤……”下面的那一行,你的问题是如何链接Select-String。我会更新我的答案。 - Matt
显示剩余2条评论
2个回答

3

发现这行代码存在问题:

$input = ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56."

您不能按照您在此处尝试使用 $input 的方式使用它。它是 Powershell 中的默认变量之一。请将 $input 实例更改为 $iplist 或其他变量名称。
根据您最新的编辑更新答案:
奇怪的是看到它选择 IPv4。我这里没有得到相同的结果。尝试这个,它类似于 Matt 的方法,但跳过了几个 Foreach-Object 调用。
$ipAddress = (select-string -inputObject $inputVar -Pattern $regex -AllMatches).tostring().split(" ")[-1]

需要注意的是,如果返回了多个网络适配器,则此方法可能不是最佳选择。在我的情况下,我有三个适配器,这种方法只选择了最后一个。


刚试了一下,但不幸的是它没有解决我的问题。虽然这很好知道,我以后会避免使用 $input 作为变量!谢谢! - will
有趣。看起来其他人报告说不工作的代码对他们有效。肯定还有其他因素在其中,比如不同的PowerShell版本或环境设置。 - Booga Roo
我完全同意。只是不确定这是什么!不过,我很高兴你指出了我使用的过时版本的 PS。更新肯定是好事,无论它是否解决了这个特定的问题。 - will

2

链接 Select-String

我认为这就是你遇到的问题。此外,你把 Select-String 当作 Where-Object 来使用。请考虑以下内容

ipconfig |  select-string "IPv4" | % { $_.Matches } | % { $_.Value }

这将至少产生一个IPv4字符串,因为这正是您所要求的内容。请注意,`select-string` 不会返回字符串,而是 `Microsoft.PowerShell.Commands.MatchInfo` 对象。我可以想象您脑海中正在发生什么:“但我的输出应该是字符串吗?” 这就是为什么您会看到大多数使用 `Select-String` 使用 `% { $_.Matches } | % { $_.Value }`提取匹配的字符串。PowerShell 显示在控制台上和实际底层对象之间存在差异。不确定是否需要更多解释。此编辑将使您的代码正常工作:
ipconfig |  select-string "IPv4" | out-string | 
    select-string -notmatch "192.168.56." | out-string |
    select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

然而,这似乎有点奇怪,因为前面的Select-String就像我已经提到的那样,起到了Where子句的作用。你也可以这样做:

ipconfig | Where-Object {$_ -match "IPv4" -and $_ -notmatch "192\.168\.56\." } |
    select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

其他方法

我认为您的不工作版本没有问题,但是我想给出一些改进意见,建议您使用 select-string 的一个非常小的修改就可以实现使用一个以上的效果。

$ipAddressPrefix = [regex]::Escape("169.254.125.")
$ipaddresses = ipconfig | select-string "IPv4.*?: (?!$ipAddressPrefix).*" | % { $_.Matches } | % {$_.Value.Split(": ")[-1]}

# Pop up an alert window with the IP address
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup($ipaddresses,0,"Done",0x1)

让我们取前三个八位字节,创建一个转义匹配字符串。然后使用负向先行断言匹配包含“IPv4”但不在冒号后面跟随$ipAddressPrefix的行。
然后,使用返回的所有匹配项在冒号和空格上拆分字符串以获取匹配的IP地址。再次注意,以这种方式返回多个地址是非常可能的。
首选方法
解析字符串以获取此信息很好,但您可以使用WMI更轻松地获取它,而不必担心前导和尾随空格。
$ipAddressPrefix = [regex]::Escape("169.254.125.")
$ipaddresses = gwmi Win32_NetworkAdapterConfiguration | Where { $_.IPAddress -and ($_.IPAddress -notmatch "$ipAddressPrefix|:") } |
    Select -Expand IPAddress

$ipaddresses应该包含使用任何代码片段的相同结果。 Where 子句分为以下两个部分:

  1. $_.IPAddress --> 这将过滤掉空字符串和 null 值。
  2. $_.IPAddress -notmatch "$ipAddressPrefix|:" 将确保省略 $ipAddressPrefix 匹配项。您还将看到 :,这将省略 IPv6 地址。

感谢你的所有帮助,Matt!我会尝试调试一下,看看能否在我的电脑上运行。第一次尝试并未产生任何结果,但我认为现在是用户错误。我非常感激您的帮助。为了方便大家,我已更新我的原始帖子,添加了Daniel Lindegaard要求的信息。 - will
这个完全可行,也很可能是我目前的解决方案。但我仍然想弄清楚那段其他代码发生了什么。 - will

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