Skip to content

Instantly share code, notes, and snippets.

@comoc
Last active April 30, 2026 00:56
Show Gist options
  • Select an option

  • Save comoc/9611de85d25bbbda632bd4e611565490 to your computer and use it in GitHub Desktop.

Select an option

Save comoc/9611de85d25bbbda632bd4e611565490 to your computer and use it in GitHub Desktop.
LAN内機器の種別を推定するPowerShellスクリプト (Shift-JISかUTF-8+BOMで保存すること)
<#
.SYNOPSIS
LAN内機器の種別を推定するスクリプト (既定は画面出力のみ)
.DESCRIPTION
既定では Get-NetNeighbor から LAN 内機器を取得し、
OUI ベンダー、TTL からの OS 推定、NetBIOS 名、代表ポート開閉、
dns-sd.exe (利用可能時) による mDNS/Bonjour 機器名を取得して、
DeviceType (機器カテゴリ推定) を付与し画面表示する。
-InputCsv 指定時は Get-LanDevices.ps1 が出した CSV を入力として使う。
-SaveCsv または -OutputPath 指定時のみ CSV 保存。
.PARAMETER InputCsv
Get-LanDevices.ps1 が出した CSV のパス。指定すると CSV を入力に使う。
省略時は Get-NetNeighbor から直接取得 (既定動作)。
.PARAMETER Discover
後方互換用 (現在は既定動作と同じため指定しなくても可)。
.PARAMETER SaveCsv
既定パス (カレント\lan-identified_<timestamp>.csv) に CSV 保存する。
.PARAMETER OutputPath
出力 CSV パスを明示。指定すると暗黙的に保存ON。
.PARAMETER ScanPorts
代表ポート (22,80,443,445,515,631,8009,9100,32400 ほか) のスキャンを行う。既定 ON。
.PARAMETER UseOnlineOuiLookup
内蔵 OUI 表に無いものを api.macvendors.com に問い合わせる。
.PARAMETER NoMdns
dns-sd.exe が利用可能でも mDNS 検索を行わない。
.PARAMETER MdnsBrowseSeconds
mDNS browse の待機秒数。既定 3。
.PARAMETER PortTimeoutMs
1ポートあたりの接続タイムアウト [ms]。既定 300。
.EXAMPLE
.\Identify-LanDevices.ps1
既定動作: Get-NetNeighbor + (利用可能なら) mDNS で取得し画面表示
.EXAMPLE
.\Identify-LanDevices.ps1 -UseOnlineOuiLookup -SaveCsv
オンラインOUI検索 + CSV保存
#>
[CmdletBinding()]
param(
[string]$InputCsv,
[switch]$Discover,
[switch]$SaveCsv,
[string]$OutputPath,
[bool]$ScanPorts = $true,
[switch]$UseOnlineOuiLookup,
[switch]$NoMdns,
[int]$MdnsBrowseSeconds = 3,
[int]$PortTimeoutMs = 300,
[int]$PingTimeoutMs = 400
)
#-------------------------------------------------------------------------------
# 内蔵 OUI 表
#-------------------------------------------------------------------------------
$script:InternalOui = @{
'B827EB' = 'Raspberry Pi Foundation'
'DCA632' = 'Raspberry Pi Trading'
'E45F01' = 'Raspberry Pi Trading'
'28CDC1' = 'Raspberry Pi Trading'
'D83ADD' = 'Raspberry Pi Trading'
'2CCF67' = 'Raspberry Pi Trading'
'240AC4' = 'Espressif'
'30AEA4' = 'Espressif'
'7C9EBD' = 'Espressif'
'A020A6' = 'Espressif'
'8CAAB5' = 'Espressif'
'840D8E' = 'Espressif'
'C45BBE' = 'Espressif'
'C8C9A3' = 'Espressif'
'94B97E' = 'Espressif'
'005056' = 'VMware'
'000C29' = 'VMware'
'001C14' = 'VMware'
'00155D' = 'Microsoft Hyper-V'
'080027' = 'VirtualBox (Oracle)'
'0A0027' = 'VirtualBox (Oracle)'
'7C1E52' = 'Microsoft'
'286521' = 'Microsoft'
'0019FD' = 'Nintendo'
'7CBB8A' = 'Nintendo'
'CC9E00' = 'Nintendo'
'00074D' = 'BUFFALO'
'4CE676' = 'BUFFALO'
'9CA525' = 'BUFFALO'
'00601D' = 'I-O DATA'
'009095' = 'I-O DATA'
'0008EE' = 'NEC'
'00007A' = 'NEC'
'08007A' = 'Sharp'
'B0BAC4' = 'Panasonic'
'402CF4' = 'Panasonic'
'001132' = 'Synology'
'244BFE' = 'QNAP'
'24E4A8' = 'QNAP'
}
$script:OuiOnlineCache = @{}
#-------------------------------------------------------------------------------
# OUI ベンダー解決
#-------------------------------------------------------------------------------
function Get-OuiVendor {
param([string]$MacAddress, [switch]$UseOnline)
if (-not $MacAddress) { return '' }
$oui = ($MacAddress -replace '[-:]', '').ToUpper()
if ($oui.Length -lt 6) { return '' }
$oui = $oui.Substring(0, 6)
$firstByte = [Convert]::ToInt32($oui.Substring(0, 2), 16)
if (($firstByte -band 0x02) -eq 0x02) {
return '(Locally administered)'
}
if ($script:InternalOui.ContainsKey($oui)) { return $script:InternalOui[$oui] }
if ($script:OuiOnlineCache.ContainsKey($oui)) { return $script:OuiOnlineCache[$oui] }
if ($UseOnline) {
try {
$url = "https://api.macvendors.com/{0}" -f $oui
$vendor = Invoke-RestMethod -Uri $url -TimeoutSec 5 -ErrorAction Stop
$script:OuiOnlineCache[$oui] = $vendor
Start-Sleep -Milliseconds 1100
return $vendor
} catch {
$script:OuiOnlineCache[$oui] = ''
return ''
}
}
return ''
}
#-------------------------------------------------------------------------------
# Ping TTL → OS 推定
#-------------------------------------------------------------------------------
function Get-PingTtl {
param([string]$IPAddress, [int]$TimeoutMs = 400)
try {
$ping = New-Object System.Net.NetworkInformation.Ping
$reply = $ping.Send($IPAddress, $TimeoutMs)
if ($reply.Status -eq 'Success') {
$ttl = $reply.Options.Ttl
$osGuess = switch ($true) {
($ttl -ge 250) { 'Embedded/Cisco (TTL=255)' ; break }
(($ttl -ge 110) -and ($ttl -le 128)) { 'Windows (TTL=128)' ; break }
(($ttl -ge 60) -and ($ttl -le 64)) { 'Linux/macOS/iOS/Android (TTL=64)' ; break }
default { "TTL=$ttl" }
}
return [PSCustomObject]@{ Ttl = $ttl; OsGuess = $osGuess }
}
} catch { }
return [PSCustomObject]@{ Ttl = $null; OsGuess = '' }
}
#-------------------------------------------------------------------------------
# NetBIOS 名取得
#-------------------------------------------------------------------------------
function Get-NetBIOSName {
param([string]$IPAddress)
try {
$output = & nbtstat.exe -A $IPAddress 2>$null
if (-not $output) { return '' }
$line = $output | Select-String -Pattern '<00>\s+UNIQUE' | Select-Object -First 1
if ($line) {
return ($line.Line.Trim() -split '\s+')[0]
}
} catch { }
return ''
}
#-------------------------------------------------------------------------------
# ポートスキャン
#-------------------------------------------------------------------------------
$script:CommonPorts = @(
@{ Port = 22; Service = 'SSH' },
@{ Port = 80; Service = 'HTTP' },
@{ Port = 139; Service = 'NetBIOS' },
@{ Port = 443; Service = 'HTTPS' },
@{ Port = 445; Service = 'SMB' },
@{ Port = 515; Service = 'LPD' },
@{ Port = 548; Service = 'AFP' },
@{ Port = 631; Service = 'IPP' },
@{ Port = 3389; Service = 'RDP' },
@{ Port = 5000; Service = 'UPnP/Synology' },
@{ Port = 7000; Service = 'AirPlay' },
@{ Port = 8009; Service = 'Cast' },
@{ Port = 9100; Service = 'RAW-Print' },
@{ Port = 32400; Service = 'Plex' }
)
function Test-OpenPorts {
param([string]$IPAddress, [int]$TimeoutMs = 300)
$tasks = foreach ($p in $script:CommonPorts) {
$client = New-Object System.Net.Sockets.TcpClient
[PSCustomObject]@{
Port = $p.Port
Service = $p.Service
Client = $client
Task = $client.ConnectAsync($IPAddress, $p.Port)
}
}
$deadline = (Get-Date).AddMilliseconds($TimeoutMs)
while ((Get-Date) -lt $deadline) {
$pending = $tasks | Where-Object { -not $_.Task.IsCompleted }
if (-not $pending) { break }
Start-Sleep -Milliseconds 30
}
$open = foreach ($t in $tasks) {
if ($t.Task.IsCompletedSuccessfully) {
"$($t.Port)/$($t.Service)"
}
try { $t.Client.Close() } catch { }
}
return ($open -join ',')
}
#-------------------------------------------------------------------------------
# mDNS / Bonjour: dns-sd.exe を使った機器名検索
#-------------------------------------------------------------------------------
function Test-DnsSdAvailable {
return ($null -ne (Get-Command dns-sd.exe -ErrorAction SilentlyContinue))
}
function Resolve-MdnsHostname {
param([string]$Hostname, [string]$DnsSdPath)
if (-not $Hostname) { return $null }
$Hostname = $Hostname.TrimEnd('.')
# Method 1: System.Net.Dns (Windows 10+ の mDNS リゾルバ)
try {
$addrs = [System.Net.Dns]::GetHostAddresses($Hostname)
$v4 = $addrs | Where-Object { $_.AddressFamily -eq 'InterNetwork' } | Select-Object -First 1
if ($v4) { return $v4.ToString() }
} catch { }
# Method 2: dns-sd -G v4 で同期解決
if ($DnsSdPath) {
$tempFile = [System.IO.Path]::GetTempFileName()
try {
$proc = Start-Process -FilePath $DnsSdPath `
-ArgumentList @('-G', 'v4', $Hostname) `
-RedirectStandardOutput $tempFile `
-NoNewWindow -PassThru -WindowStyle Hidden -ErrorAction Stop
Start-Sleep -Milliseconds 1200
try { $proc | Stop-Process -Force -ErrorAction SilentlyContinue } catch { }
$content = Get-Content $tempFile -Raw -ErrorAction SilentlyContinue
if ($content -and $content -match '\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\s|$)') {
return $matches[1]
}
} catch { }
finally {
Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
}
}
return $null
}
function Invoke-MdnsDiscovery {
param(
[int]$BrowseSeconds = 3,
[int]$ResolveSeconds = 2
)
$cmd = Get-Command dns-sd.exe -ErrorAction SilentlyContinue
if (-not $cmd) {
Write-Host "[mDNS] dns-sd.exe が見つかりません (Bonjour for Windows 未インストール)。スキップ。" -ForegroundColor DarkYellow
return @{}
}
$dnssdPath = $cmd.Source
Write-Host ("[mDNS] dns-sd.exe で機器名を検索中 ({0}秒)..." -f $BrowseSeconds) -ForegroundColor Cyan
$serviceTypes = @(
'_http._tcp', '_https._tcp', '_ssh._tcp',
'_ipp._tcp', '_ipps._tcp', '_pdl-datastream._tcp', '_printer._tcp',
'_smb._tcp', '_afpovertcp._tcp',
'_airplay._tcp', '_raop._tcp', '_googlecast._tcp',
'_workstation._tcp', '_companion-link._tcp',
'_apple-mobdev2._tcp', '_homekit._tcp',
'_device-info._tcp'
)
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ('dns-sd-' + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
try {
# ===== Stage 1: 各サービスタイプを並列 browse =====
$browseProcs = foreach ($svc in $serviceTypes) {
$safeName = $svc -replace '[^a-zA-Z0-9]', '_'
$outfile = Join-Path $tempDir "browse_$safeName.txt"
$errfile = Join-Path $tempDir "browse_$safeName.err"
try {
$proc = Start-Process -FilePath $dnssdPath `
-ArgumentList @('-B', $svc, 'local.') `
-RedirectStandardOutput $outfile `
-RedirectStandardError $errfile `
-NoNewWindow -PassThru -WindowStyle Hidden -ErrorAction Stop
[PSCustomObject]@{ Service = $svc; Process = $proc; OutFile = $outfile }
} catch {
$null
}
}
$browseProcs = $browseProcs | Where-Object { $_ }
Start-Sleep -Seconds $BrowseSeconds
foreach ($p in $browseProcs) {
try { $p.Process | Stop-Process -Force -ErrorAction SilentlyContinue } catch { }
}
# ===== Stage 2: 出力からインスタンス名を抽出 =====
$instances = @{}
foreach ($p in $browseProcs) {
if (-not (Test-Path $p.OutFile)) { continue }
$lines = Get-Content $p.OutFile -ErrorAction SilentlyContinue
foreach ($line in $lines) {
if ($line -match '^\s*\d{1,2}:\d{2}:\d{2}\.\d+\s+Add\s+\d+\s+\d+\s+\S+\s+\S+\s+(.+?)\s*$') {
$instance = $matches[1].Trim()
if ($instance) {
$key = "{0}|{1}" -f $p.Service, $instance
if (-not $instances.ContainsKey($key)) {
$instances[$key] = @{ Service = $p.Service; Instance = $instance }
}
}
}
}
}
if ($instances.Count -eq 0) {
Write-Host "[mDNS] 応答なし (ネットワークに mDNS 機器がない or Bonjour サービス停止)。" -ForegroundColor DarkYellow
return @{}
}
Write-Host ("[mDNS] {0} 件のサービス応答を発見。ホスト名解決中..." -f $instances.Count) -ForegroundColor Cyan
# ===== Stage 3: 各インスタンスを -L で解決 =====
$resolveProcs = foreach ($key in $instances.Keys) {
$info = $instances[$key]
$safeName = $key -replace '[^a-zA-Z0-9]', '_'
if ($safeName.Length -gt 80) { $safeName = $safeName.Substring(0, 80) }
$outfile = Join-Path $tempDir "resolve_$safeName.txt"
$errfile = Join-Path $tempDir "resolve_$safeName.err"
try {
$proc = Start-Process -FilePath $dnssdPath `
-ArgumentList @('-L', $info.Instance, $info.Service, 'local.') `
-RedirectStandardOutput $outfile `
-RedirectStandardError $errfile `
-NoNewWindow -PassThru -WindowStyle Hidden -ErrorAction Stop
[PSCustomObject]@{
Instance = $info.Instance
Service = $info.Service
Process = $proc
OutFile = $outfile
}
} catch {
$null
}
}
$resolveProcs = $resolveProcs | Where-Object { $_ }
Start-Sleep -Seconds $ResolveSeconds
foreach ($p in $resolveProcs) {
try { $p.Process | Stop-Process -Force -ErrorAction SilentlyContinue } catch { }
}
# ===== Stage 4: 出力から target hostname を抽出 =====
# instance -> @{ Hostname; Services }
$instanceMap = @{}
foreach ($p in $resolveProcs) {
if (-not (Test-Path $p.OutFile)) { continue }
$content = Get-Content $p.OutFile -Raw -ErrorAction SilentlyContinue
if (-not $content) { continue }
if ($content -match 'can be reached at\s+(\S+?):\d+') {
$hostname = $matches[1].TrimEnd('.')
if (-not $instanceMap.ContainsKey($p.Instance)) {
$instanceMap[$p.Instance] = @{ Hostname = $hostname; Services = New-Object System.Collections.Generic.List[string] }
}
if (-not $instanceMap[$p.Instance].Services.Contains($p.Service)) {
$instanceMap[$p.Instance].Services.Add($p.Service)
}
}
}
# ===== Stage 5: hostname → IPv4 解決 =====
$ipMap = @{}
foreach ($instance in $instanceMap.Keys) {
$info = $instanceMap[$instance]
$ip = Resolve-MdnsHostname -Hostname $info.Hostname -DnsSdPath $dnssdPath
if (-not $ip) { continue }
if (-not $ipMap.ContainsKey($ip)) {
$ipMap[$ip] = [PSCustomObject]@{
Name = $instance
Hostname = $info.Hostname
Services = ($info.Services | Sort-Object) -join ','
}
} else {
# 同一IPに複数機能がぶら下がるケース (例: AirPlay + AirPort)
if ($ipMap[$ip].Name -notmatch [regex]::Escape($instance)) {
$ipMap[$ip].Name = "{0}; {1}" -f $ipMap[$ip].Name, $instance
}
$existing = $ipMap[$ip].Services -split ','
$combined = ($existing + $info.Services | Sort-Object -Unique | Where-Object { $_ }) -join ','
$ipMap[$ip].Services = $combined
}
}
Write-Host ("[mDNS] {0} 機器の名前を取得。" -f $ipMap.Count) -ForegroundColor Green
return $ipMap
}
finally {
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
#-------------------------------------------------------------------------------
# 機器カテゴリ推定 (mDNS サービスも考慮)
#-------------------------------------------------------------------------------
function Get-DeviceTypeGuess {
param(
[string]$Vendor,
[string]$OsGuess,
[string]$OpenPorts,
[string]$Hostname,
[string]$NetBIOSName,
[string]$MdnsName,
[string]$MdnsServices,
[string]$Note
)
$v = $Vendor.ToLower()
$h = "$Hostname $NetBIOSName $MdnsName".ToLower()
$p = ",$OpenPorts,"
$m = ",$MdnsServices,"
if ($Note -match 'Gateway') { return 'Router / Default Gateway' }
if ($v -match 'vmware|hyper-v|virtualbox|qemu|kvm|xen') { return "Virtual Machine ($Vendor)" }
if ($v -match 'raspberry') { return 'Raspberry Pi' }
if ($v -match 'espressif') { return 'ESP32 / ESP8266 / M5Stack (IoT)' }
# mDNS シグナル優先
if ($m -match ',_googlecast\._tcp,') { return 'Chromecast / Google TV' }
if ($m -match ',_airplay\._tcp,|,_raop\._tcp,') { return "AirPlay device ($Vendor)" }
if ($m -match ',_homekit\._tcp,') { return "HomeKit device ($Vendor)" }
if ($m -match ',_apple-mobdev2\._tcp,') { return 'Apple iOS device (iPhone/iPad)' }
if ($m -match ',_companion-link\._tcp,' -and $v -match 'apple') { return 'Apple device (Mac/iPhone/iPad)' }
if ($m -match ',_ipp\._tcp,|,_ipps\._tcp,|,_pdl-datastream\._tcp,|,_printer\._tcp,') {
return "Printer ($Vendor)"
}
if ($m -match ',_workstation\._tcp,' -and $v -notmatch 'apple') {
return "Linux/Unix workstation ($Vendor)"
}
# ポート系
if ($p -match ',9100/|,515/|,631/') { return "Printer ($Vendor)" }
if ($v -match 'synology|qnap|netgear.*nas|buffalo' -and $p -match ',445/|,5000/|,548/') {
return "NAS ($Vendor)"
}
if ($p -match ',8009/') { return 'Chromecast / Google Cast device' }
if ($p -match ',7000/') { return "AirPlay device ($Vendor)" }
if ($p -match ',32400/') { return "Plex Media Server ($Vendor)" }
if ($v -match 'nintendo') { return 'Nintendo Switch / Wii' }
if ($v -match 'sony.*interactive|playstation') { return 'PlayStation' }
if ($v -match 'microsoft' -and $h -match 'xbox') { return 'Xbox' }
if ($v -match 'apple') {
if ($p -match ',548/|,7000/') { return 'Apple device (Mac/Apple TV)' }
return 'Apple device (iPhone/iPad/Mac)'
}
if ($OsGuess -match 'Windows' -or $p -match ',445/|,3389/|,139/' -or $NetBIOSName) {
if ($NetBIOSName) { return "Windows PC ($NetBIOSName)" }
return 'Windows PC'
}
if ($OsGuess -match 'Linux' -and $p -match ',22/') { return "Linux/Unix host ($Vendor)" }
if ($v -match 'buffalo|nec|netgear|tp-link|tplink|asus|aterm|cisco|aruba|i-?o data|iodata') {
return "Router / AP ($Vendor)"
}
if ($v -match 'sony|sharp|panasonic|lg electronics|samsung') {
return "Consumer electronics ($Vendor)"
}
if ($Vendor) { return "Unknown ($Vendor)" }
return 'Unknown'
}
#-------------------------------------------------------------------------------
# === メイン処理 ===
#-------------------------------------------------------------------------------
Write-Host "===== LAN 機器識別スクリプト =====" -ForegroundColor Yellow
# CSV 保存判定
$shouldSave = $SaveCsv -or $PSBoundParameters.ContainsKey('OutputPath')
if ($shouldSave -and -not $OutputPath) {
$OutputPath = Join-Path -Path (Get-Location) -ChildPath ("lan-identified_{0}.csv" -f (Get-Date -Format 'yyyyMMdd_HHmmss'))
}
# 入力取得 (InputCsv 指定が無ければ Discover が既定動作)
if ($InputCsv) {
if (-not (Test-Path $InputCsv)) {
Write-Error "InputCsv が見つかりません: $InputCsv"
exit 1
}
$devices = Import-Csv -Path $InputCsv -Encoding UTF8
} else {
Write-Host "[Discover] Get-NetNeighbor から取得中..." -ForegroundColor Cyan
$devices = Get-NetNeighbor -AddressFamily IPv4 -ErrorAction SilentlyContinue |
Where-Object {
$_.State -in 'Reachable', 'Stale', 'Permanent' -and
$_.LinkLayerAddress -and
$_.LinkLayerAddress -ne '00-00-00-00-00-00' -and
$_.IPAddress -notmatch '^(127\.|169\.254\.|22[4-9]\.|23[0-9]\.|255\.)'
} | ForEach-Object {
[PSCustomObject]@{
IPAddress = $_.IPAddress
MACAddress = $_.LinkLayerAddress
Hostname = ''
State = $_.State
Note = ''
}
}
}
if (-not $devices -or @($devices).Count -eq 0) {
Write-Warning "対象機器がありません。"
exit 0
}
Write-Host ("[対象] {0} 台" -f @($devices).Count) -ForegroundColor Cyan
if ($UseOnlineOuiLookup) {
Write-Host "[Note] api.macvendors.com 利用 (1 req/sec 制限)" -ForegroundColor DarkYellow
}
# mDNS 検索 (一括)
$mdnsMap = @{}
if (-not $NoMdns) {
if (Test-DnsSdAvailable) {
$mdnsMap = Invoke-MdnsDiscovery -BrowseSeconds $MdnsBrowseSeconds
} else {
Write-Host "[mDNS] dns-sd.exe 未インストールのためスキップ (iTunes / Bonjour Print Services で導入可)。" -ForegroundColor DarkYellow
}
}
# 各デバイスを enrich
$results = foreach ($d in $devices) {
Write-Host (" 処理中: {0,-15} {1}" -f $d.IPAddress, $d.MACAddress) -ForegroundColor DarkGray
$vendor = Get-OuiVendor -MacAddress $d.MACAddress -UseOnline:$UseOnlineOuiLookup
$ttlInfo = Get-PingTtl -IPAddress $d.IPAddress -TimeoutMs $PingTimeoutMs
$nbName = Get-NetBIOSName -IPAddress $d.IPAddress
$openPorts = ''
if ($ScanPorts) {
$openPorts = Test-OpenPorts -IPAddress $d.IPAddress -TimeoutMs $PortTimeoutMs
}
# mDNS 情報
$mdnsName = ''
$mdnsHostname = ''
$mdnsServices = ''
if ($mdnsMap.ContainsKey($d.IPAddress)) {
$info = $mdnsMap[$d.IPAddress]
$mdnsName = $info.Name
$mdnsHostname = $info.Hostname
$mdnsServices = $info.Services
}
$deviceType = Get-DeviceTypeGuess `
-Vendor $vendor `
-OsGuess $ttlInfo.OsGuess `
-OpenPorts $openPorts `
-Hostname $d.Hostname `
-NetBIOSName $nbName `
-MdnsName $mdnsName `
-MdnsServices $mdnsServices `
-Note $d.Note
[PSCustomObject]@{
IPAddress = $d.IPAddress
MACAddress = $d.MACAddress
Vendor = $vendor
DeviceType = $deviceType
MdnsName = $mdnsName
MdnsHostname = $mdnsHostname
MdnsServices = $mdnsServices
OsGuess = $ttlInfo.OsGuess
Ttl = $ttlInfo.Ttl
Hostname = $d.Hostname
NetBIOSName = $nbName
OpenPorts = $openPorts
State = $d.State
Note = $d.Note
}
}
# 表示
Write-Host ("`n===== 識別結果 =====") -ForegroundColor Green
$results | Format-Table IPAddress, Vendor, DeviceType, MdnsName, NetBIOSName, OsGuess, OpenPorts -AutoSize -Wrap
# CSV 保存 (明示指定時のみ)
if ($shouldSave) {
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
$saveMsg = "`nCSV を保存しました: {0}" -f $OutputPath
Write-Host $saveMsg -ForegroundColor Cyan
} else {
Write-Host "`n(CSV保存はスキップ。保存するには -SaveCsv または -OutputPath を指定)" -ForegroundColor DarkGray
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment