Fundamentos de los comandos de PowerShell — operaciones básicas y uso seguro
· Actualizado el: · Go Komura · PowerShell, Windows, Línea de comandos, Automatización, Aprovechamiento de activos existentes
1. Lo primero que hay que entender
PowerShell es un entorno de comandos que permite comprobar la configuración de Windows, organizar archivos, investigar registros (logs), procesar CSV, operar servicios y automatizar tareas repetitivas.
Sin embargo, no hace falta escribir scripts complejos desde el principio. Basta con recordar el siguiente flujo para ser bastante productivo en el trabajo diario.
buscar → ver → filtrar → ordenar → mostrar → modificar si es necesario
Lo básico de PowerShell no consiste en «memorizar comandos largos». Lo importante son estos tres puntos:
- Poder buscar comandos con
Get-CommandyGet-Help - Poder pasar resultados a la siguiente instrucción mediante la canalización
| - Confirmar el efecto con
-WhatIfo-Confirmantes de eliminar, detener o modificar algo
PowerShell permite ejecutar «investigación» y «modificación» en la misma pantalla. Esto es cómodo, pero también significa que una eliminación o una detención se pueden ejecutar de inmediato, por lo que es importante adquirir el hábito de empezar siempre por una comprobación de solo lectura.
El código que aparece en este artículo se publica en GitHub como un conjunto de ejemplos organizado por temas siguiendo la estructura de los capítulos (con un espacio de trabajo de práctica que se crea en una carpeta temporal para poder probar con seguridad), junto con pruebas Pester.
powershell-command-basics - komurasoft-blog-samples (GitHub)
Cómo leer este artículo
Este artículo tiene 26 capítulos. No hace falta leerlo entero de principio a fin. Según su objetivo, es más fácil orientarse leyendo primero los capítulos siguientes.
| Objetivo | Capítulos a leer |
|---|---|
| Voy a empezar a usar PowerShell | Capítulos 1–8 (forma de pensar, cómo buscar, canalización, manejo de archivos) y capítulo 24 (orden de aprendizaje) |
| Quiero investigar registros o configuración | Capítulo 9 (texto), 12 (procesos y servicios), 13 (registro de eventos), 21 (ejemplo de investigación) |
| Quiero procesar CSV o JSON | Capítulos 10, 11 y 15 |
| Quiero convertir un proceso en un script | Capítulos 14, 16, 17 y 18 |
| Quiero eliminar o mover con seguridad | Capítulos 19, 22 y 25 |
| Solo quiero consultar comandos | Capítulo 20 (lista de comandos básicos), 23 (problemas habituales) |
El propio orden de aprendizaje se resume en el capítulo 24, «Orden para aprender los comandos». Cuando vaya a practicar por primera vez, tenga a mano solo la primera etapa del capítulo 24 (Get-Command, Get-Help, Get-Member, Get-Location, Set-Location, Get-ChildItem, Get-Content, Select-String) y siga el texto principal. Con estos ocho comandos claros, el resto se puede consultar según haga falta.
Diferencia con el «recetario práctico de comandos»
Hay otro artículo sobre PowerShell. Como cumplen funciones distintas, úselos de la siguiente manera.
| Artículo | Contenido | Cuándo leerlo |
|---|---|---|
| Este artículo (fundamentos) | Sistema de operación: cómo buscar comandos, la forma de pensar la canalización, el orden para ejecutar con seguridad los comandos de modificación | Léalo primero |
| Recetario práctico de comandos de PowerShell | Piezas por objetivo: contar cantidades y totales (Measure-Object), agregar por categoría (Group-Object), comparar diferencias (Compare-Object), registrar el historial de trabajo (Start-Transcript), etc. |
Cuando en el trabajo real surja un «quiero hacer esto» |
Los puntos a tener en cuenta sobre CSV, procesos, servicios, registro de eventos y Format-* aparecen en ambos artículos. Este artículo trata «por qué se escribe de esa forma», mientras que el recetario práctico trata «la pieza que se usa en cada situación».
2. Diferencias entre PowerShell y el símbolo del sistema
PowerShell se parece al símbolo del sistema tradicional (cmd.exe), pero su forma de pensar interna es diferente.
| Aspecto | Símbolo del sistema | PowerShell |
|---|---|---|
| Salida principal | Cadenas de texto | Objetos |
| Nombres de comandos | dir, copy, etc. |
Get-ChildItem, Copy-Item, etc. |
| Procesamiento de resultados | Centrado en el procesamiento de texto | Filtrado y ordenación mediante propiedades |
| Automatización | Archivos por lotes (batch) | Scripts .ps1 |
| Punto fuerte | Compatibilidad con comandos antiguos | Administración de Windows, CSV, JSON, API, tareas repetitivas |
Por ejemplo, al obtener un listado de archivos, en cmd.exe lo habitual es mirar el resultado de dir como una cadena de texto. En PowerShell, en cambio, el archivo se trata como un objeto con propiedades como Name, Length y LastWriteTime.
Get-ChildItem
Al entender esta diferencia, resulta natural escribir procesos como el siguiente: «mostrar solo los archivos actualizados en los últimos 7 días, ordenados de más reciente a más antiguo».
Get-ChildItem -File |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) } |
Sort-Object LastWriteTime -Descending |
Select-Object Name, Length, LastWriteTime
3. Windows PowerShell 5.1 y PowerShell 7.x
En Windows existen Windows PowerShell 5.1, incluido desde hace tiempo, y PowerShell 7.x, de la nueva serie.
En el trabajo diario resulta más claro pensarlo de la siguiente manera.
| Tipo | Ejecutable | Uso principal |
|---|---|---|
| Windows PowerShell 5.1 | powershell.exe |
Módulos antiguos exclusivos de Windows, scripts existentes, mantenimiento de activos internos |
| PowerShell 7.x | pwsh.exe |
Scripts nuevos, multiplataforma, mejoras continuas de funcionalidad |
Si está aprendiendo desde cero, puede tomar PowerShell 7.x como referencia sin problema. Sin embargo, los scripts de administración antiguos de la empresa o los módulos específicos de Windows a veces están hechos partiendo de Windows PowerShell 5.1.
Por eso, en el trabajo diario conviene comprobar primero la versión.
$PSVersionTable
En los scripts que deban ejecutarse con una versión mínima determinada, escribir una condición al principio reduce los accidentes.
#Requires -Version 7.0
¿Con cuál de las dos funcionan los ejemplos de este artículo?
Salvo que se indique lo contrario, los ejemplos siguientes están escritos para funcionar tal cual tanto en Windows PowerShell 5.1 como en PowerShell 7.x. Sin embargo, aunque el comando sea el mismo, los siguientes puntos cambian de resultado, así que revíselos antes de copiarlos.
| Dónde aparece la diferencia | Windows PowerShell 5.1 | PowerShell 7.x |
|---|---|---|
Columnas mostradas por defecto en Get-Process |
Handles, NPM(K), PM(K), WS(K), CPU(s), Id, SI, ProcessName |
NPM(K), PM(M), WS(M), CPU(s), Id, SI, ProcessName (la memoria en unidades de MB) |
Significado de -Encoding UTF8 |
UTF-8 (con BOM) | UTF-8 (sin BOM). Si desea incluir el BOM, indique utf8BOM |
Al omitir -Encoding |
Varía según el comando (Out-File usa UTF-16LE, Set-Content usa ANSI, Export-Csv usa ASCII) |
Todos usan utf8NoBOM |
En los ejemplos de este artículo, para evitar una codificación de caracteres no deseada, se indica explícitamente -Encoding UTF8 en los comandos de escritura. La forma de pensar sobre la presencia del BOM y los códigos de salto de línea en sí se trata en Cómo entender la codificación de caracteres y los saltos de línea en Windows.
4. Los nombres de los cmdlets siguen el patrón Verbo-Sustantivo
Los comandos estándar de PowerShell tienen básicamente la forma Verbo-Sustantivo.
Get-Process
Get-Service
Get-ChildItem
Copy-Item
Remove-Item
Export-Csv
Al recordar este patrón, resulta más fácil localizar comandos que aún no conoce.
| Verbo | Significado | Ejemplo |
|---|---|---|
Get |
Obtener | Get-Process |
Set |
Establecer | Set-Location |
New |
Crear | New-Item |
Copy |
Copiar | Copy-Item |
Move |
Mover | Move-Item |
Remove |
Eliminar | Remove-Item |
Start |
Iniciar | Start-Service |
Stop |
Detener | Stop-Process |
Import |
Importar | Import-Csv |
Export |
Exportar | Export-Csv |
También se pueden usar nombres cortos como dir, ls, cat o cd, pero en su mayoría son alias.
Get-Command dir
Si va a dejar el código como script, es más seguro usar el nombre oficial en lugar del alias.
# Se puede leer, pero conviene evitarlo en scripts
ls *.log
# La intención queda clara
Get-ChildItem -Filter *.log
5. Los tres primeros comandos de consulta que hay que aprender
Como PowerShell tiene una gran cantidad de comandos, en la práctica resulta más útil aprender «cómo buscarlos» que memorizarlos.
1. Get-Command — buscar los comandos disponibles
Get-Command es el comando para buscar los comandos, funciones, alias y aplicaciones instalados.
# Buscar comandos cuyo sustantivo sea Process
Get-Command -Noun Process
# Buscar comandos relacionados con Service
Get-Command *Service*
# Buscar comandos relacionados con CSV
Get-Command *Csv*
Cuando no recuerda bien el nombre de un comando, use comodines.
Get-Command *Item*
Get-Command *Content*
Get-Command *Json*
2. Get-Help — consultar el modo de uso
Get-Help es el comando para consultar la descripción, los parámetros y los ejemplos de un comando.
Get-Help Get-ChildItem
Get-Help Get-ChildItem -Examples
Get-Help Get-ChildItem -Full
Get-Help Get-ChildItem -Online
Al principio, -Examples resulta muy útil.
Get-Help Where-Object -Examples
Si la ayuda está desactualizada o incompleta, actualícela ejecutando PowerShell con privilegios de administrador.
Update-Help
Según el entorno, puede requerir conexión a internet y permisos. En equipos de la empresa, el proxy o las directivas de administración a veces provocan que la operación falle.
3. Get-Member — ver el contenido de un objeto
La salida de PowerShell, en la mayoría de los casos, no es una «cadena de texto» sino un «objeto». Con Get-Member se puede comprobar qué propiedades y métodos tiene ese objeto.
Get-Process | Get-Member
Get-Service | Get-Member
Get-ChildItem | Get-Member
Por ejemplo, un servicio tiene propiedades como Status y Name.
Get-Service | Select-Object Name, Status
Un archivo tiene propiedades como Name, Length y LastWriteTime.
Get-ChildItem -File | Select-Object Name, Length, LastWriteTime
Cuando no sepa «con qué filtrar», compruebe primero los nombres de las propiedades con
Get-Member.
6. Fundamentos de la canalización (pipeline)
El | de PowerShell pasa el resultado del comando de la izquierda al comando de la derecha.
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10
Este ejemplo realiza el siguiente proceso de izquierda a derecha.
Obtener el listado de procesos
→ ordenar de forma descendente por uso de CPU
→ mostrar solo los 10 primeros
Cómo se muestra la salida
El resultado de Get-Process se muestra por defecto como una tabla con las columnas siguientes. Los valores cambian según el entorno, así que lo que hay que fijarse es en «el orden de las columnas».
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
| Columna | Significado |
|---|---|
NPM(K) |
Uso de memoria del grupo no paginado (KB) |
PM(M) |
Uso de memoria paginable (MB) |
WS(M) |
Tamaño del working set (páginas de memoria a las que se accedió recientemente), en MB |
CPU(s) |
Tiempo de procesador utilizado en todos los procesadores (segundos) |
Id |
Identificador del proceso |
SI |
Identificador de sesión |
ProcessName |
Nombre del proceso |
Esta es la vista por defecto de PowerShell 7.x. En Windows PowerShell 5.1 aparece Handles (número de identificadores abiertos) al principio, y las columnas de memoria son PM(K) y WS(K), en unidades de KB. Aunque los nombres de las columnas sean distintos, los nombres de propiedad que se indican en Sort-Object o Where-Object (CPU, WorkingSet, Id, Name, etc.) son comunes a ambas versiones. El nombre de columna que se muestra en pantalla y el nombre de la propiedad son cosas distintas, así que si una condición de filtrado no funciona, compruebe el nombre real de la propiedad con Get-Process | Get-Member.
Las combinaciones más habituales son las siguientes.
| Comando | Función | Ejemplo |
|---|---|---|
Where-Object |
Filtrar por condición | Obtener solo los servicios detenidos |
Sort-Object |
Ordenar | Ordenar de más reciente a más antiguo por fecha de modificación |
Select-Object |
Elegir columnas o cantidad | Mostrar solo el nombre y el tamaño |
ForEach-Object |
Procesar cada elemento | Procesar archivo por archivo |
Export-Csv |
Exportar a CSV | Guardar los resultados de una investigación |
Where-Object — filtrar por condición
# Mostrar solo los servicios detenidos
Get-Service | Where-Object { $_.Status -eq "Stopped" }
# Mostrar solo los archivos de más de 100 MB
Get-ChildItem -File |
Where-Object { $_.Length -gt 100MB }
# Mostrar solo los archivos cuyo nombre contenga backup
Get-ChildItem -File |
Where-Object { $_.Name -like "*backup*" }
$_ representa el objeto actual que está pasando por la canalización.
Where-Object { $_.Length -gt 100MB }
Esto evalúa si «el Length del archivo que se está examinando en ese momento es mayor que 100 MB».
A partir de PowerShell 3.0 también se puede usar una sintaxis simplificada.
Get-Service | Where-Object Status -EQ "Stopped"
Sin embargo, mientras se es principiante, la sintaxis con scriptblock resulta más fácil de seguir.
Get-Service | Where-Object { $_.Status -eq "Stopped" }
Sort-Object — ordenar
# De mayor a menor uso de memoria
Get-Process |
Sort-Object WorkingSet -Descending |
Select-Object -First 10 Name, Id, WorkingSet
# De más reciente a más antiguo por fecha de modificación
Get-ChildItem -File |
Sort-Object LastWriteTime -Descending |
Select-Object -First 20 Name, LastWriteTime
Select-Object — elegir solo las columnas necesarias
Get-Process |
Select-Object Name, Id, CPU, WorkingSet
También se usa para limitar la cantidad de resultados.
Get-Process | Select-Object -First 5
Get-Process | Select-Object -Last 5
Antes de exportar a CSV, resulta más manejable dejar organizadas solo las columnas necesarias con Select-Object.
Get-Service |
Select-Object Name, DisplayName, Status, StartType |
Export-Csv .\services.csv -NoTypeInformation -Encoding UTF8
7. Los comandos Format se usan al final
PowerShell dispone de comandos para dar formato a la presentación.
Format-Table
Format-List
Format-Wide
Estos comandos son «para la presentación en pantalla». Si se usan antes de exportar a CSV o antes de pasar el resultado a un proceso posterior, el objeto deja de tener las propiedades originales y pasa a ser un objeto de formato de presentación.
# Evitar: la información de formato se mezcla en el CSV
Get-Process |
Format-Table Name, CPU |
Export-Csv .\process.csv -NoTypeInformation
# Correcto: elegir antes las propiedades y luego exportar a CSV
Get-Process |
Select-Object Name, CPU |
Export-Csv .\process.csv -NoTypeInformation -Encoding UTF8
Recordar que
Format-*es «para mostrar en pantalla al final» reduce los accidentes.
8. Fundamentos del manejo de archivos y carpetas
Lo que más se usa en PowerShell es el manejo de archivos y carpetas.
| Qué se quiere hacer | Comando |
|---|---|
| Ver la ubicación actual | Get-Location |
| Cambiar de ubicación | Set-Location |
| Ver el listado | Get-ChildItem |
| Crear un archivo o una carpeta | New-Item |
| Copiar | Copy-Item |
| Mover | Move-Item |
| Cambiar el nombre | Rename-Item |
| Eliminar | Remove-Item |
| Comprobar si existe | Test-Path |
Comprobar la ubicación actual
Get-Location
Nos movemos.
Set-Location C:\Work
Si la ruta contiene espacios, enciérrela entre comillas.
Set-Location "C:\Work Files\Reports"
Ver el listado de archivos
Get-ChildItem
Get-ChildItem -File
Get-ChildItem -Directory
Get-ChildItem -Recurse
Al ejecutarlo, debajo del encabezado de la carpeta correspondiente aparece una tabla con las columnas siguientes.
Directory: C:\Work
Mode LastWriteTime Length Name
---- ------------- ------ ----
Mode es una serie de letras que representan atributos: d es directorio, a es atributo de archivo, r es solo lectura, h es oculto, s es sistema y l es vínculo. En las filas de directorio, Length (tamaño) queda en blanco. Los valores LastWriteTime y Length que aparecen aquí son, tal cual, los nombres de propiedad que se pueden usar en Where-Object o Sort-Object.
Para filtrar por extensión, -Filter resulta muy útil.
Get-ChildItem -Filter *.log
Para buscar incluyendo las subcarpetas, use -Recurse.
Get-ChildItem C:\Logs -Filter *.log -Recurse
En ubicaciones con una gran cantidad de archivos, no use -Recurse de inmediato: primero limite la carpeta de destino y luego ejecute.
Crear
# Crear una carpeta
New-Item -ItemType Directory -Path .\archive
# Crear un archivo vacío
New-Item -ItemType File -Path .\memo.txt
Si es posible que ya exista, compruébelo primero.
if (-not (Test-Path .\archive)) {
New-Item -ItemType Directory -Path .\archive
}
Copiar
Copy-Item .\report.xlsx .\backup\report.xlsx
Para copiar una carpeta entera, use -Recurse.
Copy-Item .\data .\backup\data -Recurse
Cuando pueda haber sobrescritura, compruebe antes el efecto.
Copy-Item .\data .\backup\data -Recurse -WhatIf
Mover
Move-Item .\old.log .\archive\old.log
Al mover varios archivos según una condición, añada primero -WhatIf.
Get-ChildItem .\logs -Filter *.log |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Move-Item -Destination .\archive -WhatIf
Si todo está bien, quite -WhatIf.
Get-ChildItem .\logs -Filter *.log |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Move-Item -Destination .\archive
Eliminar
La eliminación requiere un cuidado especial.
Remove-Item .\old.log -WhatIf
Confirme que el objetivo es correcto antes de ejecutar.
Remove-Item .\old.log
La combinación de comodines y -Recurse es muy potente. No la ejecute directamente en la carpeta de producción: primero confírmelo mostrando el listado.
# Primero confirmar el objetivo
Get-ChildItem C:\Logs -Filter *.tmp -Recurse |
Select-Object FullName, Length, LastWriteTime
# A continuación confirmar la eliminación prevista
Get-ChildItem C:\Logs -Filter *.tmp -Recurse |
Remove-Item -WhatIf
# Por último, ejecutar
Get-ChildItem C:\Logs -Filter *.tmp -Recurse |
Remove-Item
9. Leer, buscar y escribir archivos de texto
En la investigación de registros se usa con frecuencia el manejo de archivos de texto.
Leer un archivo
Get-Content .\app.log
Para ver solo el final, use -Tail.
Get-Content .\app.log -Tail 50
Para observar un registro al que se le van añadiendo líneas, use -Wait.
Get-Content .\app.log -Tail 20 -Wait
Buscar una cadena de texto
Select-String -Path .\app.log -Pattern "ERROR"
Se pueden abarcar varios archivos a la vez.
Select-String -Path .\logs\*.log -Pattern "ERROR", "WARN"
También se puede extraer del resultado de la búsqueda el nombre de archivo, el número de línea y el contenido, y volcarlo a un CSV.
Select-String -Path .\logs\*.log -Pattern "ERROR" |
Select-Object Path, LineNumber, Line |
Export-Csv .\error-lines.csv -NoTypeInformation -Encoding UTF8
Escribir en un archivo
"hello" | Set-Content .\memo.txt -Encoding UTF8
"next line" | Add-Content .\memo.txt -Encoding UTF8
Al guardar el resultado de un comando, Export-Csv o ConvertTo-Json a veces resultan más fáciles de manejar después que Out-File.
Get-Process |
Select-Object Name, Id, CPU |
Export-Csv .\process.csv -NoTypeInformation -Encoding UTF8
10. Fundamentos del manejo de CSV
En la automatización de tareas de oficina, el CSV aparece con muchísima frecuencia. En PowerShell, el CSV se puede tratar no como texto, sino como «objetos con columnas».
Leer un CSV
Supongamos, por ejemplo, que existe el siguiente archivo users.csv.
Name,Department,Enabled
Suzuki,Sales,true
Tanaka,Accounting,false
Sato,Sales,true
Lo leemos.
$users = Import-Csv .\users.csv
$users
Los nombres de las columnas se convierten en propiedades.
$users | Select-Object Name, Department
Filtrar por condición
$users |
Where-Object { $_.Department -eq "Sales" }
Como los valores del CSV suelen entrar como cadenas de texto, tenga cuidado al trabajar con true/false o con números.
$users |
Where-Object { $_.Enabled -eq "true" }
Exportar a CSV
$users |
Where-Object { $_.Department -eq "Sales" } |
Export-Csv .\sales-users.csv -NoTypeInformation -Encoding UTF8
La regla de oro es no colocar Format-Table antes de Export-Csv.
# Evitar
$users | Format-Table | Export-Csv .\out.csv -NoTypeInformation
# Correcto
$users | Select-Object Name, Department, Enabled | Export-Csv .\out.csv -NoTypeInformation -Encoding UTF8
11. Fundamentos del manejo de JSON
En archivos de configuración y en API web también se usa mucho JSON.
$data = Get-Content .\settings.json -Raw | ConvertFrom-Json
$data
Convertimos un objeto a JSON.
[pscustomobject]@{
Name = "BatchJob"
Enabled = $true
Retry = 3
} | ConvertTo-Json
Cuando hay una jerarquía profunda, indique -Depth.
$config | ConvertTo-Json -Depth 10 | Set-Content .\settings.json -Encoding UTF8
12. Fundamentos de procesos y servicios
Consultar procesos
Get-Process
Comprobamos los procesos con mayor uso de memoria.
Get-Process |
Sort-Object WorkingSet -Descending |
Select-Object -First 10 Name, Id, WorkingSet
Filtramos por nombre.
Get-Process -Name notepad
La detención se realiza con cuidado.
Stop-Process -Name notepad -WhatIf
Si todo está bien, se ejecuta.
Stop-Process -Name notepad
Consultar servicios
Get-Service
Este es el caso de ver solo los servicios detenidos.
Get-Service |
Where-Object { $_.Status -eq "Stopped" }
Filtramos por nombre.
Get-Service -Name "Spooler"
Al reiniciar un servicio, también se confirma primero el objetivo.
Get-Service -Name "Spooler"
Restart-Service -Name "Spooler" -WhatIf
Como las operaciones sobre servicios suelen afectar al negocio, en entornos de producción conviene confirmar antes el procedimiento documentado, la ventana de mantenimiento y el método de recuperación.
13. Fundamentos del registro de eventos
En la investigación de Windows, el registro de eventos es imprescindible.
Comprobamos los errores recientes.
Get-WinEvent -LogName System -MaxEvents 100 |
Where-Object { $_.LevelDisplayName -eq "Error" } |
Select-Object TimeCreated, ProviderName, Id, Message
Cómo se muestra la salida
Por defecto, el resultado de Get-WinEvent incluye un encabezado por cada origen del registro (ProviderName), y debajo aparece una tabla con las columnas siguientes.
ProviderName: PowerShell
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
Como en el ejemplo anterior, si elige columnas con Select-Object, no aparece ese encabezado del origen y la tabla contiene solo las columnas elegidas. Como Message es largo, en pantalla queda cortado. Si quiere leer un evento completo, limite el resultado a un solo elemento y muéstrelo con Format-List.
Get-WinEvent -LogName System -MaxEvents 1 | Format-List *
En LevelDisplayName aparecen los valores Critical, Error, Warning, Information y Verbose. En registros con muchos eventos, resulta más rápido pasar la condición a -FilterHashtable que filtrar con Where-Object (Level = 3 corresponde a advertencia y Level = 4 a información).
Consultamos los 50 eventos más recientes del registro de aplicación.
Get-WinEvent -LogName Application -MaxEvents 50 |
Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message
Este es el caso de filtrar por un período.
$start = (Get-Date).AddHours(-24)
Get-WinEvent -FilterHashtable @{
LogName = "System"
StartTime = $start
} |
Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message
Dejar constancia del resultado de la investigación en un CSV facilita compartirlo más adelante.
Get-WinEvent -LogName System -MaxEvents 500 |
Where-Object { $_.LevelDisplayName -in @("Error", "Warning") } |
Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message |
Export-Csv .\system-events.csv -NoTypeInformation -Encoding UTF8
14. Variables, arreglos y tablas hash
Se puede trabajar solo con comandos cortos, pero en cuanto se alargan un poco, las variables resultan útiles.
Variables
$path = "C:\Logs"
Get-ChildItem $path
Las variables de PowerShell empiezan con $.
$today = Get-Date
$limit = (Get-Date).AddDays(-30)
Arreglos
$extensions = @("*.log", "*.txt", "*.csv")
foreach ($ext in $extensions) {
Get-ChildItem C:\Work -Filter $ext
}
Tablas hash
Una tabla hash es una combinación de claves y valores.
$params = @{
Path = "C:\Logs"
Filter = "*.log"
Recurse = $true
}
Get-ChildItem @params
A esta forma de escribir, como @params, se la llama «splatting». Resulta más legible cuando aumenta el número de parámetros.
15. Organizar resultados con PSCustomObject
Cuando se quiere reunir el resultado de una investigación en formato de tabla, [pscustomobject] resulta muy útil.
[pscustomobject]@{
ComputerName = $env:COMPUTERNAME
UserName = $env:USERNAME
CheckedAt = Get-Date
}
Se pueden generar varios resultados y volcarlos a un CSV.
Get-ChildItem C:\Logs -Filter *.log |
ForEach-Object {
[pscustomobject]@{
Name = $_.Name
FullName = $_.FullName
SizeMB = [math]::Round($_.Length / 1MB, 2)
LastWriteTime = $_.LastWriteTime
}
} |
Export-Csv .\log-files.csv -NoTypeInformation -Encoding UTF8
16. Fundamentos del archivo de script .ps1
Los procesos que se usan con frecuencia se convierten en archivos .ps1.
Como ejemplo, crearemos un script que liste los registros antiguos.
# Find-OldLogs.ps1
param(
[string]$Path = "C:\Logs",
[int]$Days = 30,
[string]$OutputPath = ".\old-logs.csv"
)
$limit = (Get-Date).AddDays(-$Days)
Get-ChildItem -Path $Path -Filter *.log -File -Recurse |
Where-Object { $_.LastWriteTime -lt $limit } |
Select-Object FullName, Length, LastWriteTime |
Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Exported: $OutputPath"
Este es un ejemplo de ejecución.
.\Find-OldLogs.ps1 -Path C:\Logs -Days 60 -OutputPath .\old-logs.csv
Al recibir los argumentos mediante param(), resulta más fácil cambiar las condiciones más adelante.
17. Fundamentos de la directiva de ejecución
Al intentar ejecutar un .ps1, a veces aparece un error como el siguiente.
La ejecución de scripts está deshabilitada en este sistema...
En ese caso, compruebe la directiva de ejecución actual.
Get-ExecutionPolicy
Get-ExecutionPolicy -List
Si solo quiere ejecutar en su equipo de desarrollo personal scripts creados localmente, lo habitual es establecer RemoteSigned en el ámbito del usuario actual.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Sin embargo, en los equipos de la empresa a veces está controlado por la directiva de grupo. No intente evitarlo por la fuerza: consulte con el administrador o revise las normas de operación.
La directiva de ejecución es una función de seguridad que controla las condiciones bajo las que se ejecutan los scripts de PowerShell. Sin embargo, no constituye un límite de seguridad completo. En una organización hay que considerarla combinada con la firma de código, AppLocker, Microsoft Defender, la gestión de permisos y la auditoría de registros.
18. Fundamentos del manejo de errores
PowerShell, en algunos casos, sigue ejecutando el proceso aunque se produzca un error.
En procesos importantes, use -ErrorAction Stop junto con try/catch.
try {
Copy-Item .\source.txt .\backup\source.txt -ErrorAction Stop
Write-Host "Copy succeeded"
}
catch {
Write-Error "Copy failed: $($_.Exception.Message)"
}
Al procesar varios archivos, registrar los fallos permite hacerles seguimiento más adelante.
$results = foreach ($file in Get-ChildItem .\input -File) {
try {
Copy-Item $file.FullName .\backup -ErrorAction Stop
[pscustomobject]@{
FileName = $file.Name
Status = "OK"
Message = ""
}
}
catch {
[pscustomobject]@{
FileName = $file.Name
Status = "NG"
Message = $_.Exception.Message
}
}
}
$results | Export-Csv .\copy-result.csv -NoTypeInformation -Encoding UTF8
19. Orden para ejecutar con seguridad los comandos de modificación
En PowerShell es importante seguir un procedimiento para manejar con seguridad los comandos de modificación, como eliminar, mover o detener.
El orden básico es el siguiente.
1. Ver el objetivo con un comando Get-
2. Filtrar con Where-Object
3. Confirmar la lista de objetivos con Select-Object
4. Registrar con Export-Csv
5. Confirmar el cambio previsto con -WhatIf
6. Ejecutar de verdad
Como ejemplo, veamos el caso de eliminar archivos .tmp de más de 30 días de antigüedad.
Paso 1: ver el objetivo
Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse
Paso 2: filtrar por condición
$limit = (Get-Date).AddDays(-30)
Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse |
Where-Object { $_.LastWriteTime -lt $limit }
Paso 3: mostrar solo las columnas necesarias
$targets = Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse |
Where-Object { $_.LastWriteTime -lt $limit }
$targets |
Select-Object FullName, Length, LastWriteTime
Paso 4: dejar constancia
$targets |
Select-Object FullName, Length, LastWriteTime |
Export-Csv .\delete-targets.csv -NoTypeInformation -Encoding UTF8
Paso 5: confirmar con WhatIf
$targets | Remove-Item -WhatIf
Paso 6: ejecutar
$targets | Remove-Item
Siguiendo este orden se reducen los accidentes del tipo «no sé qué se eliminó».
20. Lista de comandos básicos de uso frecuente
Ubicación y archivos
| Objetivo | Ejemplo de comando |
|---|---|
| Mostrar la carpeta actual | Get-Location |
| Cambiar de carpeta | Set-Location C:\Work |
| Mostrar el listado | Get-ChildItem |
| Mostrar solo archivos | Get-ChildItem -File |
| Mostrar solo carpetas | Get-ChildItem -Directory |
| Buscar también en subcarpetas | Get-ChildItem -Recurse |
| Comprobar si existe | Test-Path .\file.txt |
| Crear una carpeta | New-Item -ItemType Directory .\backup |
| Copiar | Copy-Item .\a.txt .\backup\a.txt |
| Mover | Move-Item .\a.txt .\archive\a.txt |
| Confirmar la eliminación prevista | Remove-Item .\a.txt -WhatIf |
Procesamiento de objetos
| Objetivo | Ejemplo de comando |
|---|---|
| Filtrar por condición | Where-Object { $_.Status -eq "Running" } |
| Ordenar | Sort-Object LastWriteTime -Descending |
| Elegir columnas | Select-Object Name, LastWriteTime |
| Mostrar solo los primeros | Select-Object -First 10 |
| Procesar cada elemento | ForEach-Object { $_.Name } |
| Examinar el contenido | Get-Member |
Entrada y salida
| Objetivo | Ejemplo de comando |
|---|---|
| Leer texto | Get-Content .\app.log |
| Ver el final | Get-Content .\app.log -Tail 50 |
| Buscar una cadena de texto | Select-String -Path .\app.log -Pattern "ERROR" |
| Leer un CSV | Import-Csv .\users.csv |
| Exportar a CSV | Export-Csv .\out.csv -NoTypeInformation -Encoding UTF8 |
| Leer JSON | Get-Content .\a.json -Raw | ConvertFrom-Json |
| Exportar a JSON | $obj | ConvertTo-Json -Depth 10 |
Investigación de Windows
| Objetivo | Ejemplo de comando |
|---|---|
| Listado de procesos | Get-Process |
| Listado de servicios | Get-Service |
| Registro de eventos | Get-WinEvent -LogName System -MaxEvents 100 |
| Variables de entorno | Get-ChildItem Env: |
| Versión de PowerShell | $PSVersionTable |
| Directiva de ejecución | Get-ExecutionPolicy -List |
21. Ejemplo práctico: investigar registros y crear un informe
Consideremos el siguiente requisito.
Quiero buscar, entre los archivos .log dentro de C:\App\Logs, las líneas de los archivos actualizados en los últimos 7 días que contengan ERROR, y reunirlas en un CSV.
En lugar de escribir directamente la versión final, la construiremos por etapas.
Paso 1: buscar los archivos de registro
Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse
Paso 2: filtrar por los últimos 7 días
$since = (Get-Date).AddDays(-7)
Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse |
Where-Object { $_.LastWriteTime -ge $since }
Paso 3: buscar ERROR
$since = (Get-Date).AddDays(-7)
Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse |
Where-Object { $_.LastWriteTime -ge $since } |
Select-String -Pattern "ERROR"
Paso 4: exportar a CSV
$since = (Get-Date).AddDays(-7)
Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse |
Where-Object { $_.LastWriteTime -ge $since } |
Select-String -Pattern "ERROR" |
Select-Object Path, LineNumber, Line |
Export-Csv .\error-report.csv -NoTypeInformation -Encoding UTF8
Paso 5: convertirlo en script
# Export-ErrorReport.ps1
param(
[string]$LogPath = "C:\App\Logs",
[int]$Days = 7,
[string]$Pattern = "ERROR",
[string]$OutputPath = ".\error-report.csv"
)
$since = (Get-Date).AddDays(-$Days)
Get-ChildItem $LogPath -Filter *.log -File -Recurse |
Where-Object { $_.LastWriteTime -ge $since } |
Select-String -Pattern $Pattern |
Select-Object Path, LineNumber, Line |
Export-Csv $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Exported: $OutputPath"
Este es un ejemplo de ejecución.
.\Export-ErrorReport.ps1 -LogPath C:\App\Logs -Days 14 -Pattern "ERROR|FATAL" -OutputPath .\errors.csv
22. Ejemplo práctico: archivar archivos antiguos
El siguiente es un ejemplo en el que, en lugar de eliminar, se mueve.
Mover a C:\Work\Archive los archivos .xlsx de C:\Work\Reports que no se han actualizado en 90 días o más.
Confirmar el objetivo
$source = "C:\Work\Reports"
$dest = "C:\Work\Archive"
$limit = (Get-Date).AddDays(-90)
$targets = Get-ChildItem $source -Filter *.xlsx -File |
Where-Object { $_.LastWriteTime -lt $limit }
$targets | Select-Object FullName, Length, LastWriteTime
Crear el destino de archivado
if (-not (Test-Path $dest)) {
New-Item -ItemType Directory -Path $dest
}
Exportar constancia
$targets |
Select-Object FullName, Length, LastWriteTime |
Export-Csv .\archive-targets.csv -NoTypeInformation -Encoding UTF8
Confirmar con WhatIf
$targets | Move-Item -Destination $dest -WhatIf
Ejecutar
$targets | Move-Item -Destination $dest
Si es posible que haya nombres de archivo duplicados, tal como está fallará. En el trabajo real conviene decidir de antemano reglas como separar por carpetas de año y mes, añadir la fecha y hora al nombre del archivo de destino, u omitir el archivo si ya existe uno con ese nombre.
23. Problemas habituales
| Síntoma | Causa | Solución |
|---|---|---|
| Los comandos son largos y no se recuerdan | Se está intentando memorizarlos | Buscarlos con Get-Command y Get-Help -Examples |
No se entiende $_ |
Falta de comprensión del valor actual de la canalización | Aprenderlo con la forma Where-Object { $_.Name -like "*log*" } |
| El CSV queda con contenido extraño | Se usa Export-Csv después de Format-Table |
Usar Export-Csv después de Select-Object |
| Falla cuando la ruta tiene espacios | Faltan las comillas | Encerrarla así: "C:\Work Files\a.txt" |
| No se puede ejecutar el script | Directiva de ejecución | Comprobarla con Get-ExecutionPolicy -List |
| Hay demasiados objetivos para eliminar | La condición es demasiado amplia | Confirmar antes con Select-Object FullName y -WhatIf |
| No se conoce el nombre de la propiedad | No se ha examinado la estructura del objeto | Usar Get-Member |
| Aparecen caracteres corruptos | La codificación asumida no coincide | Comprobar el -Encoding de entrada y salida y la aplicación usada |
24. Orden para aprender los comandos
No hace falta aprender todos los comandos desde el principio.
Se recomienda el siguiente orden.
Primera etapa: ver y buscar
Get-Command
Get-Help
Get-Member
Get-Location
Set-Location
Get-ChildItem
Get-Content
Select-String
Segunda etapa: filtrar y organizar
Where-Object
Sort-Object
Select-Object
ForEach-Object
Format-Table
Format-List
Tercera etapa: entrada y salida
Import-Csv
Export-Csv
ConvertFrom-Json
ConvertTo-Json
Set-Content
Add-Content
Out-File
Cuarta etapa: modificar
New-Item
Copy-Item
Move-Item
Rename-Item
Remove-Item
Start-Service
Stop-Service
Restart-Service
Stop-Process
Los comandos de modificación siempre se aprenden en conjunto con los de comprobación.
# Ver
Get-ChildItem .\logs -Filter *.tmp
# Ver la eliminación prevista
Get-ChildItem .\logs -Filter *.tmp | Remove-Item -WhatIf
# Ejecutar
Get-ChildItem .\logs -Filter *.tmp | Remove-Item
25. Lista de verificación para la operación en el trabajo diario
Al usar PowerShell en el trabajo, conviene comprobar los siguientes puntos para mayor seguridad.
- Confirmé si el PowerShell que se va a ejecutar es
powershell.exeopwsh.exe - Confirmé la versión con
$PSVersionTable - Mostré el objetivo con un comando
Get-*antes de modificar - Confirmé en pantalla la condición de
Where-Object - Usé
-WhatIfantes de eliminar, mover o detener - Guardé en un CSV el listado de objetivos antes de ejecutar
- En entornos de producción confirmé la copia de seguridad o el procedimiento de recuperación
- Confirmé la directiva de ejecución del script y las normas internas
- Preparé la salida de registro para el caso de error
- En los scripts que se comparten, usé el nombre oficial del comando en lugar del alias
26. Resumen
Lo básico de PowerShell no consiste en memorizar una gran cantidad de comandos. Lo que realmente sirve en el trabajo diario es adquirir el siguiente patrón.
Buscar con Get-Command
Consultar el uso con Get-Help
Comprobar las propiedades con Get-Member
Ver el objetivo con Get-*
Filtrar con Where-Object
Organizar las columnas con Select-Object
Dejar constancia con Export-Csv
Confirmar el cambio previsto con -WhatIf
Ejecutar al final
En particular, PowerShell es un entorno muy potente que permite eliminar archivos, detener servicios, finalizar procesos e incluso manipular el registro de Windows. Precisamente por eso, lo primero que hay que aprender no son los «comandos peligrosos», sino el «procedimiento para confirmar el objetivo con seguridad».
ver → filtrar → registrar → ensayar → ejecutar
Si se respeta este orden, PowerShell deja de ser una simple pantalla negra y se convierte en una herramienta práctica para organizar, investigar y automatizar el trabajo con Windows.
Referencias
- Conjunto completo de código de ejemplo de este artículo (scripts por tema y pruebas Pester) - komurasoft-blog-samples (GitHub)
- Documentación de PowerShell - Microsoft Learn
- Instalar PowerShell 7 en Windows - Microsoft Learn
- Diferencias entre Windows PowerShell 5.1 y PowerShell 7.x - Microsoft Learn
- Get-Command - Microsoft Learn
- Get-Help - Microsoft Learn
- Get-Member - Microsoft Learn
- about_Pipelines - Microsoft Learn
- Where-Object - Microsoft Learn
- Select-Object - Microsoft Learn
- Get-Process - Microsoft Learn (incluye la explicación de las columnas mostradas por defecto)
- Get-WinEvent - Microsoft Learn
- about_Character_Encoding (Windows PowerShell 5.1) - Microsoft Learn
- Import-Csv - Microsoft Learn
- Export-Csv - Microsoft Learn
- about_Execution_Policies - Microsoft Learn
Artículos relacionados
- Recetario práctico de comandos de PowerShell — sume pequeñas funciones útiles para el trabajo diario
- Cómo entender la codificación de caracteres y los saltos de línea en Windows
- Guía de migración para prepararse ante la obsolescencia de VBScript
- Trampas del desarrollo con COM/OCX/ActiveX
Servicios relacionados con este tema
Desarrollo de aplicaciones para Windows
Brindamos apoyo en el desarrollo de software para Windows: aplicaciones de negocio, integración con equipos y herramientas de comunicación, entre otros.
Aprovechamiento de activos existentes y apoyo a la migración
Brindamos apoyo en el inventario, la mejora y la migración de activos existentes, incluyendo archivos por lotes antiguos, VBScript, VBA, PowerShell y COM/ActiveX.
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
Diseño de parámetros y modularización de scripts de PowerShell — de un «script que funciona» a un «script que se puede entregar»
Explicamos cómo llevar un script de PowerShell a una calidad entregable: param, [CmdletBinding()], validación, pipeline, -WhatIf, módulos...
Colección práctica de comandos de PowerShell ── Amplíe las pequeñas funciones que usa a diario
Repaso de comandos prácticos de PowerShell para el trabajo diario y cuándo usarlos: Measure-Object, Group-Object, Select-String, Compare-...
Cómo invocar COM y .NET desde PowerShell en la práctica ── ampliar de un salto el alcance de sus scripts
Cómo invocar clases .NET desde PowerShell, integrar C# y la API Win32 con Add-Type, operar COM, gestionar los procesos residuales de Exce...
Diferencias entre Windows PowerShell 5.1 y PowerShell 7 ── Guía práctica de migración de scripts internos
Explicamos la relación entre Windows PowerShell 5.1 y PowerShell 7 (coexistencia y pwsh.exe), la política oficial de no añadir funciones ...
Automatizar el procesamiento de Excel y CSV con PowerShell — recetas prácticas de agregación, cotejo y generación de informes
Recetas prácticas para automatizar con PowerShell la agregación y el cotejo de CSV y la generación de informes en Excel: codificación, Gr...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Servicios relacionados con este tema
El artículo está directamente relacionado con los siguientes servicios.
Desarrollo de aplicaciones para Windows
Aplicaciones empresariales, integración de dispositivos y herramientas de comunicación, de los requisitos al desarrollo.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿Por dónde debería empezar a aprender PowerShell?
- Lo importante no es memorizar comandos, sino aprender a buscarlos. Hay tres puntos clave: poder buscar comandos con Get-Command y Get-Help, poder pasar resultados a la siguiente instrucción mediante la canalización (|), y confirmar el efecto con -WhatIf o -Confirm antes de eliminar, detener o modificar algo. Cuando no se conoce el nombre de una propiedad de un objeto, se revisa su contenido con Get-Member. El flujo básico es «buscar → ver → filtrar → ordenar → mostrar → modificar si es necesario».
- ¿En qué se diferencian PowerShell y el símbolo del sistema?
- La diferencia más importante es que la salida del símbolo del sistema se centra en cadenas de texto, mientras que la salida de PowerShell son objetos. Por ejemplo, un archivo se trata como un objeto con propiedades como Name, Length y LastWriteTime, por lo que resulta natural filtrar con Where-Object u ordenar con Sort-Object. Además, los nombres de los comandos siguen de manera uniforme el formato Verbo-Sustantivo, como Get-ChildItem o Copy-Item, lo que facilita encontrar comandos que aún no se conocen.
- ¿Debería usar Windows PowerShell 5.1 o PowerShell 7.x?
- Si está empezando a aprender, puede tomar PowerShell 7.x (pwsh.exe) como referencia sin problema. Sin embargo, los scripts de administración antiguos de la empresa o los módulos específicos de Windows a veces están hechos partiendo de Windows PowerShell 5.1 (powershell.exe). En el trabajo diario conviene comprobar primero la versión con $PSVersionTable, y en los scripts que deban ejecutarse con una versión mínima determinada, escribir al principio una condición como #Requires -Version 7.0 para reducir los accidentes.
- ¿Cómo se pueden ejecutar de forma segura las eliminaciones o modificaciones en PowerShell?
- Respetar el orden «ver → filtrar → registrar → ensayar → ejecutar». En concreto, primero se muestra el objetivo con un comando Get-, se filtra con Where-Object, se confirma la lista de objetivos con Select-Object, se deja constancia en un CSV con Export-Csv, se comprueba el cambio previsto con -WhatIf y solo entonces se ejecuta de verdad. Como PowerShell permite investigar y modificar desde la misma pantalla, es importante adquirir el hábito de empezar siempre por una comprobación de solo lectura.
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.