Hey, Scripting Guy!Administración de escritorios desde ultratumba

Los encargados del scripting

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

Con motivo de la abrumadora demanda popular, pensamos que este mes deberíamos hacer algo diferente. En lugar de empezar hablando del scripting de administración de sistemas, empezaremos por... (y aquí se oye música siniestra) contar una historia de miedo.

Nota: vale, técnicamente, si de verdad quisiéramos hacer algo diferente este mes, empezaríamos por hablar del scripting de administración de sistemas, para variar. Pero síganos la corriente, ¿vale? Gracias.

Hace muchos años, falleció una de las tataratataratatarabuelas del scripting. Poco después de enterrar a la abuela en un ataúd de madera muy sencillo, el abuelo empezó a tener pesadillas terribles en las que veía a su amada esposa arañando el ataúd, tratando desesperadamente de salir de la tumba. Después de repetirse las pesadillas varias veces, y tras repetidas súplicas, el abuelo convenció finalmente a las autoridades locales para que exhumaran el cuerpo. Cuando abrieron el ataúd, todos quedaron horrorizados al ver que las uñas de la abuela estaban torcidas y el interior del ataúd estaba lleno de arañazos.

Bueno, quizás esta historia no sea enteramente cierta; de hecho, cuanto más lo pensamos, más convencidos estamos de que no tiene nada de verdadera. No obstante, la historia nos enseña una lección importante. No tenemos la menor idea de cuál, pero tiene que estar ahí, en alguna parte.

Espere un momento, ¡ahora nos acordamos! Los ataúdes se diseñaron originalmente para proteger al difunto de los elementos y para evitar la descomposición del cuerpo. Desgraciadamente, los ataúdes tuvieron una consecuencia inesperada: al menos en teoría, es posible enterrar a personas vivas y las cajas de pino dificultan mucho la escapada. Como demuestra claramente la historia de la tataratataratatarabuela del scripting, incluso los mejores planes pueden tener como resultado el desastre... y el entierro de personas vivas. (Música siniestra una vez más...)

Nota: por supuesto, esto es así a menos que elija la opción del ataúd mejorado, inventado por Franz Vester alrededor de 1860. Este ataúd incluye una cuerda conectada a una campana que permanece en la superficie; en caso de entierro prematuro, el "difunto" puede tocar la campana para pedir ayuda. Este ataúd mejorado incluye también una escalera plegable, aunque no nos queda enteramente claro cómo una escalera plegable te puede ayudar a escapar de un ataúd que está dos metros bajo tierra. Si resulta que te entierran encima de un garaje, bueno, una escalera plegable sería útil. Pero en caso contrario...

Exactamente lo mismo (lo de que incluso los mejores planes pueden llevar al desastre) se puede aplicar a los firewalls de Internet. (Bueno, más o menos...) Los firewalls se diseñaron originalmente para mantener a los tipos malos fuera: bloquean el tráfico de red entrante, lo que puede ayudarle a mantener a piratas informáticos e intrusos alejados de sus equipos. Eso está muy bien pero, como ocurre con el problema de ser enterrado vivo, aquí también hay una consecuencia inesperada: los firewalls también pueden dejar fuera a los tipos buenos. Esto es especialmente cierto en el caso de Instrumental de administración de Windows® (WMI), que depende de DCOM para realizar tareas administrativas en equipos remotos. Los firewalls tienden a bloquear todo el tráfico DCOM entrante, algo que hace muy difícil (si no totalmente imposible) la administración de los equipos a través de Internet mediante programación. De hecho, sin abrir puertos adicionales en el firewall y, por lo tanto, sin incrementar su vulnerabilidad ante piratas informáticos e intrusos, esto es totalmente imposible. Por supuesto, a menos que opte por WinRM: Administración remota de Windows (no incluye la escalera plegable).

¿Qué es Administración remota de Windows?

