Hey, Scripting Guy!Trabajar sin esforzarse también es gratificante

Los chicos de la sección Microsoft Scripting Guy

Descargar el código de este artículo: HeyScriptingGuy2008_06.exe (150KB)

Durante siglos se creyó que la dedicación al trabajo era una recompensa; que la satisfacción total se obtenía después de un duro día de trabajo; que la felicidad completa llega al alcanzar el destino, no al realizar el viaje; que... bueno, ésa es la idea. Si trabaja mucho y no se queja (como Cenicienta), algún día tendrá una recompensa.

Nota: ¿es realmente cierto que si trabaja mucho no sólo será más feliz si no que, algún día se le recompensará por sus esfuerzos? ¿Por qué nos plantea esta pregunta?

Cenicienta tuvo suerte; vivir con una madrastra y dos hermanastras malvadas significa tener mucho trabajo. ¿Pero, qué pasa si no tiene tanta suerte? ¿Qué ocurre si no tiene la oportunidad de vivir con una madrastra y dos hermanastras malvadas? ¿Cómo logrará trabajar mucho durante todo el día sin obtener nada a cambio? ¿Cómo obtendrá algún día la felicidad completa?

Si completamente decidido a dejarse la piel trabajando, los chicos de la sección Scripting Guy le sugieren que trate de escribir un script para generar datos en un formato atractivo, ordenado y tabular, como el resultado que se muestra en la figura 1.

Figura 1 Resultado tabular

Figura 1** Resultado tabular **(Hacer clic en la imagen para ampliarla)

Insistimos, Cenicienta tuvo mucha suerte: limpiaba la casa de arriba abajo y, cuando terminaba, descansaba en la chimenea entre las cenizas y el hollín. Sin embargo, también ella se habría negado a tener que escribir un script que muestre los datos en formato tabular. Sentarse en las cenizas y el hollín es una cosa, y escribir un script que genere datos en formato tabular es totalmente diferente.

Nota: a menos que deba sentarse en la chimenea para escribir scripts; entonces es casi lo mismo.

¿Por qué Cenicienta se negaría a hacer esta tarea? Sencillamente, porque es demasiado duro. La única manera de mostrar datos en formato tabular en VBScript es hacer algo similar a esto:

  • Para empezar, determine el tamaño máximo de las columnas. Por ejemplo, es posible que el nombre para mostrar de un servicio deba tener 52 caracteres.
  • A partir de ahí, calcule el número de caracteres de una parte de los datos. Por ejemplo, el nombre para mostrar de Adobe LM Service tiene 16 caracteres.
  • Si éste supera el límite de 52 caracteres, determine el número de caracteres que debe cortar al final de la cadena para que se ajuste al espacio asignado. Si el nombre para mostrar no supera los 52 caracteres, determine los espacios en blanco que debe agregar al final de la cadena para conseguir un largo exacto de 52 caracteres.
  • Repita estos pasos en el conjunto de datos siguiente. Y en el siguiente. Etc.

Si conoce el cuento de Cenicienta, sabrá que en cualquier momento puede aparecer su hada madrina, convertir un ratoncito en un administrador del sistema (no haremos ningún comentario acerca de lo fácil que puede resultar la conversión) y el ratoncito le escribirá este script. Aunque no debería contar con que suceda esto. Está sólo ante el peligro.

Sin embargo, por el lado bueno, será la persona más feliz del mundo. También se sentirá así porque será la persona que más trabaja, con diferencia.

Por supuesto, algunas personas están dispuestas a cambiar un poco esa satisfacción por no tener que trabajar tanto. Si es su caso, seguramente estará pensando: "Gracias a Dios que puedo contar con los chicos de la sección Scripting Guy; ellos me explicarán cómo convertir un ratoncito en un administrador del sistema y conseguir que el ratoncito dé formato de tabla a los datos". Sentimos decirle que tenemos malas noticias: los chicos de la sección Scripting Guy no tienen la menor idea de cómo convertir un ratoncito en un administrador del sistema. Aunque, si le sirve de algo, podemos convertir un administrador del sistema en un ratoncito. Nosotros no sabemos cómo conseguir que alguien le escriba scripts, y menos aún, los que generan datos en formato tabular.

Pero no es necesario. Siempre que ejecute Windows® XP o Windows Server® 2003 (que es lo que usan muchos de los usuarios), no necesita que un ratoncito le escriba scripts. (Lo siento, me temo que no funciona en Windows Vista®.) En cambio, puede hacerlo usted mismo dedicándole un esfuerzo mínimo con el objeto Microsoft.CmdLib. Observe el script de muestra de la figura 2.

