¡ Hola, chicos del scripting! Trabajar con bases de datos de Access en Windows PowerShell

The Microsoft Scripting Guys

las bases de datos son desconcertante fragmentos de software. En su forma más sencilla son nada más que los contenedores de almacenamiento para almacenar información. La magia real empieza por la aplicación de esta información almacenada. Por supuesto, la mayor parte perfectamente diseñado base de datos sin los registros se nada más que un ejercicio académico. Es los datos que hace la base de datos. Siempre que oímos sobre alguien que tenga una base de datos grande, las personas reaccionan con awe. No debido a la base de datos, pero para los datos que contiene.

Todos los datos ¿cómo obtener en una base de datos? Entrada manual de los datos en una base de datos es para los pájaros, y fue fuera con tarjetas de perforación claves. Para crear una base de datos lo suficientemente grande para impresión tus amigos y proporcionar el potencial para desbloquear los Misterios de la red, debe automatizar. Hoy en día, esto significa que Windows PowerShell, y en este artículo, eso es lo que utilizaremos para recopilar algunos datos sobre el equipo local y escribirlo a una base de datos de Access de Office denominado ComputerData.mdb. Esta base de datos puede crearse manualmente, o puede utilizar la secuencia de comandos que se encuentra en el artículo" ¿Cómo puedo crear una base de datos con más de una tabla?." Se llamará a nuestra secuencia de comandos WriteToAccessDatabase.ps1 para que sepan lo que hace.

Empezaremos mediante la creación de la función de comprobación de ruta de acceso, que se utilizará para asegurarse de que existe la base de datos. Para crear la función, se utiliza la palabra clave (función), asigne un nombre a la función y definir las variables de entrada que necesite para recibir. Lo primero que hace de comprobación de ruta de acceso es usar el cmdlet Test-ruta de acceso para ver si existe el directorio de la ruta de acceso base de datos. Para ello, se utiliza el cmdlet de ruta de acceso de división para dividir la ruta de acceso en una parte principal y una parte secundaria. Sólo se necesita la parte de principal de la ruta de acceso para comprobar la existencia del directorio. Éste es cómo utilizamos división de ruta de acceso para recuperar la ruta de acceso primaria:

PS C:\> Split-Path C:\fso\ComputerData.mdb -Parent
C:\fso

En lugar de comprobar la presencia de la ruta de acceso, se utiliza el operador Not (!) para buscar su ausencia. Si la carpeta no existe, se utiliza la palabra clave throw para elevar un error:

Function Check-Path($Db)
{
 If(!(Test-Path -path (Split-Path -path $Db -parent)))
   { 
     Throw "$(Split-Path -path $Db -parent) Does not Exist" 
   }

Incluso si la carpeta existe, el archivo de base de datos podría ser que falta. Se utiliza la palabra clave ELSE para introducir esa condición alternativa. Una vez más, se utiliza la instrucción IF para buscar la presencia del archivo de base de datos y la palabra clave throw para elevar un error si no existe:

  ELSE
  { 
   If(!(Test-Path -Path $Db))
     {
      Throw "$db does not exist"
     }
  }
} #End Check-Path

Realmente no necesitamos usar la construcción IF…ELSE para comprobar la existencia de la base de datos. Una simple llamada al cmdlet Test-ruta de acceso mediante el parámetro –path podría trabajar. Sin embargo, el uso IF…ELSE proporciona un mayor nivel de comentarios. ¿Queremos saber si existe el directorio y, si es así, existe el archivo? Es posible ciertamente, la base de datos es posible que falte en la carpeta, pero también es posible que la carpeta sí podría ser que falta. Esto proporciona a los comentarios más granular y puede ayudar a solucionar problemas.

Cuando se ha asegurado de que la base de datos existe, se cree la función Get-BIOS para obtener la información del BIOS de la clase WMI Win32_Bios. Ésta es la función de Get-BIOS:

Function Get-Bios
{
 Get-WmiObject -Class Win32_Bios
} #End Get-Bios