Según el SDK de WinRM (msdn2.microsoft.com/aa384426), Administración remota de Windows es la implementación de Microsoft del Protocolo WS-Management, un protocolo estándar, basado en SOAP y de fácil uso con firewalls que permite la interoperabilidad de hardware y sistemas operativos de proveedores diferentes. Impresionante, ¿no? No vamos a tratar el Protocolo WS-Management en la columna de este mes, así que recomendamos la lectura del SDK de WinRM para obtener los detalles. Por el momento, todo lo que nos interesa es que WinRM está disponible en Windows Server® 2003 R2, Windows Vista® y Windows Server 2008, y que permite administrar equipos a través de Internet. Para ello, WinRM usa el puerto 80, un puerto de servicios estándar de Internet que la mayoría de los firewalls dejan abierto. Sin embargo, el puerto usado por WinRM y el mecanismo de transporte predeterminado, HTTP, se puede cambiar como se requiera.

En la columna de este mes, tampoco trataremos la instalación y la configuración de WinRM. Ya hay mucha información disponible al respecto: msdn2.microsoft.com/aa384372. Sin embargo, nos tomaremos un momento para remarcar un punto importante: si quiere usar WinRM para recuperar información de un equipo remoto (que, por supuesto, es la razón principal para usar WinRM en primer lugar), su equipo local y el equipo remoto deben ejecutar WinRM.

¿Qué significa eso? Bien, significa que si no ha actualizado sus equipos cliente a Windows Vista (¡díganos que no es cierto!) o, si no ha actualizado sus servidores a Windows Server 2003 R2 o a Windows Server 2008, no encontrará WinRM especialmente útil, por lo menos en la actualidad. No obstante, no hace falta decir que este probablemente no será el caso mañana. Y, por supuesto, si damos por hecho que su firewall lo permite, siempre puede usar WMI y DCOM para administrar equipos remotos.

Devolución de todas las propiedades y las instancias de una clase

A quién le importan todas esas advertencias y avisos, ¿no le parece? En vez de preocuparnos de todas esas paparruchas, veamos si podemos averiguar cómo escribir un script que aproveche WinRM. Casualmente, resulta que tenemos un script muy sencillo que, por medio del protocolo HTTP y el puerto 80, conecta a un equipo denominado atl-fs-01.fabrikam.com y, seguidamente, devuelve información completa sobre todos los servicios instalados en el equipo. Consulte la figura 1 para ver el script en todo su esplendor.

Figure 1 Componentes de proyecto de Silverlight

Archivo Descripción
CreateSilverlight J Se trata de un script JScript (en su versión inicial, Silverlight admite sólo scripting JScript) que se usa para especificar la configuración de inicio de Silverlight, incluido el archivo XAML usado para configurar objetos de interfaz de usuario y gráficos.
SampleProject.js Se trata simplemente de un archivo vacío donde puede especificar funciones JScript.
Silverlight.js Este archivo se usa para inicializar el control de Silverlight.
SampleProject.html Esta es la parte más emocionante. SampleProject.html es simplemente un archivo HTML que incluye código para la lectura de los tres archivos .js. También incluye código para crear una instancia del control de Silverlight.
SampleProject.xaml ¿Para qué sirve esto? Para averiguarlo, tendrá que regresar al texto principal de este artículo.
   

Figure 1 Enumeración de servicios en un equipo remoto

strComputer = "atl-fs-01.fabrikam.com"

Set objWRM = CreateObject("WSMan.Automation")
Set objSession = objWRM.CreateSession("http://" & strComputer)

strResource = "https://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Service"

Set objResponse = objSession.Enumerate(strResource)

Do Until objResponse.AtEndOfStream
    DisplayOutput(objResponse.ReadItem)
Loop

Sub DisplayOutput(strWinRMXml)
    Set xmlFile = CreateObject("MSXml2.DOMDocument.3.0")    
    Set xslFile = CreateObject("MSXml2.DOMDocument.3.0")
    xmlFile.LoadXml(strWinRMXml)
    xslFile.Load("WsmTxt.xsl")
    Wscript.Echo xmlFile.TransformNode(xslFile)
End Sub

Como puede ver, empezamos por asignar el nombre DNS del equipo (atl-fs-01.fabrikam.com) a una variable llamada strComputer. Alternativamente, podríamos realizar la conexión con la dirección IP del equipo (o incluso con la dirección IPv6). Por ejemplo:

strComputer = "192.168.1.1"

Después de asignar un valor a strComputer, creamos una instancia del objeto WSMan.Automation. Luego, llamamos al método CreateSession para conectar al equipo remoto, en este caso, mediante el protocolo HTTP (justo como dijimos que íbamos a hacer):

