Windows PowerShellEl buzón de entrega

Don Jones

Contenido

Personificación del cmdlet
La función de filtrado
Pensamiento físico
Aplicaciones prácticas
Haga tiempo para el juego

En una de las clases sobre Windows PowerShell que impartí hace poco, algunos alumnos tenían problemas para entender en qué consistía la canalización del shell. He de admitir que la canalización no es un concepto totalmente intuitivo y, por lo tanto, los estudiantes pueden tener dificultades para comprender de forma visual qué está ocurriendo exactamente. Cuando llegué al

concepto de las funciones de filtrado, con actividad directa en la canalización, las cosas se complicaron de verdad. Con sólo verles la cara comprendí que los estaba perdiendo.

Para intentar ayudarles, aparecí al día siguiente con una caja, algunas etiquetas adhesivas y varias pelotas de ping-pong. Nadie ha dicho que Windows PowerShell® no pueda ser divertido. Decidí hacerles una demostración mediante la siguiente línea de comandos:

Get-Process | Where { $_.VM –gt 1000 } | Sort VM
–descending | Export-CSV C:\Procs.csv

Personificación del cmdlet

Con las etiquetas adhesivas (las pegatinas de "Hola, me llamo …" que había que llevar en las reuniones del colegio y en otros eventos igualmente temidos), asigné un nombre de cmdlet a cada estudiante. Les expliqué que las pelotas de ping-pong representaban objetos de proceso de Windows® (más concretamente, los objetos del tipo System.Diagnostics.Process), y les pregunté qué nombre de cmdlet parecía tener más probabilidades de generar objetos de proceso.

Al mirar las etiquetas de los demás, se decidieron por la elección evidente de Get-Process. Este es uno de los principales motivos por los que me gustan los nombres de cmdlet que se usan en Windows PowerShell: son totalmente lógicos.

De forma que el estudiante que llevaba la etiqueta Get-Process tomó todas las pelotas de ping-pong y las colocó en la caja de cartón. La caja representa la canalización del shell, y lo que hizo el estudiante es bastante parecido a lo que hace un cmdlet. Un cmd­let genera uno o más objetos y los vuelca en la canalización.

El siguiente cmdlet de la canalización entra en escena. En este ejemplo, el estudiante que representaba el cmdlet Where-Object tomó todas las pelotas de ping-pong y las examinó, una a una, para comprobar si la propiedad VM de alguna de ellas era mayor que 1.000. En el shell, "VM" significa memoria virtual y, para el propósito del ejercicio, había escrito con rotulador las diferentes cantidades de memoria virtual en cada pelota de ping-pong.

Todas las pelotas (también llamadas procesos) que tenían una VM de 1.000 o superior, se devolvían a la caja (o a la canalización), mientras que las otras, de un valor inferior, se tiraban a la papelera, de donde nunca saldrían. En realidad, eso no es del todo cierto; las recuperé para volver a usarlas en futuras clases.

A continuación, el estudiante que hacía de Sort-Object inspeccionó la caja de pelotas de ping-pong y las ordenó según la propiedad VM. He de admitir que esta parte del ejercicio no estaba muy bien pensada (fue algo difícil evitar que las pelotas saliesen rodando). Creo que tendré que encontrar dados de ping-pong para la próxima clase o tener cartones de huevos a mano.

Para terminar, el alumno que hacía de Export-CSV tomó las pelotas y escribió la información en un archivo CSV. En el mundo real, esto equivalía a escribir las propiedades del proceso en un cuaderno, que representaba el archivo CSV.

La función de filtrado

Una vez que quedó claro qué es una canalización sencilla, decidimos echar un vistazo a las funciones de filtrado, que son un poco más difíciles. Les propuse la función de filtrado y la línea de comandos que aparece en la Figura 1.

Figura 1 Función de filtrado y línea de comandos de ejemplo

Function Do-Something {
 BEGIN { }
 PROCESS {
  $obj = New-Object PSObject
  $obj | Add-Member NoteProperty "TimeStamp" (Get-Date)
  $obj | Add-Member NoteProperty "ProcessName" ($_.Name)
  Write-Output $obj
 }
 END {}
}
Get-Process | Do-Something | Format-Table *

Antes que nada, me gustaría explicar brevemente para qué sirven las funciones de filtrado. La idea es que la función contiene tres bloques de script, llamados BEGIN, PROCESS y END.

Al usar la función en la canalización, el bloque de script BEGIN se ejecuta en primer lugar. Este script puede realizar cualquier tarea de configuración, como abrir una conexión de base de datos.