Al encapsular la llamada WMI en una función, obtener la capacidad de cambiar fácilmente la función, como agregar capacidad remota o Aceptar credenciales. La modificación se ha podido realizar aquí sin afectar el resto de la secuencia de comandos. De hecho, desde una perspectiva de las pruebas, si no funciona, simplemente comentario el código de la función y la secuencia de comandos restante sigue funcionando. Para obtener ayuda de buscar información relacionada con las clases WMI, puede utilizar el Windows PowerShell Scriptomaticse muestra en la figura 1 . Esta herramienta le permite explorar fácilmente espacios de nombres de WMI y clases y incluso crea la secuencia de comandos de Windows PowerShell para recuperar la información.

fig01.gif

Figura 1 versión de Windows PowerShell de la utilidad Scriptomatic

A continuación, vamos a crear la función de Get-vídeo para recuperar información de vídeo de la clase Win32_VideoController WMI. Como puede observar, esta función es similar a la función de Get-BIOS:

Function Get-Video
{
 Get-WmiObject -Class Win32_VideoController
} #End Get-Video

Ahora debemos realizar una conexión a la base de datos. Para ello, utilizamos la función de base de datos de conectar. Creamos dos parámetros de entrada para la función de conectar de base de datos: –DB y –Tables cuyos valores se almacenan en el DB $ y las variables de tablas de $ dentro de la función. Lo primero que se dentro de la base de datos para conectar función es asignar valores a un par de variables que se utilizan para controlar el modo en que el RecordSet se abre. El método Open del objeto RecordSet puede aceptar hasta cinco parámetros diferentes, los siguientes:

RecordSet.Open Source, ActiveConnection, CursorType, LockType, Options 

El primero es el parámetro de origen, que se evalúa como un objeto command válido, una instrucción SQL, un nombre de tabla, una llamada a un procedimiento almacenado, una dirección URL o el nombre de un objeto de archivo o la secuencia que contiene un Recordset almacenado persistentemente. El segundo parámetro es el ActiveConnection, una cadena que se evalúa como un objeto de conexión válida o una cadena que contiene los parámetros connectionstring. El parámetro de valor de CursorType se utiliza para determinar el tipo de cursor que se utilizará al abrir el RecordSet. Valores permitidos para el tipo de cursor se muestran en la figura 2 .

La Figura 2 ADO cursor tipo de enumeración constantes y valores
Constante Valor Descripción
valor adOpenDynamic 2 Utiliza un cursor dinámico. Las adiciones, cambios y eliminaciones realizadas por otros usuarios son visibles y se permiten todos los tipos de movimiento en el objeto Recordset, excepto para marcadores, si el proveedor no admite.
valor adOpenForwardOnly 0 De forma predeterminada. Utiliza un cursor de sólo avance. Idéntico a un cursor estático, excepto que sólo se puede desplazar por los registros hacia delante. Este cursor permite mejorar el rendimiento cuando se necesita realizar un sólo paso a través de un conjunto de registros.
valor adOpenKeyset 1 Utiliza un cursor estático de claves. Como un cursor dinámico, excepto que no se puede ver los registros agregan por otros usuarios y los registros que otros usuarios son inaccesibles desde el Recordset. Cambios de datos por otros usuarios son visibles todavía.
valor adOpenStatic 3 Utiliza un cursor estático, que es una copia estática de un conjunto de registros que puede utilizar para buscar datos o generar informes. Las adiciones, modificaciones o eliminaciones realizadas por otros usuarios no están visibles.
adOpenUnspecified -1 No se especifica el tipo de cursor.

El parámetro LockType se utiliza para regir el tipo de bloqueo que se va a utilizar al actualizar los registros, y el parámetro options se utiliza para indicar al proveedor de cómo evaluar el parámetro de origen. Los valores permitidos para el parámetro LockType se muestran en la figura 3 .

La figura 3 bloquear ADO tipo de enumeración constantes y valores
Constante Valor Descripción
AdLockBatchOptimistic 4 Indica actualizaciones por lotes " optimista ". Se requiere para modo de actualización por lotes.
AdLockOptimistic 3 Indica bloqueo optimista, registro por registro. El proveedor utiliza bloqueo optimista, que bloquea los registros sólo cuando se llama al método Update.
AdLockPessimistic 2 Indica bloqueo pesimista, registro por registro. El proveedor realiza lo necesarias para garantizar la correcta modificación de los registros, normalmente bloqueando los registros en el origen de datos inmediatamente después de editar.
adLockReadOnly 1 Indica registros de sólo lectura. No se puede modificar los datos.
adLockUnspecified -1 No se especifica un tipo de bloqueo. Para duplicados, el duplicado se crea con el mismo tipo de bloqueo que el original.

