【问题标题】:RegEx to match string between two strings in PowershellRegEx在Powershell中匹配两个字符串之间的字符串
【发布时间】:2018-11-20 05:58:56
【问题描述】:

这是我的示例数据:

选项 failonnomatch on
选项批处理开启
选项确认关闭
打开 sftp://username:password@host.name.net:22 hostkey="ssh-rsa 1024 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"

获取文件*.txt\local\path\Client\File.txt
mv 文件*.txt /remote/archive/

关闭
退出

我想创建一个 powershell 脚本来从这个文本文件中提取信息片段。

我需要的物品清单:

  • 用户名
  • 密码
  • 主机
  • 端口
  • ssh 密钥
  • 文件名
  • 本地路径
  • 远程路径

我希望如果我学会了如何做其中的几个,该方法将适用于所有项目。我尝试使用以下 powershell/regex 提取 ssh 密钥:

$doc -match '(?<=hostkey=")(.*)(?=")' 

$doc 是样本数据

但它似乎正在返回整行。任何帮助将不胜感激。谢谢你。

【问题讨论】:

  • 如果它们的所有键/值都这样,只需使用(?&lt;=\bkey=")([^"]*)(?=") 或者,您可以使用(?&lt;=\b\w+=")([^"]*)(?=") 进行全局匹配
  • 你的命令只会返回 $true/$false。要返回一个值,您需要评估 $Matches 集合。另外你指的是什么文件? edit您的问题包含一些示例数据。
  • 最后一行的哪一部分是“文件”,哪一部分是“路径”? File*.txt 看起来像一个文件规范。下一部分似乎是完整的文件名。我想你想把它分成 \SERVER\Path\ClientFile.txt 但我不确定。

标签: regex powershell


【解决方案1】:

这使用命名匹配并将标志设置为singleline, multiline, case insensitive,然后使用$Matches.MatchName 将项目放入自定义对象中。

# fake reading in a text file as one string
#    in real life, use Get-Content -Raw
$InStuff = @'
open sftp://username:password@host.name.net:22 hostkey="ssh-rsa 1024 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"

get File*.txt \SERVER\Path\Client\File.txt
'@

$Null = $InStuff -match '(?smi).+//(?<UserName>.+):(?<Password>.+)@(?<HostName>.+):(?<Port>.+) hostkey="(?<SshKey>.+)".+get .+ (?<FullFileName>\\.+)$'

[PSCustomObject]@{
    UserName = $Matches.UserName
    Password = $Matches.Password
    Port = $Matches.Port
    SshKey = $Matches.SshKey
    PathName = Split-Path -Path $Matches.FullFileName -Parent
    FileName = Split-Path -Path $Matches.FullFileName -Leaf
    }

输出...

UserName : username
Password : password
Port     : 22
SshKey   : ssh-rsa 1024 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
PathName : \SERVER\Path\Client
FileName : File.txt

【讨论】:

  • 这是一个有效的解决方案 (+1),但是如果您提供一个完整的解决方案,该解决方案特定于 OP 的确切场景 而不解决问题所暗示的误解(关于如何-match 工作),你会让 OP 非常高兴,但未来有类似误解的读者 - 但不同的场景 - 不一定会受益。
  • @mklement0 - 我明白你的意思......我提到“作为一个字符串”涵盖了这个想法。你的在这个问题上要详细得多。我会尽量记住这一点。 [咧嘴一笑]
  • 嗨,李,我没有提到我在给定示例之前和之后还有其他行。我怎样才能适应这些线路?谢谢。
  • @MichaelSPalatsi - 您需要在原始帖子中添加 complete 文本,以便人们可以有一个真实的示例来编写代码。如果文本太长,请将其发布到 Pastebin 或 Gist.GitHub 并将其链接添加到您的 OP 中。
  • @Lee_Dailey 这是有道理的。对不起,我是新人。 :) 我已经更新了 OP。
【解决方案2】:

如果-match 返回一个整行,这意味着-match 操作的LHS 是一个数组 em>, 这反过来表明您使用Get-Content 没有-Raw,这会将输入作为数组 em>,在这种情况下,-match 充当 过滤器

相反,将您的文件读取为带有Get-Content -Raw 的单个多行字符串; 带有标量 LHS,
-match然后返回[bool]
,并且匹配操作的结果报告在自动变量$Matches(一个哈希表,其0 条目包含整体匹配,1 第一个捕获组匹配的内容,...):

