Búsqueda de sitios web

Formato personalizado sencillo de PowerShell


Una de las características que realmente disfruto de PowerShell es la capacidad de presentar la información que necesito en la forma que quiero. He aquí un buen ejemplo. Ejecutar Get-Process es bastante simple y el resultado es bastante completo. Pero una cosa que lo mejoraría para mí es que a veces quiero una manera fácil de ver las propiedades de uso elevado de memoria. Sí, puedo canalizar Get-Process a Sort-Object y Where-Object. Sin embargo, en esta situación particular, lo que realmente quiero es ver los procesos de uso elevado de memoria mostrados en rojo. Quizás me gustaría ver en amarillo aquellos que se están acercando a mi límite arbitrario. Esto no es tan difícil de lograr usando secuencias de escape ANSI.

Para probar esto, definí una serie de propiedades personalizadas que en esencia duplican la salida predeterminada de Get-Process.

$props = @(
    "Handles",
    @{Name = "NPM(K)"; Expression = { [int]($_.npm / 1kb) } },
    @{Name = "PM(K)"; Expression = { [int]($_.pm / 1kb) } },
    @{Name = "WS(M)"; Expression = {
            if ($_.ws -ge 500MB) {
                "$([char]0x1b)[91m$([int]($_.ws/1mb))$([char]0x1b)[0m"
            }
            elseif ($_.ws -ge 250MB) {
                "$([char]0x1b)[93m$([int]($_.ws/1mb))$([char]0x1b)[0m"
            }
            else {
                [int]($_.ws / 1mb)
            }
        }
    },
    @{Name = "CPU"; Expression = { New-TimeSpan -Seconds $_.cpu } },
    "ID",
    @{Name = "ProcessName"; Expression = {
            if ($_.ws -ge 500MB) {
                "$([char]0x1b)[1;91m$($_.processname)$([char]0x1b)[0m"
            }
            elseif ($_.ws -ge 250MB) {
                "$([char]0x1b)[1;93m$($_.processname)$([char]0x1b)[0m"
            }
            else {
                $_.processname
            }
        }
    }
)

Las propiedades personalizadas se definen como tablas hash, tal como lo haría al usar Select-Object. En este código, si el valor de WorkingSet es mayor o igual a MB, los valores de WS y de nombre de proceso se formatean en rojo usando una secuencia de escape ANSI que funcionará tanto en Windows PowerShell como en PowerShell 7.

"$([char]0x1b)[1;91m$($_.processname)$([char]0x1b)[0m"

También cambié el nombre del encabezado WS para reflejar el valor en MB como [INT]. Y mientras estaba en eso, me di cuenta de que la propiedad de la CPU es en realidad una cantidad de segundos, así que la mostraré como un intervalo de tiempo.

@{Name = "CPU"; Expression = { New-TimeSpan -Seconds $_.cpu } }

Con esta matriz cargada en mi sesión de PowerShell, puedo ejecutar un comando como este.

Get-Process | Where-Object WS -ge 100MB | Format-Table -Property $props -AutoSize

Agregué algunos filtros para que la captura de pantalla sea más fácil de leer.

Ahora viene la parte divertida.

No quiero tener que escribir todo ese código para las propiedades personalizadas. Una cosa que puedo hacer es crear una vista de tabla personalizada con un nombre como WS. El formato personalizado se realiza con un archivo .ps1xml. Sí. XML. Pero lo hice un proceso fácil y la semana pasada lo hice aún más fácil.

El módulo PSScriptTools, que puede descargar desde la Galería de PowerShell, tiene un comando llamado New-PSFormatXML. La premisa es que canaliza un objeto de muestra al comando, especificando qué tipo de formato personalizado desea, incluidas las propiedades, y creará el archivo .ps1xml por usted. La semana pasada, agregué la capacidad de admitir scriptblocks que crearán automáticamente las etiquetas ScriptBlock en el archivo. En versiones anteriores, había que crearlos manualmente. Con el módulo PSScriptTools instalado, puedo ejecutar un comando como este para generar un archivo, usando mi conjunto de propiedades personalizadas previamente definida.

Get-Process -Id $pid | New-PSFormatXML -Properties $props -ViewName WS -FormatType Table -Path c:\scripts\wsprocess.format.ps1xml -Passthru

