You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PowerShell解析Windows DNS区域文件并转换格式

优化Windows DNS区域文件解析与格式转换的PowerShell方案

需求说明

需要解析Windows DNS区域文件,提取所有DNS记录并转换为可导入其他DNS软件的格式,要求每条记录用单个空格分隔,同时正确处理TXT(移除括号)、SRV(提取优先级、权重、端口、目标)等特殊类型记录。现有正则表达式和PowerShell代码存在解析失败的情况,需要优化实现。


DNS区域文件示例

;
;  Zone NS records
;

@                       NS  servweb.test.com.
servweb.test.com.  A    31.233.196.98
@                       NS  servweb3.test.com.
servweb3.test.com. A    91.133.157.25

;
;  Zone records
;

@                       A   85.233.152.58
@                       MX  10  mail.factory.com.
@                       MX  20  mx2.factory.com.
@                       MX  50  servmail3.test.fr.
@                       TXT ( "v=spf1 include:spf.sendinblue.com mx ~all" )
@                       TXT ( "MS=ms94956023" )
_dmarc                  TXT ( "v=DMARC1; p=none; sp=none; rua=mailto:dmarc@mailinblue.com!10m; ruf=mailto:dmarc@mailinblue.com!10m; rf=afrf; pct=100; ri=86400" )
mail._domainkey         TXT ( "k=rsa;p=MIGfMA0GCSqNADCBiQKBgQDeMVIzrCa3T14JsNY0viLQBwXsa7shBD6TrBkswsFUToPyMRWC9tbR/5ey0nRBH0ZVxp+lsmTxid2Y2z+FApQ6raISgXE" )
_autodiscover._tcp      SRV 0 0 443 owa.factory.com.
_sipfederationtls._tcp  SRV 100 1 5061  sipfed.online.lync.com.
_sip._tls               SRV 100 1 443   sipdir.online.lync.com.
appli                   A   193.233.140.115
cpanel                  A   85.33.152.58
cpcalendars             A   85.33.152.58
cpcontacts              A   85.33.152.58
ftp                     CNAME   factory.com.
lyncdiscover            CNAME   webdir.online.lync.com.
mail                    A   90.115.33.93

现有实现代码

$domains = Import-Csv -Path C:\temp\export_domaines_termines.csv -Delimiter ";"
foreach ($domain in $domains){


    $ndd = $domain.Name
    $nomfichier = "$ndd.dns"
    $records = get-content -Path "C:\temp\dns\dns\$nomfichier" | Where-Object { $_ -notmatch '^\;' -and $_.trim() -ne "" } | Select-Object -Skip 10
    $fichiergandi = New-Item -Path "C:\temp\dns\$nomfichier.txt" -ItemType File -Force -Confirm:$false


    foreach ($record in $records){
        $type = $prio = $value = $donnees = $name = $null
        $match = Select-String '(\S+)\s+([A-Za-z\s]+)[ \t](\S+)' -InputObject $record
        $type = $match.matches.groups[2].value

            if ($type -like "MX") {
                $match = Select-String '(\S+)\s+([A-Za-z\s]+)[ \t](\S+)[ \t](\S+)' -InputObject $record
                $name = $match.matches.groups[1].value
                $prio = $match.matches.groups[3].value
                $MX = $match.matches.groups[4].value
                "$name 10800 IN $type $prio $MX" | Out-File -FilePath $fichiergandi -Append
            }
            elseif ($type -like "TXT") {
                $match = Select-String '(\S+)\s+([A-Za-z\s]+)[ \t](\S+)[ \t]\(([^\)]+)\)' -InputObject $record
                $name = $match.matches.groups[1].value
                $donnees = ($match.matches.groups[4].value).Trim()
                "$name 10800 IN $type $donnees" | Out-File -FilePath $fichiergandi -Append
            }

            elseif ($type -like "SRV") {
                $match = Select-String '(\S+)\s+([A-Za-z\s]+)[ \t](\S+)\s(\S+)\s(\S+)\s(\S+)' -InputObject $record
                $name = $match.matches.groups[1].value
                $prio = $match.matches.groups[3].value
                $poids = $match.matches.groups[4].value
                $port = $match.matches.groups[5].value
                $cible = $match.matches.groups[6].value
                "$name 10800 IN $type $prio $poids $port $cible" | Out-File -FilePath $fichiergandi -Append
            }

            else {
                $name = $match.matches.groups[1].value
                $donnees = $match.matches.groups[3].value
                "$name 10800 IN $type $donnees" | Out-File -FilePath $fichiergandi -Append
            }
    }
}