Todos los cinco de los parámetros para el método Open del objeto RecordSet son opcional; por lo general, utilizamos sólo la primera cuatro. Después de se han asignado valores para la enumeración de cursor y el tipo de bloqueo, se utiliza el cmdlet New-Object para crear un nuevo objeto ADODB.Connection que se almacenan en la variable de conexión $. A continuación, utilizamos el método Open desde el objeto Connection, que necesita el nombre del proveedor y el origen de datos. A continuación, se llame a la función de actualización de registros y pase la variable de las tablas de $. Ésta es la función conectar-DataBase:

Function Connect-Database($Db, $Tables)
{
  $OpenStatic = 3
  $LockOptimistic = 3
  $connection = New-Object -ComObject ADODB.Connection
  $connection.Open("Provider = Microsoft.Jet.OLEDB.4.0;Data Source=$Db" )
  Update-Records($Tables)
} #End Connect-DataBase

En función de la actualización de registros, lo primero que hacemos es crear una instancia del objeto ADODB.RecordSet. Se utiliza el cmdlet New-Object para hacer esto y almacenar el objeto RecordSet recién creado en la variable de RecordSet $. A continuación, utilizamos la para cada instrucción para recorrer la matriz de tablas. Los nombres de tabla se almacenan en la variable de las tablas de $ y se asignan al principio de la secuencia de comandos. Dentro del bucle ForEach, primero creamos nuestra consulta, que es una selección bastante genérico * de tabla $. La ventaja de utilizar una variable para el nombre de tabla es que sólo debemos escribir el código una vez; el nombre de tabla en la consulta obtiene cambiado cada vez que recorremos la matriz de nombres de tabla.

Ahora hemos llegado al método del objeto RecordSet abierto. Se especifica la consulta que se almacena en la variable de consulta $, el objeto de conexión en la variable de conexión $, el valor de OpenStatic $ y el valor de LockOptimistic de $ para controlar la forma que se abre el RecordSet. Se usa a continuación, el cmdlet de Invoke de expresiones para ejecutar el valor de una cadena. Hacemos esto porque hemos creado dos funciones que están diseñadas para actualizar las tablas de base de datos diferente. Hemos denominado las funciones después de las tablas que actualizan. No se está autorizados para llamar a un nombre de función cuando la mitad de es una variable, por lo que necesita resolver la variable y, a continuación, llamar a la función.

Pero que no funciona bien, al menos no directamente. Lo que queremos hacer es tratar el nombre de la función como si fuese una cadena y no un comando. Pero desea ejecutarlo como un comando. Para ello, se utilice Invoke de expresiones. Este cmdlet llama a cada una de las funciones de actualización diferente. Dentro del bucle que pasa a través de la matriz de nombres de tabla, se cierre cada uno de los objetos RecordSet, a continuación, volver al siguiente elemento de la matriz de nombres de tabla, cree una nueva consulta, abrir un nuevo objeto RecordSet y llamar a una función nueva. Esto continúa para cada uno de la tabla de nombres de la matriz de tablas, como para:

Function Update-Records($Tables)
{
  $RecordSet = new-object -ComObject ADODB.Recordset
   ForEach($Table in $Tables)
     {
      $Query = "Select * from $Table"
      $RecordSet.Open($Query, $Connection, $OpenStatic, $LockOptimistic)
      Invoke-Expression "Update-$Table"
      $RecordSet.Close()
     }

Después se actualizan los registros, se puede cerrar la conexión. Para ello, utilizamos el método Close del objeto Connection:

   $connection.Close()
} #End Update-Records

La función de actualización de registros llama a dos funciones de soporte técnico, actualización de BIOS y Update-vídeo, que está diseñado para actualizar los campos correspondientes de la tabla base de datos correspondientes. Si desea agregar tablas adicionales a la base de datos, deberá agregar una función de actualización * adicional para actualizar las nuevas tablas. Como práctica recomendada, se recomienda mantener los nombres de campo de base de datos de la misma que los nombres de las propiedades de WMI. Facilita las cosas mucho realizar un seguimiento de. Al escribir una secuencia de comandos para actualizar una base de datos existente, quizás desee observar el base de datos de esquema de la tablas, columnas y tipos de datos contenidos en los campos. El esquema de base de datos para la base de datos ComputerData se muestra en la figura 4 . Esta vista generó la secuencia de comandos en el artículo" Cómo puedo saber qué tablas y columnas están en una base de datos sin abrir se?"