Set objSession = objWRM.CreateSession _
    ("http://" & strComputer)

Como mencionamos, queremos devolver información acerca de los servicios instalados en el equipo remoto. Además y, por lo menos para este primer ejemplo, queremos información acerca de todas las propiedades de todos los servicios. ¿Qué significa todo esto? Esto significa que necesitamos especificar una cadena de recurso URI que enlace a la clase Win32_Service en el equipo remoto:

strResource = _
  "https://schemas.microsoft.com" & _
  "/wbem/wsman/1/wmi/root/cimv2" & _
  "/Win32_Service"

Ya, este no es el URI más bonito jamás visto. (Aunque, ahora que lo pienso, creo que nunca se ha visto un URI bonito.) Pero, afortunadamente, la mayor parte del URI es cosa hecha; sólo necesita preocuparse de la ruta WMI al final:

https://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Service

Esto debería ser bastante sencillo. ¿Qué sucede si desea conectar a la clase root/cimv2/Win32_Process? Pues sólo tiene que modificar la ruta URI en consecuencia:

https://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process

¿Que le interesa la clase root/default/SystemRestore? Bien, una vez más, modifique la clase URI, con cuidado de especificar el espacio de nombres predeterminado (en lugar del espacio de nombres cimv2):

https://schemas.microsoft.com/wbem/wsman/1/wmi/root/default/SystemRestore

Etc... Es una pena que también necesite incluir la parte https://schemas.microsoft.com/wbem/wsman/1/wmi del URI, pero...

Llegados a este punto, estamos listos para recibir algunos datos. Para ello, simplemente llamamos al método Enumerate, con el paso de la variable strResource como único parámetro del método:

Set objResponse = _
  objSession.Enumerate(strResource)

¿Rellenará esta línea de código objResponse con información acerca de los servicios instalados en el equipo atl-fs-01? Seguro que sí. Sin embargo, a diferencia de lo que ocurre con scripting WMI estándar, no recibirá una serie de objetos, cada uno con sus propiedades y métodos de propiedad. En su lugar, recibirá un blob XML enorme que se parece a lo que puede ver en la figura 2.

Figure 2 Colores elegantes

<Canvas
 xmlns="https://schemas.microsoft.com/client/2007"
 xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
 Width="800"
 Height="300">
 
 <Canvas.Background>
 <LinearGradientBrush>
 <GradientStop Color="Blue" Offset="0.0" />
 <GradientStop Color="Black" Offset="1.0" />
 </LinearGradientBrush>
 </Canvas.Background>

</Canvas>

Figure 2 Blob XML enorme

<p:Win32_Service xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="
https://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Service" xmlns:ci
m="http://schemas.dmtf.org/wbem/wscim/1/common" xsi:type="p:Win32_Service_Type"
xml:lang="en-US"><p:AcceptPause>false</p:AcceptPause><p:AcceptStop>false</p:Acce
ptStop><p:Caption>Windows Media Center Service Launcher</p:Caption><p:CheckPoint
>0</p:CheckPoint><p:CreationClassName>Win32_Service</p:CreationClassName><p:Desc
ription>Starts Windows Media Center Scheduler and Windows Media Center Receiver
services at startup if TV is enabled within Windows Media Center.</p:Description
><p:DesktopInteract>false</p:DesktopInteract><p:DisplayName>Windows Media Center

Si es todo un experto en XML, esto no supondrá problema alguno; cualquier usuario familiarizado con XML debería ser capaz de analizar y producir esta información sin demasiadas complicaciones (aunque, en palabras del SDK de WinRM, esta información no está en formato de lectura humana). Pero, ¿qué sucede si no es un experto en XML? En este caso, tiene dos opciones. Una, puede esperar hasta el mes próximo, cuando le enseñaremos algunos trucos para trabajar con XML de WinRM. O dos, puede hacer lo que hicimos nosotros en nuestro script de ejemplo: usar la transformación XSL que se instala junto con WinRM.

¿La qué?

Una transformación XSL no es nada más que una plantilla que describe cómo se debe mostrar un archivo XML. Una discusión completa sobre archivos XSL va más allá de lo que podemos hacer en la columna de este mes, bueno, incluso una descripción rápida de los archivos XSL va más allá de lo que tenemos capacidad para hacer en la columna de este mes. Por lo tanto, no intentaremos explicar cómo WsmTxt.xsl (el nombre de la transformación integrada) funciona realmente. En su lugar, simplemente le mostraremos cómo puede usar esta transformación en su script.