目标输出格式

@ 10800 IN A 85.236.152.133
@ 10800 IN MX 10 mail.example.com.
@ 10800 IN MX 20 mx2.example.com.
@ 10800 IN MX 50 servmail3.example.fr.
@ 10800 IN TXT "Sendinblue-code:022f2cda7e5e0c6d945db"
@ 10800 IN TXT "v=spf1 include:spf.sendinblue.com mx ~all"
@ 10800 IN TXT "MS=ms94956023"
_autodiscover._tcp 10800 IN SRV 0 0 443 owa.example.com.
_sipfederationtls._tcp 10800 IN SRV 100 1 5061 sipfed.online.lync.com.
_sip._tls 10800 IN SRV 100 1 443 sipdir.online.lync.com.

优化后的实现方案

核心优化点

  1. 替换固定Select-Object -Skip 10为动态过滤注释/空行,适配不同结构的DNS文件
  2. 使用更精准的正则表达式,一次匹配核心字段,避免多次调用Select-String
  3. 统一记录处理逻辑,简化分支判断
  4. 更可靠地处理TXT记录的括号和空格,SRV记录的多字段拆分

优化代码

$domains = Import-Csv -Path C:\temp\export_domaines_termines.csv -Delimiter ";"
$ttl = "10800" # 统一TTL值

foreach ($domain in $domains) {
    $ndd = $domain.Name
    $nomfichier = "$ndd.dns"
    $dnsFilePath = Join-Path -Path "C:\temp\dns\dns" -ChildPath $nomfichier
    $outputFilePath = Join-Path -Path "C:\temp\dns" -ChildPath "$nomfichier.txt"

    # 读取并过滤有效记录:跳过注释行、空行
    $records = Get-Content -Path $dnsFilePath | Where-Object {
        $trimmed = $_.Trim()
        $trimmed -ne "" -and $trimmed -notmatch '^;'
    }

    # 初始化输出内容数组
    $outputContent = @()

    foreach ($record in $records) {
        $record = $record.Trim()
        # 匹配通用记录结构:名称 类型 后续字段
        if ($record -match '^(\S+)\s+([A-Z]+)\s+(.*)$') {
            $name = $matches[1]
            $type = $matches[2]
            $content = $matches[3]

            switch ($type) {
                "MX" {
                    # 拆分MX的优先级和目标地址
                    if ($content -match '^(\d+)\s+(\S+)$') {
                        $outputContent += "$name $ttl IN $type $($matches[1]) $($matches[2])"
                    }
                }
                "TXT" {
                    # 移除TXT记录的包裹括号,保留内部文本和引号
                    $cleanContent = $content -replace '^\s*\(\s*(.*?)\s*\)\s*$', '$1'
                    $outputContent += "$name $ttl IN $type $cleanContent"
                }
                "SRV" {
                    # 拆分SRV的优先级、权重、端口、目标地址
                    if ($content -match '^(\d+)\s+(\d+)\s+(\d+)\s+(\S+)$') {
                        $outputContent += "$name $ttl IN $type $($matches[1]) $($matches[2]) $($matches[3]) $($matches[4])"
                    }
                }
                default {
                    # 处理A、CNAME、NS等简单类型记录
                    $outputContent += "$name $ttl IN $type $content"
                }
            }
        }
    }

    # 一次性写入文件,提升效率
    $outputContent | Out-File -FilePath $outputFilePath -Encoding utf8 -Force
}

代码说明

  • 动态过滤:通过Where-Object跳过所有注释行(以;开头)和空行,无需依赖固定行数的Skip,兼容性更强
  • 正则匹配:用一个基础正则匹配所有记录的名称、类型和内容部分,再针对不同类型做细分处理
  • TXT处理:用正则移除包裹内容的括号,保留内部的引号和文本
  • 性能优化:先收集所有输出内容到数组,再一次性写入文件,比循环Append效率更高
  • 可读性:用switch替代多分支if-else,逻辑更清晰

内容的提问来源于stack exchange,提问作者Marek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 12:10:56