fig04.gif

La figura 4 el esquema de base de datos de la base de datos ComputerData

En la función de actualización de BIOS, nos primero publicar un mensaje indicando que está actualizando la información del BIOS. A continuación, se llame a la función Get-BIOS y almacenar el objeto WMI Win32_Bios devuelto en la variable BiosInfo $. Ahora debemos agregar un registro a la tabla de la base de datos. Para ello, se llama el método AddNew desde el objeto RecordSet. Después de que tengamos un nuevo registro, agregar información a cada uno de los campos de la tabla. Cuando se han actualiza todos los campos, llamamos al método Update para confirmar el registro en la tabla. La función de actualización de BIOS completa se muestra aquí:

Function Update-Bios
{
 "Updating Bios"
 $BiosInfo = Get-Bios
 $RecordSet.AddNew()
 $RecordSet.Fields.Item("DateRun") = Get-Date
 $RecordSet.Fields.Item("Manufacturer") = $BiosInfo.Manufacturer
 $RecordSet.Fields.Item("SerialNumber") = $BiosInfo.SerialNumber
 $RecordSet.Fields.Item("SMBIOSBIOSVersion") = $BiosInfo.SMBIOSBIOSVersion
 $RecordSet.Fields.Item("Version") = $BiosInfo.Version
 $RecordSet.Update()
} #End Update-Bios

Cuando se ha actualiza la tabla de BIOS, tenemos que actualizar la tabla de vídeo. Para ello, se puede llamar a la función actualización de vídeo, que es exactamente el mismo que la función de actualización de BIOS. Se presenta un mensaje indicando que está actualizando el vídeo, llame a la función de Get-vídeo para recuperar la información de vídeo, llamar al método AddNew para agregar un nuevo registro a la tabla de vídeo y escribir toda la información en los campos correspondientes. Cuando hemos terminados, llamamos al método Update.

Un posible problema en recopilar la información de vídeo es el número de controladores de vídeo en el equipo. Mi equipo personal tiene una tarjeta hija y se informa de varios controladores de vídeo. Para controlar esta posibilidad, utilizamos la instrucción ForEach para recorrer en iteración una colección de Win32_VideoControllers. Si no está interesado en la información de configuración de la tarjeta hija o si la tarjeta de vídeo es canal dual y informa el mismo dos veces, la que puede quitar el bucle ForEach y seleccione $ VideoInfo [0] al índice directamente en el primer registro que se notifica. El problema con este enfoque es que si la consulta devuelve un objeto singleton, se generará un error porque se no se puede indizar en un solo registro:

Function Update-Video
{ "Updating video" $VideoInformation = Get-Video 
Foreach($VideoInfo in $VideoInformation)  
  {
   $RecordSet.AddNew()   $RecordSet.Fields.Item("DateRun") = Get-Date
   $RecordSet.Fields.Item("AdapterCompatibility") = $VideoInfo.AdapterCompatibility
   $RecordSet.Fields.Item("AdapterDACType") = $VideoInfo.AdapterDACType
   $RecordSet.Fields.Item("AdapterRAM") = $VideoInfo.AdapterRAM
   $RecordSet.Fields.Item("Description") = $VideoInfo.Description
   $RecordSet.Fields.Item("DriverDate") = $VideoInfo.DriverDate
   $RecordSet.Fields.Item("DriverVersion") = $VideoInfo.DriverVersion
   $RecordSet.Update()
  }
} 
#End Update-Video

El punto de entrada a la secuencia de comandos apunta a la base de datos, recoja las tablas y llama en a, a continuación, la función DataBase de conectar, tal como se muestra aquí:

$Db = "C:\FSO\ComputerData.mdb"+
$Tables = "Bios","Video"
Check-Path -db $Db
Connect-DataBase -db $Db -tables $Tables

Después de ejecuta la secuencia de comandos, los registros nuevos se escriben en la base de datos ComputerData.mdb tal como se muestra en la figura 5 . La secuencia de comandos WriteToAccessDatabase.ps1 completa puede verse en la figura 6 .