Al llamar al método Enumerate, WinRM devuelve un script XML. La manera más fácil de trabajar con estos datos es establecer un bucle Do Until que continúe ejecutándose hasta que alcance el final del script. Eso es lo que hacemos aquí:

Do Until objResponse.AtEndOfStream
    DisplayOutput(objResponse.ReadItem)
Loop

Como puede ver, dentro de nuestro bucle, llamamos a una subrutina denominada DisplayOutput. Cuando llamamos a esta subrutina, pasamos el valor del método ReadItem de la secuencia como parámetro de la subrutina. Como sugiere este enfoque, la secuencia XML se devuelve en trozos, no como un blob de datos enorme. Nuestro script, a su vez, lee los datos XML, un trozo, o un elemento, cada vez.

Mientras tanto, la subrutina DisplayOutput tiene este aspecto:

Sub DisplayOutput(strWinRMXml)
  Set xmlFile = _
    CreateObject("MSXml2.DOMDocument.3.0")    
  Set xslFile = _
    CreateObject("MSXml2.DOMDocument.3.0")
  xmlFile.LoadXml(strWinRMXml)
  xslFile.Load("WsmTxt.xsl")
  Wscript.Echo xmlFile.TransformNode(xslFile)
End Sub

En pocas palabras, empezamos por crear dos instancias del objeto MSXml2.DOMDocument.3.0. Cargamos la secuencia de datos XML (strWinRMXML) en un objeto y, luego, cargamos el archivo XSL (WsmTxt.xsl) en el otro objeto. Ahora, llamamos al método TransformNode con el fin de usar la información en el archivo XSL para dar formato y mostrar los datos obtenidos de la secuencia XML.

Sí, es algo confuso. Pero por lo menos los resultados (aunque no son perfectos) son más fáciles de leer (consulte la figura 3).

Figure 3 Pintura de texto

<TextBlock 
 Name="Test"
 FontSize="40"
 FontFamily="Georgia"
 FontWeight="Bold"
 Canvas.Top="20" 
 Canvas.Left="20"
 Text="The TechNet Script Center">

 <TextBlock.Foreground>
 <SolidColorBrush Name="test_brush" Color="red"/>
 </TextBlock.Foreground>

</TextBlock>

Figure 3 Una versión más limpia de XML

Win32_Service
    AcceptPause = false
    AcceptStop = true
    Caption = User Profile Service
    CheckPoint = 0
    CreationClassName = Win32_Service
    Description = This service is responsible for loading and unloading user profiles. If this service is stopped or disabled, users will no longer be able to successfully logon or logoff, applications may have problems getting to users' data, and components registered to receive profile event notifications will not receive them.

Como ya hemos dicho, esto está muy bien, pero no es necesariamente excelente. Esta es una razón más para regresar el mes próximo, cuando le enseñaremos algunas maneras de manipular los resultados XML.

Devolución de las propiedades y las instancias seleccionadas de una clase

Es obvio que todo esto está muy bien, pero... quizás no refleje del todo su modo de hacer las cosas. Sí, habrá veces en que deseará devolver todas las propiedades de todas las instancias de una clase; sin embargo, también habrá veces (quizás muchas más veces) en que deseará devolver sólo propiedades o instancias seleccionadas de una clase. Por ejemplo, quizás quiera devolver sólo información acerca de los servicios en ejecución, algo que se consigue con un script WMI normal mediante el uso de código semejante a este:

Set colItems = objWMIService.ExecQuery _
  ("Select * From Win32_Service " & _
   "Where State = 'Running'")

Muy bien. Pero, ¿se puede saber cómo modificará su cadena de recurso para conseguir la equivalencia con esto?

Bueno, para ser honestos, no modificará su cadena de recurso para hacerla equivalente a la instrucción ExecQuery. Definitivamente necesitará modificar la cadena de recurso, pero también tendrá que hacer otras cosas.

Con esto en mente, echemos un vistazo a la figura 4. Este es un script WinRM que devuelve información acerca de los servicios en ejecución en un equipo (no de los servicios instalados en el equipo).