Figure 2 Crear una visualización tabular

Dim arrResultsArray()
i = 0

Set objCmdLib = _
  CreateObject("Microsoft.CmdLib")
Set objCmdLib.ScriptingHost = _
  WScript.Application

arrHeader = Array("Display Name", _
  "State", "Start Mode")
arrMaxLength = Array(52, 12, 12)
strFormat = "Table"
blnPrintHeader = True
arrBlnHide = Array(False, False, False)

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" _
  & strComputer & "\root\cimv2")

Set colServices = objWMIService.ExecQuery _
  ("Select * FROM Win32_Service")

For Each objService In colServices
  ReDim Preserve arrResultsArray(i)
  arrResultsArray(i) = _
  Array(objService.DisplayName, _
    objService.State,objService.StartMode)
  i = i + 1
Next
objCmdLib.ShowResults arrHeader, _
  arrResultsArray, arrMaxLength, _
  strFormat, blnPrintHeader, arrBlnHide

En caso de que se lo pregunte, Microsoft.CmdLib es un objeto COM que se distribuye con Windows XP y Windows Server 2003. Este objeto tiene diferentes características; para obtener más información, escriba cmdlib.wsc /? en el símbolo del sistema y lea los comentarios del archivo de script que aparece en la pantalla. Hoy tan sólo nos centraremos en una característica de Microsoft.CmdLib: la capacidad de mostrar datos en formato tabular.

Esto hace que nos planteemos una pregunta obvia: ¿cómo se muestran datos en formato tabular? Intentaremos explicarlo. Como puede observar, el script de muestra empieza con la definición de una matriz dinámica denominada arrResultsArray:

Dim arrResultsArray()

En un script normal, se devuelven los datos a la pantalla en el momento en que se recuperan. ¿Pero de verdad cree que los chicos de la sección Scripting Guy hacen las cosas de forma normal? En este script, no devolveremos los datos en el momento de recuperarlos. En cambio, almacenaremos todos los datos devueltos en una matriz y el formato Microsoft.CmdLib nos mostrará los datos.

En otras palabras, ésta es la razón por la que crearemos una matriz dinámica (es decir, una matriz a la que se puede cambiar el tamaño durante el script). Después de definir la matriz, establecemos los valores de una variable de contador de i a 0; usaremos esta variable para realizar el seguimiento del tamaño actual de la matriz.

En caso de que se lo pregunte, establecemos el valor de i a 0 porque al primer elemento de una matriz siempre se le atribuye el número de índice 0. Por consiguiente, al agregar el primer elemento a la matriz, agregamos el elemento 0 en lugar del 1.

A continuación, debemos inicializar la copia del objeto Microsoft.CmdLib; es para lo que sirven las dos líneas de código:

Set objCmdLib = _
CreateObject("Microsoft.CmdLib")
Set objCmdLib.ScriptingHost = WScript.Application

Esto nos lleva al siguiente bloque de código:

arrHeader = Array("Display Name", "State", "Start Mode")
arrMaxLength = Array(52, 12, 12)
strFormat = "Table"
blnPrintHeader = True
arrBlnHide = Array(False, False, False)

Aquí configuramos algunos parámetros para configurar el resultado. En la primera línea, asignamos valores a una matriz denominada arrHeader; como puede imaginar, se trata de los encabezados de cada columna de la tabla de salida. En el script de muestra, recuperaremos información acerca de los servicios que se ejecutan en un equipo, a continuación, mostraremos los valores de las propiedades DisplayName, State y StartMode de cada servicio.

No es muy sorprendente que denominemos a las columnas de la matriz DisplayName, State y StartMode (aunque también habríamos podido denominarlas A, B y C; Larry, Moe y Curly; o cualquier otra cosa. El nombre de las columnas no será el mismo que el de las propiedades.).

Nota: Existen muchas versiones del cuento de Cenicienta, incluidos los nombres de sus hermanastras. En la versión de Disney, las hermanastras se llaman Drizzella y Anastasia, que casualmente son los nombres de nuestro editor de scripting.

En la segunda línea del código, asignamos valores a otra matriz, denominada arrMaxLength. Esta matriz contiene el tamaño de las columnas de la tabla. En el resultado, asignaremos 52 caracteres a la columna 1, que contiene el nombre para mostrar del servicio (DisplayName); a continuación, asignaremos 12 espacios al estado del servicio (State) y al modo de inicio (StartMode). Microsoft.CmdLib insertará automáticamente un espacio en blanco entre columnas. Para que el tamaño de las columnas sea de 52, 12 y 12 caracteres, usaremos un código similar a éste:

arrMaxLength = Array(52, 12, 12)

La tercera línea especifica el formato de salida de los datos. Para que los datos se muestren como una tabla, estableceremos el valor de strFormat (variable que contiene el tipo de salida) como Table. Ahora, en lugar de eso, supongamos que queremos mostrar los datos como una lista de valores separados por comas. En ese caso, estableceríamos el formato como CSV, de la siguiente manera:

strFormat = "CSV"

En cambio, obtendremos un resultado similar al que aparece a continuación:

"Display Name","State","Start Mode"
"Adobe LM Service","Stopped","Manual"
"Adobe Active File Monitor V4","Stopped","Manual"
"Alerter","Stopped","Manual"
"Application Layer Gateway Service","Running","Manual"
"Apple Mobile Device","Running","Auto"
"Application Management","Stopped","Manual"

Fíjese que Microsoft.CmdLib no sólo puso comas entre cada elemento, sino que también colocó los valores individualmente entre comillas dobles. Esta característica es más atractiva de lo que parece al principio. Después de todo, para hacerlo manualmente, deberá usar un código similar al siguiente:

Wscript.Echo Chr(34) & objService.DisplayName & Chr(34) & "," & Chr(34) & objService.State & Chr(34) & "," & Chr(34) & objService.StartMode & Chr(34)

Vaya.

¿Cómo que qué es eso? Aunque no le gusten las tablas, ¿tampoco le gusta el formato CSV? En ese caso, trate de configurar el formato como List, lo que le proporcionará un resultado similar al siguiente:

Display Name: Adobe LM Service
State:        Stopped
Start Mode:   Manual
Display Name: Adobe Active File Monitor V4
State:        Stopped
Start Mode:   Manual

Pero hacemos una digresión. (Como solemos hacer de vez en cuando.) Después de definir el formato de salida, establecemos el valor de una variable denominada blnPrintHeader como True:

blnPrintHeader = True

Usaremos blnPrintHeader para decir a Microsoft.CmdLib que imprima el encabezado de las columnas. ¿Qué debemos hacer para no imprimir el encabezado de las columnas? Establezca blnPrintHeader como False:

blnPrintHeader = False

Por último, tenemos la siguiente línea de código:

arrBlnHide = Array(False, False, False)

Cuando llega el momento de mostrar los datos, Microsoft.CmdLib le da la opción de mostrar u ocultar alguna columna. Para ocultar una columna (es decir, para suprimir los datos de una propiedad particular), establezca el valor de esa propiedad como True; para mostrar una columna, establezca el valor como False.

En el resultado, mostraremos los valores de las propiedades DisplayName, State y StartMode, en ese orden; por consiguiente, en nuestra matriz usamos los valores False, False, False. ¿Qué debemos hacer para mostrar DisplayName, ocultar State y mostrar StartMode? En ese caso, usaremos la siguiente línea de código:

arrBlnHide = Array(False, True, False)

Recuerde, use False para mostrar una columna y True para ocultarla.

En este punto, podemos generar algunos datos, si los tenemos. ¿Alguna vez los chicos de la sección Scripting Guy escribieron un script que produjo un error al generar datos y, después de pasar una gran cantidad de tiempo depurándolo, se dieron cuenta de que no se habían molestado en recuperar ningún dato en primer lugar? No, claro que no; ¿por qué lo pregunta?

Con esta idea en mente, el siguiente paso es enlazar el servicio Instrumental de administración de Windows (WMI) en el equipo local y usar, a continuación, el método ExecQuery para recuperar información acerca de todos los servicios instalados en ese equipo:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colServices = objWMIService.ExecQuery ("Select * FROM Win32_Service")

Debemos mencionar que no sólo puede trabajar con los datos de WMI. Como es de esperar, Microsoft.CmdLib funciona con cualquier tipo de datos. WMI es una forma útil de recuperar gran cantidad de datos.

Una vez que tenemos una recopilación de datos relacionados con el servicio, configuramos un bucle For Each para recorrer todos los servicios de la recopilación. Sin embargo, en lugar de devolver los valores de propiedad de cada servicio, ejecutamos este código:

ReDim Preserve arrResultsArray(i)
arrResultsArray(i) = Array(objService.DisplayName, objService.State,objService.StartMode)
i = i + 1