fig05.gif

La figura 5 nuevos los registros agregados a la base de datos ComputerData.mdb

Figura 6 WriteToAccessDataBase.ps1

Function Check-Path($Db)
{
 If(!(Test-Path -path (Split-Path -path $Db -parent)))
   { 
     Throw "$(Split-Path -path $Db -parent) Does not Exist" 
   }
  ELSE
  { 
   If(!(Test-Path -Path $Db))
     {
      Throw "$db does not exist"
     }
  }
} #End Check-Path

Function Get-Bios
{
 Get-WmiObject -Class Win32_Bios
} #End Get-Bios

Function Get-Video
{
 Get-WmiObject -Class Win32_VideoController
} #End Get-Video

Function Connect-Database($Db, $Tables)
{
  $OpenStatic = 3
  $LockOptimistic = 3
  $connection = New-Object -ComObject ADODB.Connection
  $connection.Open("Provider = Microsoft.Jet.OLEDB.4.0;Data Source=$Db" )
  Update-Records($Tables)
} #End Connect-DataBase

Function Update-Records($Tables)
{
  $RecordSet = new-object -ComObject ADODB.Recordset
   ForEach($Table in $Tables)
     {
      $Query = "Select * from $Table"
      $RecordSet.Open($Query, $Connection, $OpenStatic, $LockOptimistic)
      Invoke-Expression "Update-$Table"
      $RecordSet.Close()
     }
   $connection.Close()
} #End Update-Records

Function Update-Bios
{
 "Updating Bios"
 $BiosInfo = Get-Bios

 $RecordSet.AddNew()
 $RecordSet.Fields.Item("DateRun") = Get-Date
 $RecordSet.Fields.Item("Manufacturer") = $BiosInfo.Manufacturer
 $RecordSet.Fields.Item("SerialNumber") = $BiosInfo.SerialNumber
 $RecordSet.Fields.Item("SMBIOSBIOSVersion") = $BiosInfo.SMBIOSBIOSVersion
 $RecordSet.Fields.Item("Version") = $BiosInfo.Version
 $RecordSet.Update()
} #End Update-Bios

Function Update-Video
{
 "Updating video"
 $VideoInformation = Get-Video
 Foreach($VideoInfo in $VideoInformation)
  { 
   $RecordSet.AddNew()
   $RecordSet.Fields.Item("DateRun") = Get-Date
   $RecordSet.Fields.Item("AdapterCompatibility") = $VideoInfo.AdapterCompatibility
   $RecordSet.Fields.Item("AdapterDACType") = $VideoInfo.AdapterDACType
   $RecordSet.Fields.Item("AdapterRAM") = $VideoInfo.AdapterRAM
   $RecordSet.Fields.Item("Description") = $VideoInfo.Description
   $RecordSet.Fields.Item("DriverDate") = $VideoInfo.DriverDate
   $RecordSet.Fields.Item("DriverVersion") = $VideoInfo.DriverVersion
   $RecordSet.Update()
  }
} #End Update-Video

# *** Entry Point to Script ***

$Db = "C:\FSO\ComputerData.mdb"
$Tables = "Bios","Video"
Check-Path -db $Db
Connect-DataBase -db $Db -tables $Tables

Si desea obtener más información sobre cómo trabajar con Office bases de datos Access de en Windows PowerShell, consulte la "Hey, Scripting Guy!" archivo de la semana de 20 de febrero de 2009. Además, el 2009 Juegos de Scripting de verano se próximamente! Visite scriptingguys.com Para obtener más información.

Ed Wilson , un experto de secuencias de comandos conocido, es el autor de ocho libros, entre ellos Windows PowerShell Scripting Guide (2008) y Microsoft Windows PowerShell Step by Step (2007). Ed contiene más de 20 certificaciones del sector, incluidos Microsoft Certified Systems Engineer (MCSE) y Certified Information Systems Security Professional (CISSP). En su tiempo libre, disfruta woodworking, fotografía underwater y scuba diving. Y té.

Craig Liebendorfer es wordsmith y longtime editor Web de Microsoft. Craig aún no se puede creer hay un trabajo que se paga a trabajar con las palabras cada día. Uno de sus cosas favoritas es irreverent humor, para que debe ajustarse derecha en aquí. Considera que su accomplishment mayor en la vida que su hija magnificent.