Búsqueda de sitios web

Conversión de intervalos de tiempo léxicos con PowerShell


He estado trabajando en algunos proyectos de scripting y los datos con los que estoy trabajando contienen intervalos de tiempo léxicos. ¿Que qué? Probablemente hayas visto estas cosas. Esta es una cadena como P0DT0H0M47S que representa un período de tiempo. No son difíciles de leer para los humanos. Este dice “0 días 0 horas 0 minutos 47 segundos”. El formato es cómo se almacenan los intervalos de tiempo o duraciones en archivos XML. Puede leer más sobre esto en https://www.w3.org/TR/xmlschema-2/#duration. Pero quiero ver estos valores como períodos de tiempo más compatibles con PowerShell. Resulta que esto es realmente muy fácil.

Un lugar donde puede ver estos valores es en las tareas programadas en Windows.

Sé que PT72H representa un lapso de tiempo de 72 horas. Convertir esto en un objeto de intervalo de tiempo es simple. Una vez que sepas cómo y en un segundo lo sabrás. Puede utilizar un método XML integrado para convertir el valor.

[System.Xml.XmlConvert]::ToTimeSpan("PT72H")

Y efectivamente, son 72 horas o 3 días. ¿Qué pasa con la conversión al revés?

$ts = New-TimeSpan -hours 72
[System.Xml.XmlConvert]::ToString($ts)

Es cierto que la cadena es diferente, pero aún representa 72 horas.

El proceso es simple, pero aun así escribí un par de funciones avanzadas de PowerShell para manejar estas tareas.

Function ConvertTo-LexicalTimespan {
    [cmdletbinding()]
    [OutputType("String")]
    Param(
        [Parameter(Position = 0, Mandatory,HelpMessage = "Enter a timespan object", ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [timespan]$Timespan
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"

    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Converting $Timespan"
        Try {
            [System.Xml.XmlConvert]::ToString($Timespan)
        }
        Catch {
            Throw $_
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close ConvertTo-LexicalTimespan

Function ConvertFrom-LexicalTimespan {
    [cmdletbinding()]
    [OutputType("string", "timespan")]
    Param(
        [Parameter(Position = 0, Mandatory, HelpMessage = "Enter a lexical time string like P23DT3H43M. This is case-sensitive.", ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [string]$String,
        [Parameter(HelpMessage = "Format the timespan as a string")]
        [switch]$AsString
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Converting $($String.toUpper()) to a timespan"
        Try {
            #convert string to upper case to help the user out
            $r = [System.Xml.XmlConvert]::ToTimeSpan($String.ToUpper())
            if ($AsString) {
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Displaying as a timespan string"
                $r.toString()
            }
            else {
                $r
            }
        }
        Catch {
            Throw $_
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close ConvertFrom-LexicalTimespan

Veamos cómo funcionan.

Aquí hay otro ejemplo crudo.

O puedo usar un código como este:

(get-scheduledtask dailywatcher).settings.idlesettings | 
select-object @{Name="Idle";Expression={ConvertFrom-LexicalTimespan $_.idleDuration}},
@{Name="Wait";Expression={ConvertFrom-LexicalTimespan $_.WaitTimeout}}

Ahora tengo resultados más significativos.

Aquí hay un ejemplo final que usa Get-WsManInstance para mostrar sesiones conectadas.

Pero creo que estará de acuerdo en que usar este código produce un mejor resultado.

Get-WSManInstance -ComputerName DOM1 -ResourceURI Shell -Enumerate | 
 Select-Object -Property ResourceURI,Owner,ClientIP,
 @{Name="ShellRunTime";Expression = {ConvertFrom-LexicalTimespan $_.ShellRunTime}},
 @{Name="ShellInactivity";Expression = {ConvertFrom-LexicalTimespan $_.ShellInactivity}},
 @{Name="IdleTimeOut";Expression = {ConvertFrom-LexicalTimespan $_.IdleTimeOut}},
 @{Name="MaxIdleTimeOut";Expression = {ConvertFrom-LexicalTimespan $_.MaxIdleTimeOut}},
 @{Name="Computername";Expression={"DOM1"}}

Este es el código que pondría en una función para crear resultados significativos para la canalización de PowerShell. O si quisiera algo más interactivo, podría usar mi módulo PSTypeExtensionTools y crear extensiones.

$t = "System.Xml.XmlElement#http://schemas.microsoft.com/wbem/wsman/1/windows/shell#Shell"
$t | Add-PSTypeExtension -MemberType ScriptProperty -MemberName ShellRun -Value {[System.Xml.XmlConvert]::ToTimeSpan($this.ShellRunTime) }
$t | Add-PSTypeExtension -MemberType ScriptProperty -MemberName ShellInactive -Value {[System.Xml.XmlConvert]::ToTimeSpan($this.ShellInactivity) }
$t | Add-PSTypeExtension -MemberType ScriptProperty -MemberName Idle -Value {[System.Xml.XmlConvert]::ToTimeSpan($this.IdleTimeout) }
$t | Add-PSTypeExtension -MemberType ScriptProperty -MemberName MaxIdle -Value {[System.Xml.XmlConvert]::ToTimeSpan($this.MaxIdleTimeout) }

Esto facilita la ejecución del comando y la selección de las nuevas propiedades.

O podría crear una vista de formato personalizado. Pero te dejaré ese ejercicio.

Artículos relacionados