# Read file as a whole, into a single, multi-line string.
$doc = Get-Content -Raw file.txt 

if ($doc -match '(?<=hostkey=")(.*)(?=")') {
   # Output what the 1st capture group captured
   $Matches[1]
}

使用您的示例输入,上述结果
ssh-rsa 1024 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00


然后您可以扩展该方法以捕获多个令牌,在这种情况下,我建议使用命名捕获组 ((?&lt;name&gt;...));以下示例使用此类命名的捕获组来提取几个感兴趣的标记:

if ($doc -match '(?<=sftp://)(?<username>[^:]+):(?<password>[^@]+)@(?<host>[^:]+)'){
  # Output the named capture-group values.
  # Note that index notation (['username']) and property
  # notation (.username) can be used interchangeably.
  $Matches.username
  $Matches.password
  $Matches.host
}

使用您的示例输入,上述结果:

username
password
host.name.net

您可以扩展上述内容以捕获所有个感兴趣的令牌。
请注意,. 默认不匹配 \n(换行符)字符。


可选阅读:使用x (IgnoreWhiteSpace) 选项使正则表达式更具可读性:

提取许多标记会导致复杂的正则表达式难以阅读,在这种情况下,x (IgnoreWhiteSpace) 正则表达式选项可以提供帮助(作为内联选项,(?x) 在正则表达式):

if ($doc -match '(?x)
    (?<=sftp://)(?<username>[^:]+)
    :(?<password>[^@]+)
    @(?<host>[^:]+)
    :(?<port>\d+)
    \s+hostkey="(?<sshkey>.+?)"
    \n+get\ File\*\.txt\ (?<localpath>.+)
    \nmv\ File\*\.txt\ (?<remotepath>.+)
  '){
    # Output the named capture-group values.
    $Matches.GetEnumerator() | ? Key -ne 0
}

注意用于使正则表达式更具可读性(将其分散到多行)的空格在匹配时是如何忽略的,而要在输入中匹配的空格必须转义(例如,匹配单个空格,[ ],或 \s 匹配任何空白字符。)

使用您的示例输入,上述结果如下:

Name                           Value
----                           -----
host                           host.name.net
localpath                      \local\path\Client\File.txt
port                           22
sshkey                         ssh-rsa 1024 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
remotepath                     /remote/archive/
password                       password
username                       username

请注意,捕获组乱序的原因是$Matches 是一个哈希表(类型为[hashtable]),其键枚举顺序是一个实现工件:没有特定的枚举订单有保障。

但是,对捕获组的随机访问效果很好;例如,$Matches.port 将返回 22

【讨论】:

  • 我喜欢这种方法,因为正则表达式似乎更有意义,但是当我去获取文件名时我被卡住了。我认为这是因为我要换行,但我不确定如何将其包含在正则表达式中。谢谢你。 (?&lt;=sftp://)(?&lt;username&gt;[^:]+):(?&lt;password&gt;[^@]+)@(?&lt;host&gt;[^:]+):(?&lt;port&gt;[^-]+) -hostkey="(?&lt;sshkey&gt;[^"]+)(?&lt;=get )(?&lt;filename&gt;[^/])
  • @MichaelSPalatsi:您还需要匹配中间空格(并且,如前所述,默认情况下 . 不匹配 \n(换行符))。请参阅我的更新,了解如何使用 IgnoreWhiteSpace 正则表达式选项使复杂的表达式更易于管理。
  • 太棒了!那肯定会清理干净。我相信我还有最后一个问题。假设我有一组文件,我打算对所有这些文件使用这个正则表达式,但在某些文件中,我的一个分组可能不匹配任何东西。我该如何处理?
  • 很高兴听到这个消息,@MichaelSPalatsi。至于您的后续问题:这很难抽象地回答。我建议您创建一个新问题,通过具体示例仅关注该问题。完成后,请随时在此处 ping 我,我很乐意查看。
猜你喜欢
  • 1970-01-01
  • 2020-11-13
  • 2015-05-10
  • 1970-01-01
  • 2014-08-05
  • 1970-01-01
  • 2014-04-27
  • 2018-07-28
  • 1970-01-01
相关资源
最近更新 更多