PowerShell Scripting#
Habilitar ejecución de scripts#
# en una consola de administrador; para cambiarla solo para el usuario actual:
# Set-ExecutionPolicy -Scope CurrentUser Unrestricted
Set-ExecutionPolicy Unrestricted
Tipos y colecciones#
En PowerShell no es necesario declarar explícitamente el tipo de una variable: PowerShell infiere el tipo automáticamente al asignar un valor. No obstante, si necesitas definir explícitamente el tipo de una variable, puedes hacerlo usando la notación [tipo].
PS C:\> $number = 42
PS C:\> echo $number.GetType()
IsPublic IsSerial Name
-------- -------- ----
True True Int32
PS C:\> [int]$a = 4
PS C:\> $a.GetType().Name
Int32
Podemos convertir los diferentes tipos por ejemplo:
PS C:\> $a=4
PS C:\> $cadena=$a.ToString()
PS C:\> $cadena.GetType().Name
String
#TryParse nos devuelve si es posible
PS C:\> [int]$b
PS C:\> [int]::TryParse($cadena, [ref]$b)
True
PS C:\> echo $b
4
PS C:\> [int]::TryParse("2w", [ref]$b)
False
PS C:\> echo $b
0
#Podemos hacerlo directamente, pero si hay algún error se parará el script
PS C:\> $b = [int]::Parse($cadena)
PS C:\> echo $b
4
Los tipos de variables más comunes que puedes encontrar y utilizar en PowerShell son:
Números enteros (Integers). Un entero es un número sin parte decimal
PS C:\> [int]$a = 4 PS C:\> $a.GetType().Name Int32
Números de coma flotante (Floating-Point). Los números de coma flotante son números que incluyen una parte decimal
PS C:\> $number = 3.14 PS C:\> echo $number.GetType().Name Double
Cadenas de texto (Strings). Una cadena es una secuencia de caracteres utilizada para representar texto.
PS C:\> $cadena = "Hola, Mundo!" PS C:\> echo $cadena.GetType().Name String
PS C:\> $a="cadena" PS C:\> echo $a cadena PS C:\> echo $a.Replace("ca","CC") CCdena PS C:\> echo $a.split("a") c den PS C:\> echo $a.Split("a")[0] c PS C:\> echo $a cadena PS C:\> echo $a.Substring(2,4) dena PS C:\> echo $a.Remove(2,4) ca PS C:\> echo $a.Contains("a") True PS C:\> echo $a.IndexOf("a") 1 PS C:\> echo $a.Replace("ca","Ca") Cadena PS C:\> echo $a.Equals("cadena") True PS C:\> echo $a.Length 6 PS C:\> echo $a.Contains("an") False PS C:\> echo $a.ToLower() cadena PS C:\> echo $a.ToUpper() CADENA PS C:\> $a.Replace("ca"," ").Replace("na"," ") de #Trim() elimina espacios al final y al principio PS C:\> $a.Replace("ca"," ").Replace("na"," ").Trim() de
Format: Formatea una cadena usando marcadores de posición {n} que se reemplazan por los valores proporcionados.
PS C:\> $nombre = "Tutankamón" PS C:\> $edad = 3394 PS C:\> $cadena = [string]::Format("Nombre: {0}, Edad: {1}", $nombre, $edad)
Redondeo a Dos Decimales
PS C:\> $pi = 3.141592 PS C:\> $out = "pi : {0:F2}" -f $pi PS C:\> Write-Output $out pi : 3,14
Formato de Moneda
PS C:\> $pi = 3.141592 PS C:\> $out = "pi : {0:C2}" -f $pi PS C:\> Write-Output $out pi : 3,14 €Formato Científico
PS C:\> $pi = 3.141592 PS C:\> $out = "pi : {0:E2}" -f $pi PS C:\> Write-Output $out pi : 3,14E+000
Formato de Porcentaje
PS C:\> $pi = 3.141592 PS C:\> $out = "pi : {0:P2}" -f $pi PS C:\> Write-Output $out pi : 314,16 %
Formato de Longitud Fija (Relleno a la Izquierda)
PS C:\> $pi = 3.141592 PS C:\> $out = "pi : {0,10}" -f $pi PS C:\> Write-Output $out pi : 3,141592
Formato de Longitud Fija (Relleno a la Derecha)
PS C:\> $pi = 3.141592 PS C:\> $out = "pi : {0,-10}" -f $pi PS C:\> Write-Output $out pi : 3,141592
Formato con Cero Padding (Relleno con ceros)
PS C:\> $pi = 3.141592 PS C:\> $out = "pi : {0:000.000}" -f $pi PS C:\> Write-Output $out pi : 003,142 PS C:\> $out = "pi : {0:00}" -f $pi PS C:\> Write-Output $out pi : 03
Booleanos (Booleans). Un valor booleano puede ser True o False.
PS C:\> $B=$true PS C:\> echo $B.GetType().Name Boolean
Arreglos (Arrays). Un arreglo es una colección de elementos, que pueden ser de cualquier tipo de dato. Los elementos están indexados comenzando desde cero.
PS C:\> $A = @(0)*4 PS C:\> echo $A 0 0 0 0 PS C:\> $numbers = @(1, 2, 3, 4, 5) PS C:\> echo $numbers.GetType().Name Object[] PS C:\> echo $numbers[0] 1 PS C:\> echo $numbers[0].GetType().Name Int32 PS C:\> echo $numbers.Length 5 PS C:\> echo $numbers.Count 5 echo $numbers.Contains(2) True #podemos añadir nuevos elementos PS C:\> $numbers+="hola" PS C:\> echo $numbers 1 2 3 4 5 hola PS C:\> echo $numbers.Length 6
Arrays multidimensionales
PS C:\> $XY = @(@(1, 2), @(3, 4)) PS C:\> echo $XY.Length 2 PS C:\> echo $XY[0] 1 2 PS C:\> echo $XY[0].Length 2 PS C:\> echo $XY[0][0] 1
Tablas hash (Hash Tables). Una tabla hash es una colección de pares clave-valor. Las claves deben ser únicas dentro de la tabla.
PS C:\> $person = @{ Name = "Tutankamón" Age = 3358 City = "Egipto" } PS C:\> echo $person.Name Tutankamón #Agregar una nueva clave PS C:\> $person.Estado = "momificado" PS C:\> $person Name Value ---- ----- Estado momificado Name Tutankamón Age 3358 City Egipto #eliminar la clave PS C:\> $person.Remove("Age") PS C:\> $person Name Value ---- ----- Estado momificado Name Tutankamón City Egipto
ScriptBlocks. Un ScriptBlock es un bloque de código que se puede ejecutar más tarde o pasar como un parámetro.
PS C:\> $script = { param($name) "Hola, $name!" } PS C:\> & $script "Tutankamón" Hola, Tutankamón!
Operadores#
PS C:\> $a=11
PS C:\> $b=5
PS C:\> $c=$a-$b # resta
PS C:\> echo $c
6
PS C:\> $c=$a+$b # suma
PS C:\> echo $c
16
PS C:\> $c=$a*$b # multiplicación
PS C:\> echo $c
55
PS C:\> $c=$a/$b # división
PS C:\> echo $c
2,2
PS C:\> $c=$a%$b # resto o módulo
PS C:\> echo $c
1
Operadores de asignación#
PS C:\> $a=6
PS C:\> $a+=2 ; echo $a # $a=$a+2
8
PS C:\> $a-=2 ; echo $a # $a=$a-2
6
PS C:\> $a++ ; echo $a # $a=$a+1
7
PS C:\> $a-- ; echo $a # $a=$a-1
6
PS C:\> $a/=2 ; echo $a # $a=$a/2
3
PS C:\> $a*=3 ; echo $a # $a=$a*3
9
PS C:\> $a%=3 ; echo $a # $a=$a%3
0
Números aleatorios
Get-Random # número aleatorio Get-Random -Minimum 1 -Maximum 10 "uno", "dos", "tres"| Get-Random
Otras conversiones
# pasar a binario $decimal = 10 [Convert]::ToString($decimal, 2) # 1010 # convertir un objeto a cadena poniéndolo entre comillas $a="$pwd" # Podemos ejecutar un texto como si fuese un comando con el operador & $a="notepad" &$a
Argumentos de entrada#
Argumentos de entrada Read-Host#
PS C:\> cat read_host.ps1
echo "Dame un entero"
$Name=Read-Host
echo "Sin pasar a int ($Name*$Name) = "
echo $($Name*$Name)
echo ""
$a = [int] $Name
echo "Pasando a int ($Name*$Name) = "
echo $($a*$a)
echo ""
PS C:\> .\read_host.ps1
Dame un entero
2
Sin pasar a int (2*2) =
22
Pasando a int (2*2) =
4
Argumentos de entrada con $args (argst.ps1)#
PS C:\> cat .\argst.ps1
echo "tenemos $($args.Count) parámetros de entrada: $args"
echo "El argumento 0: $($args[0])"
echo "El argumento 1: $($args[1])"
PS C:\> .\argst.ps1 1 2 3
tenemos 3 parámetros de entrada: 1 2 3
El argumento 0: 1
El argumento 1: 2
PS C:\> cat .\args.ps1
for($i=0;$i -lt $args.Length;$i++)
{
$salida = $args[$i]
Write-Output "i = $salida"
}
PS C:\> .\args.ps1 1 dos tres
i = 1
i = dos
i = tres
PS C:\> cat .\argsf.ps1
foreach ($i in $args)
{
echo $i
}
PS C:\> .\argsf.ps1 1 dos tres 1 dos tres
1
dos
tres
1
dos
tres
Argumentos de entrada con param (param.ps1)#
PS C:\> cat .\param.ps1
param (
[string]$Nombre,
[int]$Nacimiento
)
# Calcular la edad
$Edad = (Get-Date).Year - $Nacimiento
Write-Output "Hola $Nombre, Tienes $Edad"
PS C:\> .\param.ps1 -Nombre Tutankamón -Nacimiento -1334
Hola Tutankamón, Tienes 3358
PS C:\> .\param.ps1 -Nombre Tutankamón
Hola Tutankamón, Tienes 2024
PS C:\> .\param.ps1
Hola , Tienes 2024
En el caso de que queramos darle un valor por defecto cambiamos:
param (
[string]$Nombre = "Tutankamón",
[int]$Nacimiento = -1334
)
y cuando lo ejecutamos obtenemos:
PS C:\> .\param_def.ps1 -Nombre Nefertiti -Nacimiento -1370
Hola Nefertiti, Tienes 3394
PS C:\> .\param_def.ps1 -Nombre Nefertiti
Hola Nefertiti, Tienes 3358
PS C:\> .\param_def.ps1
Hola Tutankamón, Tienes 3358
Podemos completar la declaración de un parámetro en un script de PowerShell utilizando [Parameter()]: el argumento Mandatory=$true indica que el parámetro es obligatorio, de modo que si el usuario no proporciona un valor al ejecutar el script, PowerShell le pedirá que lo introduzca. HelpMessage especifica un mensaje de ayuda que el usuario puede consultar escribiendo !? cuando se le pide el valor.
PS C:\> cat .\param.ps1
param (
[Parameter(Mandatory=$true, HelpMessage="¿Cómo te llamas?")]
[string]$Nombre,
[Parameter(Mandatory=$true, HelpMessage="¿En qué año naciste?")]
[int]$Nacimiento
)
# Calcular la edad
$Edad = (Get-Date).Year - $Nacimiento
Write-Output "Hola $Nombre, Tienes $Edad"
#si no ponemos nada toma los valores en orden
PS C:\> .\param.ps1 Tutankamón -1334
Hola Tutankamón, Tienes 3358
PS C:\> .\param.ps1 -Nombre Tutankamón -Nacimiento -1334
Hola Tutankamón, Tienes 3358
PS C:\> .\param.ps1 -Nombre Tutankamón
cmdlet param.ps1 en la posición 1 de la canalización de comandos
Proporcione valores para los parámetros siguientes:
(Escriba !? para obtener Ayuda).
Nacimiento: -1334
Hola Tutankamón, Tienes 3358
PS C:\> .\param.ps1
cmdlet param.ps1 en la posición 1 de la canalización de comandos
Proporcione valores para los parámetros siguientes:
(Escriba !? para obtener Ayuda).
Nombre: !?
¿Cómo te llamas?
Nombre: Tutankamón
Nacimiento: -1334
Hola Tutankamón, Tienes 3358
Podemos usar param con un switch para crear parámetros booleanos/flags que activen o desactiven funciones
PS C:\> cat .\script.ps1
param (
[switch]$help
)
if ($help){
Write-Output "help es: $help"
}else{
Write-Output "help es: $help"
}
PS C:\> .\script.ps1
help es: False
PS C:\> .\script.ps1 -help
help es: True
Evaluación de condiciones#
7 -eq 7 #True (igual)
7 -eq 8 #False
7 -ne 8 #True (distinto)
3 -gt 2 #True (mayor que)
3 -ge 3 #True (mayor o igual)
2 -lt 3 #True (menor que)
3 -le 2 #False (menor o igual)
Operadores lógicos#
(5 -gt 1) -And (5 -lt 10) #True
(5 -gt 1) -Or (5 -lt 1) #True
(5 -gt 1) -Xor (5 -lt 1) #True
-Not (5 -lt 1) #True
Comparación de cadenas#
"hola" -eq "hola" #True
"hola" -eq "hoa" #False
"hola" -ne "hoa" #True
"hola" -ne "hola" #False
Estructuras condicionales con if#
$numero = -10
If ($numero -gt 0) {
echo "$numero es mayor que 0"
}
If ($numero -gt 0) {
echo "$numero es mayor que 0"
}else{
echo "$numero es negativo"
}
#-10 es negativo
Estructuras condicionales con switch#
$dia = "lunes"
switch ($dia) {
"lunes" { Write-Host "Hoy es lunes" }
"martes" { Write-Host "Hoy es martes" }
"miércoles" { Write-Host "Hoy es miércoles" }
"jueves" { Write-Host "Hoy es jueves" }
"viernes" { Write-Host "Hoy es viernes" }
default { Write-Host "No es un día válido" }
}
# Hoy es lunes
Bucles con for#
for ($i=0;$i -lt 5;$i++){
Write-Host $i
}
# 0
# 1
# 2
# 3
# 4
$A = @(1, 2, 3)
for ($i = 0; $i -lt $A.Length; $i++) {
Write-Host "Elemento $i : $($A[$i])"
}
# Elemento 0 : 1
# Elemento 1 : 2
# Elemento 2 : 3
Bucles con While#
$i = 0
While ($i -lt 5) {
echo $i
$i +=1
}
# 0
# 1
# 2
# 3
# 4
$i = 0
do {
echo $i
$i +=1
} While ($i -lt 5)
# 0
# 1
# 2
# 3
# 4
Bucles con Until#
$i = 0;
do {
Write-Host $i
$i +=1
}
until ($i -ge 5)
# 0
# 1
# 2
# 3
# 4
Bucles con Foreach#
$items = 0..4
$items | ForEach-Object {
$i="$_"
Write-Host $i
}
# 0
# 1
# 2
# 3
# 4
foreach ($num in 1,2,"hola",4,5) {
echo $num
}
# 1
# 2
# hola
# 4
# 5
Archivos#
echo "usuario,grupo" > usuarios.csv
echo "user01,group01" >> usuarios.csv
echo "user02,group02" >> usuarios.csv
echo "user03,group03" >> usuarios.csv
echo "user04,group04" >> usuarios.csv
Test-Path usuarios.csv #True ver si existe
#Leer el archivo linea a linea
foreach ($i in get-content usuarios.csv){
echo $i
}
#Podemos importarlo desde un csv
$A = Import-Csv -Path usuarios.csv
echo $A.usuario
#Podemos recorrer los valores
foreach ($i in $A){
$u=$i.usuario ; echo "usuario = $u"
}
#Eliminar la segunda linea y escribir
$lineas = Get-Content usuarios.csv
$lineas | Where-Object { $_ -ne $lineas[1] } | Set-Content usuarios.csv
cat ./usuarios.csv
usuario,grupo
user02,group02
user03,group03
user04,group04
#Eliminar la linea que tiene user03 y escribir
#(-UseQuotes requiere PowerShell 7; en Windows PowerShell 5.1 el csv se escribe entrecomillado)
$a = Import-Csv -Path usuarios.csv | Where-Object { $_.usuario -ne "user03" }
$a | Export-Csv -Path usuarios.csv -NoTypeInformation -UseQuotes Never
#Otra forma de eliminar la linea de user03 (partiendo del fichero original):
Get-Content usuarios.csv | Where-Object { $_ -notmatch 'user03' } | Set-Content usuarios.csv
cat .\usuarios.csv
usuario,grupo
user01,group01
user02,group02
user04,group04
#Podemos evaluar si existe o no el usuario
if (Import-Csv usuarios.csv | Where-Object usuario -eq "user02") {
$true
} else {
$false
}
True
Funciones#
function foo($a, $b, $c) {
"a: $a; b: $b; c: $c"
}
foo 1 3 5
# a: 1; b: 3; c: 5
function Sumar($a, $b) {
return $a + $b
}
$x = 5
$y = 7
$resultado = Sumar $x $y
Write-Host $resultado
#12
# ¡Ojo!: las funciones NO se llaman con paréntesis y comas.
# Sumar($x, $y) pasaría un único argumento (el array @(5, 7)) al parámetro $a
# y el resultado sería "5 7" en lugar de 12.
function Sumar {
return $args[0] + $args[1]
}
$resultado = Sumar 5 7
Write-Host $resultado
#12
function Sumar {
param(
[int]$a,
[int]$b
)
return $a + $b
}
$resultado = Sumar -a 5 -b 7
Write-Host $resultado
#12
$resultado = Sumar 5 7
Write-Host $resultado
#12
Ejemplo de función con recursión:
function Get-Factorial {
param (
[int]$n
)
if ($n -le 1) {
return 1
} else {
return $n * (Get-Factorial -n ($n - 1))
}
}
$number = 5
$result = Get-Factorial -n 5
Write-Output "El factorial de $number es $result"
#El factorial de 5 es 120
Windows PowerShell ISE#
Windows PowerShell ISE es el entorno de scripting integrado que viene con Windows: editor con resaltado, ejecución por selección y consola integrada. Microsoft ya no lo desarrolla activamente (recomienda Visual Studio Code con la extensión de PowerShell), pero sigue incluido en Windows y es cómodo para empezar.