Figure 4 Trucos de TextBlock

<TextBlock.Triggers>
 <EventTrigger RoutedEvent=
     "TextBlock.Loaded">
 <BeginStoryboard>
 <Storyboard>
 <DoubleAnimation
 Storyboard.TargetName="Test"
 Storyboard.TargetProperty="Opacity"
 From="0.0" To="1.0" 
 Duration="0:0:5" />
 </Storyboard>
 </BeginStoryboard>
 </EventTrigger>
</TextBlock.Triggers>

Figure 4 Búsqueda de servicios en ejecución

strComputer = "atl-fs-01.fabrikam.com"

Set objWRM = CreateObject("WSMan.Automation")
Set objSession = objWRM.CreateSession("http://" & strComputer)

strResource = "https://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/*"
strFilter = "Select * From Win32_Service Where State = 'Running'"
strDialect = "https://schemas.microsoft.com/wbem/wsman/1/WQL"

Set objResponse = objSession.Enumerate(strResource, strFilter, strDialect)

Do Until objResponse.AtEndOfStream
    DisplayOutput(objResponse.ReadItem)
Loop

Sub DisplayOutput(strWinRMXml)
    Set xmlFile = CreateObject("MSXml2.DOMDocument.3.0")    
    Set xslFile = CreateObject("MSXml2.DOMDocument.3.0")
    xmlFile.LoadXml(strWinRMXml)
    xslFile.Load("WsmTxt.xsl")
    Wscript.Echo xmlFile.TransformNode(xslFile)
End Sub

A primera vista, podría parecer idéntica al primer script WinRM que le mostramos; sin embargo, hay algunas diferencias muy importantes. Para empezar, mire el valor que asignamos a la cadena de recurso:

strResource = _
  "https://schemas.microsoft.com" & _
  "/wbem/wsman/1/wmi/root/cimv2/*"

Fíjese que, al escribir una consulta filtrada, no especificamos el nombre verdadero de la clase (Win32_Service) con la que queremos trabajar; en su lugar, conectamos simplemente al espacio de nombres (root/cimv2) donde reside esta clase. Recuerde colocar el asterisco (*) al final. Si no lo hace, el script devolverá el mensaje de error "... el nombre de la clase debe ser '*' (asterisco)", lo que significa que necesita dar a la clase el nombre *.

Además, necesitamos definir un filtro y un dialecto:

strFilter = _
  "Select * From Win32_Service " & _
  "Where State = 'Running'"
strDialect = _
  "https://schemas.microsoft.com" & _
  "/wbem/wsman/1/WQL"

El filtro debe ser fácil de resolver; aquí es donde colocamos nuestra consulta WQL ("Select * From Win32_Service Where State = 'Running'"). El dialecto, entre tanto, es el lenguaje de consulta usado al crear el filtro. Actualmente, sólo hay un lenguaje de consulta permitido: WQL. No obstante, se debe especificar el dialecto, o el script devolverá un mensaje de error para indicar que el dialecto del filtro no es compatible.

Nota: es interesante que el mensaje de error sugiere que quite el dialecto al llamar al método Enumerate. Esta es una recomendación que no debe seguir. Cuando se realiza una consulta filtrada, es necesario especificar el dialecto, que debe ser WQL. Punto.

El otro cambio que hay que hacer se da al llamar al método Enumerate. En este momento, necesitamos pasar las variables que representan el filtro (strFilter) y el dialecto (strDialect), así como la variable que representa el recurso (strResource):

Set objResponse = _
  objSession.Enumerate _
  (strResource, strFilter, strDialect)

Inténtelo y verá lo que sucede.

Y ahora, ¿qué tal si devolvemos sólo las propiedades seleccionadas de una clase? Por ejemplo, suponga que le interesa devolver sólo las propiedades Name y DisplayName de todos los servicios en ejecución en un equipo. ¿Qué hacemos en este caso?

Bien, en un caso como este, puede tratar de manipular el XML para que sólo se muestren las propiedades Name y DisplayName. Eso es posible, pero también es un poco complicado. Un método más fácil es especificar dichas propiedades al asignar el filtro:

strFilter = _
  "Select Name, DisplayName " & _
  "From Win32_Service " & _
  "Where State = 'Running'"