A continuación, se ejecuta el bloque de script PROCESS una vez por cada objeto canalizado en la función. En el bloque de script PROCESS, la variable $_ se rellena de forma automática con el objeto de canalización actual. Por lo tanto, si se canalizan 10 objetos, el bloque de script PROCESS se ejecuta 10 veces.

Finalmente, una vez que se han ejecutado todos los objetos canalizados, se ejecuta el bloque de script END, que realiza todas las tareas de limpieza necesarias, como cerrar una conexión de base de datos.

En cualquiera de estos bloques de script, cualquier cosa que se escriba mediante Write-Output acaba en la canalización a la espera del siguiente cmdlet. Cualquier cosa que no se haya escrito mediante Write-Output se descarta.

Ya que el comando comenzaba con Get-Process, nuestro alumno reunió todas las pelotas de ping-pong (mientras nos tomamos un pequeño descanso, las pelotas acabaron misteriosamente desperdigadas por toda la sala) y las colocó en la caja de canalización. Hecho esto, el estudiante con la función de filtrado "Do-Something" entró en acción.

En primer lugar, ejecutó el bloque de script BEGIN. Como estaba vacío, no costó demasiado trabajo. A continuación, tomó todos los objetos de la canalización (las pelotas de ping-pong) y las examinó una a una. Esto resultó bastante divertido, ya que había casi una docena de pelotas y él intentaba mantenerlas todas en su regazo. Era para verlo.

En fin, tomó los objetos de proceso uno a uno y ejecutó el bloque de script PROCESS. Este bloque de script le hizo crear un objeto personalizado totalmente nuevo. Para esto, usé pelotas amarillas especiales, pero no crea que fue fácil encontrarlas en la tienda de deportes. En estas pelotas de ping-pong nuevas, escribió la fecha actual junto al nombre del objeto de proceso que estaba examinando en ese momento.

Este nuevo objeto personalizado se escribió en la canalización, lo que significa que el alumno colocó la pelota de ping-pong amarilla en la caja, y se descartó la pelota de ping-pong que hacía de objeto de proceso original. Realizó este proceso con cada pelota de ping-pong que había en la caja, aproximadamente 12. Al acabar, se habían sustituido todas las pelotas de ping-pong blancas por pelotas de ping-pong amarillas personalizadas. Por último, ejecutó su bloque de script END, que estaba vacío, por lo que no tardó nada.

Para concluir el proceso, entró en escena el estudiante Format-Table. La alumna tomó todo lo que había en la caja (llegados a este punto, sólo había objetos "personalizados" amarillos) y empezó a crear una tabla, con las propiedades escritas en cada pelota. El resultado fue una tabla, escrita en nuestro cuaderno, con dos columnas (TimeStamp y ProcessName) y unas doce filas.

Pensamiento físico

Este ejercicio consiguió que comprendiesen las funciones de canalización y de filtrado perfectamente. Las pelotas de ping-pong hicieron un gran trabajo al representar los objetos, que suelen ser un poco abstractos al hablar del shell.

Todo el mundo acabó por ver cómo los cmdlets manipulaban los objetos, cómo se colocaban los resultados en la canalización y cómo el cmdlet siguiente tomaba dichos resultados y manipulaba los objetos aún más. La secuencia de eventos en una función de filtrado era más evidente, al igual que la técnica de funcionamiento del bloque de script PROCESS (con un objeto de entrada cada vez).

Así, también se demostraba cómo se crea un objeto personalizado nuevo y cómo se coloca en la canalización. Además, ayuda a mostrar las ventajas de producir objetos personalizados en lugar de texto sencillo (otros cmdlets podrían consumir los nuevos objetos personalizados, como Format-Table, lo que ofrece mucha flexibilidad a la hora de usar y representar los datos).

Aplicaciones prácticas

La pregunta lógica de los estudiantes se refería al uso de la canalización y las funciones de filtrado en aplicaciones prácticas. La respuesta me resultó sencilla, ya que había creado varios ejemplos para una conferencia reciente (puede descargar los ejemplos desde scriptinganswers.com/essentials/index.php/2008/03/25/techmentor-2008-san-francisco-auditing-examples).

Uno de los ejemplos tiene la finalidad de enumerar varias propiedades de la clase Win32_UserAccount en el Instrumental de administración de Windows (WMI) a partir de varios equipos. Siempre que los nombres de los equipos aparezcan enumerados en C:\Computers.txt, este sencillo comando lo arreglará todo:

Gwmi win32_useraccount –comp (gc c:\computers.txt)

El problema es que la clase Win32_UserAccount no contiene ninguna propiedad que indique de qué equipo procede cada instancia, de forma que la lista resultante es una mezcla bastante inútil de cuentas procedentes de equipos diferentes. El problema se puede resolver al crear un objeto personalizado nuevo que incluya el nombre del equipo original, además de seleccionar las propiedades de clase de interés. El código para este objeto personalizado aparece en la Figura 2.

Figura 2 Uso de un objeto personalizado para reunir los nombres de equipos y seleccionar las propiedades

function Get-UserInventory {
  PROCESS {
    # $_ is a computer name
    $users = gwmi win32_useraccount -ComputerName $_
    foreach ($user in $users) {
      $obj = New-Object
      $obj | Add-Member NoteProperty Computer $_
      $obj | Add-Member NotePropertyPasswordExpires ($user.PasswordExpires)
      $obj | Add-Member NoteProperty Disabled ($user.Disabled)
      $obj | Add-Member NotePropertyFullName ($user.FullName)
      $obj | Add-Member NoteProperty Lockout ($user.Lockout)
      $obj | Add-Member NoteProperty Name ($user.Name)
      $obj | Add-Member NotePropertyPasswordRequired ($user.PasswordRequired)
      $obj | Add-Member NotePropertyPasswordChangeable ($user.PasswordChangeable)
    }
  }
}

Get-Content c:\computers.txt | 
  Get-UserInventory | 
  where { $_.PasswordRequired -eq $False } | 
  selectComputer,Name | 
  Export-Csv c:\BasUsersBad.csv

La línea de comandos final envía todos los nombres de equipos a la función de filtrado, que produce los objetos personalizados. A continuación, se filtran todos los usuarios, salvo aquellos cuya propiedad PasswordRequired sea False (la idea es producir un informe de auditoría de las cuentas problemáticas). Sólo hay que conservar las propiedades Computer y Name, por lo que el informe final es una lista de nombres de equipos y de cuentas que necesitan atención ya que tienen contraseñas que no caducan. La función de filtrado hace posible este informe de varios equipos, porque agrega el nombre del equipo de origen al resultado a la vez que limita las propiedades a aquellas que estoy interesado en ver.

Aunque existen otras formas similares de realizar esta tarea, tal vez este enfoque sea el más sencillo. También sirve para ilustrar conceptos y técnicas importantes.

Haga tiempo para el juego

Incluso aunque no le disguste la canalización, hay una lección que debería aprender. El pensamiento en términos de objetos físicos puede ayudarnos a vislumbrar lo que intentamos hacer.

Por eso, la próxima vez que tenga problemas con un concepto de Windows PowerShell, intente alejarse del equipo y reproducir la tarea mediante objetos cotidianos a modo de ilustración. Le recomiendo que tenga a mano una bolsa con pelotas de ping-pong (o dados, si puede encontrarlos en alguna parte), que son muy útiles para este fin.

Cmdlet del mes Out-File

¿Cuántas veces ha enviado el resultado de un cmdlet a un archivo mediante el símbolo >? Será algo como Get-Process > Processes.txt. ¿Sabía que en realidad lo que está usando es el cmdlet Out-File disfrazado? Aquí puede ver un comando que tiene exactamente la misma función: Get-Process | Out-File Processes.txt.

Evidentemente, este comando requiere más escritura, así que, ¿para qué molestarse en escribir el cmdlet Out-File completo? Un motivo es que Out-File es mucho más claro al leerlo. Suponga que alguien viene seis meses después, echa un vistazo a uno de sus scripts y se pregunta qué significa > (al fin y al cabo, es prácticamente algo heredado).

Por el contrario, Out-File deja claro que se va a crear un archivo y se va a escribir en él. Además, Out-File proporciona el parámetro -append (muy parecido a >>), junto con los parámetros -force y -noClobber, lo que permite controlar si se sobrescriben archivos existentes. Finalmente, al escribir Out-File, también se consigue acceso al parámetro -whatif. Este parámetro de gran utilidad muestra lo que hará Out-File sin que llegue a hacerlo. Es una forma fantástica de probar una línea de comandos complicada sin apenas correr riesgos.

Don Jones es coautor de Windows PowerShell: TFM e imparte clases acerca de Windows PowerShell (www.ScriptingTraining.com).

© 2008 Microsoft Corporation y CMP Media, LLC. Reservados todos los derechos. Queda prohibida la reproducción parcial o total sin previa autorización.