¿Qué haremos con este bloque de código? En la línea 1 cambiamos el tamaño de la matriz mediante el comando ReDim Preserve no sólo para cambiar el tamaño de la matriz sino también para conservar los datos existentes de ésta. Sin la palabra clave Preserve, cambiará el tamaño de la matriz, pero se borrarán los datos que contiene.

¿De qué tamaño hacemos la matriz? La primera vez que se realiza el bucle, haremos una matriz de tamaño 0; en otras palabras, crearemos una matriz que incluye un elemento. ¿Y cómo sabemos con seguridad que estamos haciendo una matriz de tamaño 0? Porque usamos la variable de contador i, y esa variable es igual a 0.

Por tanto, ¿si usamos la variable i para representar el tamaño de la matriz, no significa que siempre estableceremos el tamaño como 0? En principio sí, salvo si cada vez que realizamos un bucle aumentamos 1 punto el valor de i; como puede observar, eso es lo que hacemos en la línea 3 del bloque de código.

Lo que nos deja con sólo una línea de código por la que preocuparnos:

arrResultsArray(i) = Array(objService.DisplayName, objService.State,objService.StartMode)

Entonces, usamos únicamente los valores de propiedad que nos interesan (DisplayName, State y StartMode) y los agregamos a la matriz arrResultsArray. Tenga en cuenta que no agregamos los valores de propiedad individualmente; en cambio, los agregamos como una matriz de valores. Efectivamente, es excepcional: significa que cada elemento de la matriz arrResultsArray será otra matriz; así es como funciona Microsoft.CmdLib.

Por supuesto, esto puede suponer otro motivo de preocupación. "Una matriz de matrices. ¿Cómo demonios podré obtener acceso a todos los valores individuales de una matriz llena de matrices?" No se asuste. Es sencillo: deje que se encargue Microsoft.CmdLib.

Nota: lamentamos decirle que obtener acceso a todos los valores individuales de una matriz de matrices es casi lo único en lo que puede ayudarle Microsoft.CmdLib. Sin embargo, en el próximo lanzamiento del objeto se explicará cómo limpiar las habitaciones de su malvada madrastra, fregar los platos y lavar la ropa de sus hermanastras.

De hecho, podemos generar datos (en formato tabular ordenado) con tan sólo llamar al método ShowResults y pasarle todas las matrices y variables configuradas anteriormente en el script:

objCmdLib.ShowResults arrHeader, arrResultsArray, arrMaxLength, strFormat, blnPrintHeader, arrBlnHide

¿Qué aspecto tendrá todo esto? Será el formato ordenado que esperábamos, tal como se muestra en la figura 1.

No está nada mal, ¿verdad? Consiguió un resultado ordenado sin necesidad de hacer mucho trabajo. Y el siguiente script será aún más fácil porque tan sólo tendrá que hacer algunos cambios secundarios a este primer script.

Hay que reconocer que no obtendrá la misma satisfacción que Cenicienta al estar esclavizada día y noche para poder mostrar el resultado de un script en una tabla. Y, para ser honesto, seguramente no se casará con un príncipe; no lo habíamos acordado. Pero es un precio pequeño que tendrá que pagar por obtener un resultado tan atractivo a partir de un script de VBScript, especialmente si tiene en cuenta lo que representa un príncipe hoy en día.

Y, ¿quién sabe?: quizás consiga casarse con un príncipe de todos modos. Después de todo, incluso la realeza necesita obtener información acerca de todos los servicios instalados en sus equipos, ¿verdad?

Dr. Scripto's Scripting Perplexer

El desafío mensual que prueba no sólo sus habilidades para resolver rompecabezas, también sus habilidades para el scripting.

Junio de 2008: El camino a PowerShell

En cada uno de estos enigmas, conecte las letras horizontal, vertical y diagonalmente de forma que lea el nombre de un cmdlet de Windows PowerShell. Cada letra se usará sólo una vez. Por ejemplo:

El cmdlet creado en este enigma es New-Alias. Ahora le toca a usted. Aquí hay tres enigmas más:

ANSWER:

Dr. Scripto's Scripting Perplexer

Encontrará las respuestas en: El camino a PowerShell, junio de 2008

Read-Host

Set-AuthenticodeSignature

Los chicos de la sección Microsoft Scripting Guy trabajan para Microsoft, mejor dicho, están contratados por Microsoft. Cuando no juegan al béisbol, ni entrenan ni lo ven (u otras actividades varias), dirigen el TechNet Script Center. Visite la página web www.scriptingguys.com.

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