Con esto, conseguirá las propiedades Name y DisplayName de cada servicio, así:

XmlFragment
    DisplayName = Windows Event Log
    Name = EventLog

XmlFragment
    DisplayName = COM+ Event System
    Name = EventSystem

Ya, el formato es un poco engorroso. ¿Qué es eso de XmlFragment? Otra razón más para regresar el mes próximo.

Espere y verá.

Con un poco de suerte, esto debería ser suficiente para introducirse en el salvaje y maravilloso mundo de WinRM. Por supuesto, no podríamos terminar una columna sobre WinRM sin mencionar las "morgues de espera" que solía haber por todas partes en Alemania. En ciudades con morgues de espera, los cadáveres no se enterraban inmediatamente. En su lugar, los colocaban en salas bien calentitas y les conectaban varias cuerdas y cables a los dedos. Por supuesto, la idea era que el movimiento más leve activaría una alarma para pedir ayuda. Los cuerpos permanecían en estas morgues de espera hasta que era evidente que estas personas ya no tenían remedio ni esperanza de hacer nada nunca más.

Ahora que lo pienso, una morgue de espera es muy semejante a formar parte del equipo de encargados del scripting, ¿no? Claro que nadie regresó jamás a la vida en ninguna morgue de espera, mientras que en el equipo de encargados del scripting...

Dr. Scripto's Scripting Perplexer

En junio de 2007, los encargados del scripting asistieron a la conferencia Tech•Ed 2007 en Orlando, Florida. Como asistir a la conferencia no fue suficiente, decidimos divertirnos un poco. No sólo eso, sino que pensamos que todo el mundo debía divertirse un poco. Así que inventamos Dr. Scripto's Fun Book, un librillo lleno de rompecabezas de scripting y otras cosas. Nos unimos a TechNet Magazine (es decir, les convencimos para que nos cedieran una esquinita de su espacio en la conferencia) y empezamos a distribuir estos librillos a todo el que pasaba.

Resultó que el librillo fue un artículo bastante popular (quizá no tan popular como las muñecas de Dr. Scripto, pero casi). Los oportunistas de TechNet Magazine vieron de inmediato el modo en que podían aprovechar el éxito de los encargados del scripting (ya que, al parecer, los encargados del scripting no son capaces de aprovechar nunca su propio éxito), y nos pidieron que creáramos algunos rompecabezas para ellos. Cuando Jean Ross, se dio la vuelta sólo un minuto, Greg Stemp dijo "¡Desde luego, lo haremos!" Y aquí estamos: Dr. Scripto's Scripting Perplexer. Que disfrute.

Scripting de orden

En este rompecabezas, todas las letras de la sección superior se pueden ordenar para crear un script (en VBScript). Pero no se preocupe, no tiene que ordenarlo todo a la vez; basta con ordenar una columna cada vez. Las letras en cada columna de la sección superior sirven para rellenar los espacios en blanco en la misma columna de la sección inferior. Por ejemplo:

Como puede ver, en la columna 1, tenemos las letras S, C y T. Estas tres letras pertenecen a la cuadrícula inferior en un orden desconocido. Cuando todas las letras quedan colocadas en el orden apropiado, la cuadrícula inferior se convierte en algo lógico que se puede leer de izquierda a derecha. Eche un vistazo a la solución:

Puede ver que las letras S, C y T en la columna 1 se colocan abajo en el orden T, S y C. Estas resultan ser las primeras letras de cada palabra en "The Script Center". El verdadero rompecabezas es un poco más difícil porque, bueno, es más largo y porque el resultado final es un script completo.

Pista: el script final empieza con una ruta de acceso completa a un archivo, luego se analiza y muestra sólo el nombre del archivo.

¡Buena suerte!

ANSWER:

Dr. Scripto's Scripting Perplexer

Respuesta: Scripting de orden, noviembre de 2007

En este rompecabezas, necesita ordenar los caracteres de una columna en los cuadros correctos más abajo para que las filas inferiores creen un script. Aquí tiene el script independiente:

name = "C:\Scripts\Test.txt"
arr = Split(name, "\")
index = Ubound(arr)
Wscript.Echo "Filename: " _
& arr(index)
        

Este es su aspecto en la cuadrícula del rompecabezas:

Los encargados del scripting 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.