La función solo necesita un único objeto. Por cierto, si ejecuta este comando en VSCode y usa el parámetro -Passthru, el nuevo archivo .ps1xml se abrirá automáticamente. Aquí está mi resultado.

<?xml version="1.0" encoding="UTF-8"?>
<!--
Format type data generated 10/11/2020 13:02:12 by PROSPERO\Jeff
This file was created using the New-PSFormatXML command that is part
of the PSScriptTools module.
https://github.com/jdhitsolutions/PSScriptTools
-->
<Configuration>
  <ViewDefinitions>
    <View>
      <!--Created 10/11/2020 13:02:12 by PROSPERO\Jeff-->
      <Name>WS</Name>
      <ViewSelectedBy>
        <TypeName>System.Diagnostics.Process</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <!--Delete the AutoSize node if you want to use the defined widths.-->
        <AutoSize />
        <TableHeaders>
          <TableColumnHeader>
            <Label>Handles</Label>
            <Width>10</Width>
            <Alignment>right</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>NPM(K)</Label>
            <Width>20</Width>
            <Alignment>right</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>PM(K)</Label>
            <Width>19</Width>
            <Alignment>right</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>WS(M)</Label>
            <Width>212</Width>
            <Alignment>right</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>CPU</Label>
            <Width>31</Width>
            <Alignment>center</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>Id</Label>
            <Width>8</Width>
            <Alignment>right</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>ProcessName</Label>
            <Width>210</Width>
            <Alignment>left</Alignment>
          </TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Handles</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>[int]($_.npm/1kb)</ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>[int]($_.pm/1kb)</ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>
                  if ($_.ws -ge 500MB) {
                      "$([char]0x1b)[91m$([int]($_.ws/1mb))$([char]0x1b)[0m"
                  }
                  elseif ($_.ws -ge 250MB) {
                      "$([char]0x1b)[93m$([int]($_.ws/1mb))$([char]0x1b)[0m"
                  }
                  else {
                      [int]($_.ws/1mb)
                  }
                </ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>New-Timespan -Seconds $_.cpu</ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Id</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>
                  if ($_.ws -ge 500MB) {
                      "$([char]0x1b)[1;91m$($_.processname)$([char]0x1b)[0m"
                  }
                  elseif ($_.ws -ge 250MB) {
                      "$([char]0x1b)[1;93m$($_.processname)$([char]0x1b)[0m"
                  }
                  else {
                      $_.processname
                  }
                </ScriptBlock>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
  </ViewDefinitions>
</Configuration>

El archivo .ps1xml tiene por defecto el tamaño automático, lo cual me parece bien. Todo lo que tengo que hacer es cargar el archivo en mi sesión de PowerShell.

Update-FormatData C:\scripts\wsprocess.format.ps1xml

Este archivo no tiene ningún efecto si simplemente ejecuto Get-Process. Pero si quiero usarlo, todo lo que necesito hacer es especificar el nombre de la vista.

Get-Process | Where-Object WS -ge 100MB | Format-Table -View ws

Nuevamente, agregué algunos filtros por el bien de la demostración.

Si quiero que esto esté siempre disponible, puedo poner la expresión Update-FormatData en mi script de perfil de PowerShell. Y como recordatorio, cuando accede a uno de los cmdlets de formato, le está diciendo a PowerShell que lo único que desea es algo bonito que ver. No puede volver a canalizar a un comando Ordenar-Objeto o Exportar-CSV. Todo lo que puede hacer es canalizar la salida a Out-File o Out-Printer.

Ahora tengo PowerShell haciendo mi trabajo por mí. Puedo saber de un vistazo qué procesos están utilizando más memoria. Diablos, podría llevar este concepto un paso más allá y definir una función "tramposa" en mi perfil.

function ws { Get-Process | Format-Table -view ws}

Solo voy a usar esto de forma interactiva en mi consola, por lo que el hecho de que no sigo la convención de nomenclatura verbo-sustantivo es irrelevante.

Podría modificar aún más el ps1xml. Aquí hay algunas cosas que yo (o usted) podríamos considerar:

  • Elimine AutoSize y ajuste el ancho de las columnas.
  • Modifique otras propiedades como PM y formatéelas como MB.
  • Agregue una propiedad como Runtime.
  • Hacer que mi vista coloreada sea la predeterminada para los objetos de Proceso.

Me encantaría saber qué formato personalizado se te ocurrió y qué problemas resolvió.

Artículos relacionados