Úteis/verificar-discos.cmd (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
<# :
@echo off
set "SELF=%~f0"
powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText($env:SELF,[System.Text.Encoding]::UTF8))"
exit /b %errorlevel%
#>
# ============================================================
# Verificar Discos - saúde dos HDs/SSDs (somente leitura)
# Desenvolvido por Pablo Murad - 2026
# ============================================================
try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
try { $Host.UI.RawUI.WindowTitle = 'Verificar Discos' } catch {}
# ------------------------------------------------------------
# Auto-elevação para administrador (necessário p/ ler tudo)
# ------------------------------------------------------------
$souAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $souAdmin) {
Write-Host 'Solicitando privilégios de administrador...' -ForegroundColor Yellow
try {
Start-Process -FilePath 'cmd.exe' -ArgumentList '/c', "`"$env:SELF`"" -Verb RunAs | Out-Null
} catch {
Write-Host '[!] Sem elevação. Alguns dados (temperatura, testes) podem faltar.' -ForegroundColor Yellow
Start-Sleep -Seconds 2
}
exit
}
function Pausar {
Write-Host ''
Write-Host 'Pressione Enter para voltar ao menu...' -ForegroundColor DarkGray
[void][System.Console]::ReadLine()
}
function Titulo($texto) {
Write-Host ''
Write-Host '============================================================' -ForegroundColor DarkCyan
Write-Host " $texto" -ForegroundColor Cyan
Write-Host '============================================================' -ForegroundColor DarkCyan
}
function Cor-Saude($status) {
switch ("$status") {
'Healthy' { 'Green' }
'Warning' { 'Yellow' }
default { if ([string]::IsNullOrWhiteSpace("$status")) { 'DarkGray' } else { 'Red' } }
}
}
function Formatar-Tamanho($bytes) {
if (-not $bytes) { return '-' }
$gb = [double]$bytes / 1GB
if ($gb -ge 1024) { return ('{0:N2} TB' -f ($gb / 1024)) }
return ('{0:N0} GB' -f $gb)
}
$smartctl = Get-Command smartctl -ErrorAction SilentlyContinue
# ------------------------------------------------------------
# Ação 1: Diagnóstico de saúde (rápido, somente leitura)
# ------------------------------------------------------------
function Diagnostico-Saude {
Titulo 'Diagnóstico de saúde'
$discos = Get-PhysicalDisk | Sort-Object DeviceId
foreach ($d in $discos) {
$cor = Cor-Saude $d.HealthStatus
Write-Host ''
Write-Host ("Disco {0}: {1}" -f $d.DeviceId, $d.FriendlyName) -ForegroundColor White
Write-Host (" Tipo: {0} | Tamanho: {1} | Barramento: {2}" -f `
$d.MediaType, (Formatar-Tamanho $d.Size), $d.BusType) -ForegroundColor Gray
Write-Host (" Saúde: {0} (operacional: {1})" -f $d.HealthStatus, ($d.OperationalStatus -join ',')) -ForegroundColor $cor
# Contadores de confiabilidade (temperatura, desgaste, erros)
try {
$rc = $d | Get-StorageReliabilityCounter -ErrorAction Stop
$partes = @()
if ($null -ne $rc.Temperature) { $partes += "Temp: $($rc.Temperature) C" }
if ($null -ne $rc.Wear) { $partes += "Desgaste: $($rc.Wear)%" }
if ($null -ne $rc.PowerOnHours) { $partes += "Horas ligado: $($rc.PowerOnHours)" }
if ($null -ne $rc.ReadErrorsTotal) { $partes += "Erros leitura: $($rc.ReadErrorsTotal)" }
if ($null -ne $rc.WriteErrorsTotal) { $partes += "Erros escrita: $($rc.WriteErrorsTotal)" }
if ($partes.Count -gt 0) { Write-Host (" " + ($partes -join ' | ')) -ForegroundColor DarkGray }
} catch {}
# SMART detalhado via smartctl, se disponível
if ($smartctl) {
try {
$dev = "/dev/pd$($d.DeviceId)"
$saidaS = & smartctl -H -A -d ata $dev 2>$null
if (-not $saidaS) { $saidaS = & smartctl -H -A $dev 2>$null }
$linhaSaude = $saidaS | Select-String 'SMART overall-health|test result'
if ($linhaSaude) { Write-Host (" SMART: " + ($linhaSaude[0].Line.Trim())) -ForegroundColor Gray }
$attrs = $saidaS | Select-String 'Reallocated_Sector|Current_Pending|Offline_Uncorrectable|Power_On_Hours'
foreach ($a in $attrs) { Write-Host (" " + ($a.Line.Trim() -replace '\s{2,}', ' ')) -ForegroundColor DarkGray }
} catch {}
}
}
if (-not $smartctl) {
Write-Host ''
Write-Host '(Dica: para detalhes SMART avançados instale o smartmontools:' -ForegroundColor DarkGray
Write-Host ' winget install smartmontools )' -ForegroundColor DarkGray
}
}
# ------------------------------------------------------------
# Ação 2: Resumo simples
# ------------------------------------------------------------
function Resumo-Simples {
Titulo 'Resumo simples'
Get-PhysicalDisk | Sort-Object DeviceId | ForEach-Object {
$ok = $_.HealthStatus -eq 'Healthy'
$txt = if ($ok) { 'OK' } else { "FALHA/AVISO ($($_.HealthStatus))" }
$cor = Cor-Saude $_.HealthStatus
Write-Host ('[{0}] {1,-32} {2,10} ' -f $_.DeviceId, $_.FriendlyName, (Formatar-Tamanho $_.Size)) -NoNewline
Write-Host $txt -ForegroundColor $cor
}
}
# ------------------------------------------------------------
# Ação 3: Teste de superfície (leitura sequencial, só leitura)
# ------------------------------------------------------------
function Teste-Superficie {
Titulo 'Teste de superfície (somente leitura)'
$discos = Get-PhysicalDisk | Sort-Object DeviceId
foreach ($d in $discos) {
Write-Host (' [{0}] {1} ({2})' -f $d.DeviceId, $d.FriendlyName, (Formatar-Tamanho $d.Size)) -ForegroundColor Gray
}
Write-Host ''
Write-Host 'AVISO: lê todos os setores do disco escolhido. Pode levar HORAS.' -ForegroundColor Yellow
Write-Host 'É seguro (apenas leitura). Ctrl+C cancela a qualquer momento.' -ForegroundColor DarkGray
$sel = Read-Host 'Número do disco para testar (Enter = cancelar)'
if ([string]::IsNullOrWhiteSpace($sel)) { Write-Host 'Cancelado.' -ForegroundColor Gray; return }
$alvo = $discos | Where-Object { "$($_.DeviceId)" -eq $sel.Trim() } | Select-Object -First 1
if (-not $alvo) { Write-Host '[!] Disco não encontrado.' -ForegroundColor Yellow; return }
$total = [int64]$alvo.Size
$caminho = "\\.\PhysicalDrive$($alvo.DeviceId)"
Write-Host ''
Write-Host "Lendo $caminho ($(Formatar-Tamanho $total))..." -ForegroundColor Cyan
$bloco = 8MB
$buffer = New-Object byte[] $bloco
$lidos = [int64]0
$errosRegioes = @()
$inicio = Get-Date
$ultimoPct = -1
try {
$fs = [System.IO.File]::Open($caminho, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
} catch {
Write-Host "[ERRO] Não foi possível abrir o disco: $($_.Exception.Message)" -ForegroundColor Red
return
}
try {
while ($lidos -lt $total) {
try {
$n = $fs.Read($buffer, 0, $bloco)
if ($n -le 0) { break }
$lidos += $n
} catch {
$errosRegioes += ('offset {0:N0} MB' -f ($lidos / 1MB))
# pula o bloco problemático e continua
$lidos += $bloco
try { $fs.Seek($lidos, [System.IO.SeekOrigin]::Begin) | Out-Null } catch { break }
}
$pct = [int](($lidos / $total) * 100)
if ($pct -ne $ultimoPct) {
$ultimoPct = $pct
$decorrido = (Get-Date) - $inicio
$mbs = if ($decorrido.TotalSeconds -gt 0) { ($lidos / 1MB) / $decorrido.TotalSeconds } else { 0 }
Write-Host ("`r Progresso: {0,3}% ({1:N0}/{2:N0} MB) {3:N0} MB/s Erros: {4} " -f `
$pct, ($lidos / 1MB), ($total / 1MB), $mbs, $errosRegioes.Count) -NoNewline -ForegroundColor Cyan
}
}
} finally {
$fs.Close()
}
Write-Host ''
Write-Host ''
if ($errosRegioes.Count -eq 0) {
Write-Host "[OK] Nenhum erro de leitura. Todos os setores lidos com sucesso." -ForegroundColor Green
} else {
Write-Host ("[!] {0} regiões com erro de leitura (possíveis setores defeituosos):" -f $errosRegioes.Count) -ForegroundColor Red
$errosRegioes | Select-Object -First 20 | ForEach-Object { Write-Host " $_" -ForegroundColor Red }
if ($errosRegioes.Count -gt 20) { Write-Host " ... e mais $($errosRegioes.Count - 20)." -ForegroundColor Red }
}
}
# ------------------------------------------------------------
# Menu principal
# ------------------------------------------------------------
while ($true) {
Titulo 'Verificar Discos'
Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray
Write-Host ''
Write-Host ' [1] Diagnóstico de saúde (rápido, recomendado)' -ForegroundColor White
Write-Host ' SMART, temperatura, erros e desgaste de todos os discos' -ForegroundColor DarkGray
Write-Host ' [2] Teste de superfície (lê todos os setores - demorado)' -ForegroundColor White
Write-Host ' Procura setores defeituosos num disco escolhido' -ForegroundColor DarkGray
Write-Host ' [3] Resumo simples (só OK/Falha por disco)' -ForegroundColor White
Write-Host ' [0] Sair' -ForegroundColor White
Write-Host ''
$op = Read-Host 'Escolha (0-3)'
switch ($op.Trim()) {
'1' { Diagnostico-Saude; Pausar }
'2' { Teste-Superficie; Pausar }
'3' { Resumo-Simples; Pausar }
'0' { break }
'' { break }
default { Write-Host '[!] Opção inválida.' -ForegroundColor Yellow; Start-Sleep -Seconds 1 }
}
}
|