Fundamentos de los comandos de PowerShell — operaciones básicas y uso seguro

· Actualizado el: · · 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:

  1. Poder buscar comandos con Get-Command y Get-Help
  2. Poder pasar resultados a la siguiente instrucción mediante la canalización |
  3. Confirmar el efecto con -WhatIf o -Confirm antes 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.exe o pwsh.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é -WhatIf antes 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

Artículos relacionados

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.

Ver el servicio / Contacto

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.

Contacto

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

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.

Volver al blog