visual - ¿Capturar el valor de salida de un comando de shell en VBA?
vbnormalfocus (6)
Basándome en la respuesta de bburns.km , agregué información de paso (usando StdInput) al ejecutable durante la llamada. Por si acaso alguien se topa con esto y tiene la misma necesidad.
'''''' <summary>
'''''' Executes the given executable in a shell instance and returns the output produced
'''''' by it. If iStdInput is given, it is passed to the executable during execution.
'''''' Note: You must make sure to correctly enclose the executable path or any given
'''''' arguments in quotes (") if they contain spaces.
'''''' </summary>
'''''' <param name="iExecutablePath">
'''''' The full path to the executable (and its parameters). This string is passed to the
'''''' shell unaltered, so be sure to enclose it in quotes if it contains spaces.
'''''' </param>
'''''' <param name="iStdInput">
'''''' The (optional) input to pass to the executable. Default: Null
'''''' </param>
Public Function ExecuteAndReturnStdOutput(ByVal iExecutablePath As String, _
Optional ByVal iStdInput As String = vbNullString) _
As String
Dim strResult As String
Dim oShell As WshShell
Set oShell = New WshShell
Dim oExec As WshExec
Set oExec = oShell.Exec(iExecutablePath)
If iStdInput <> vbNullString Then
oExec.StdIn.Write iStdInput
oExec.StdIn.Close '' Close input stream to prevent deadlock
End If
strResult = oExec.StdOut.ReadAll
oExec.Terminate
ExecuteAndReturnStdOutput = strResult
End Function
Nota: Deberá agregar una referencia al
Windows Script Host Object Model
para que seWshShell
los tiposWshShell
yWshExec
.
(Para hacer esto, vaya a Extras -> Referencias en la barra de menú del IDE de VBA).
Encontré esta función http://www.cpearson.com/excel/ShellAndWait.aspx
Pero también tendría que capturar la salida de la salida de shell. ¿Alguna sugerencia de código?
De acuerdo con la respuesta de Andrew Lessard, aquí hay una función para ejecutar un comando y devolver la salida como una cadena:
Public Function ShellRun(sCmd As String) As String
''Run a shell command, returning the output as a string
Dim oShell As Object
Set oShell = CreateObject("WScript.Shell")
''run command
Dim oExec As Object
Dim oOutput As Object
Set oExec = oShell.Exec(sCmd)
Set oOutput = oExec.StdOut
''handle the results as they are written to and read from the StdOut object
Dim s As String
Dim sLine As String
While Not oOutput.AtEndOfStream
sLine = oOutput.ReadLine
If sLine <> "" Then s = s & sLine & vbCrLf
Wend
ShellRun = s
End Function
Uso:
MsgBox ShellRun("dir c:/")
Esta función proporciona una forma rápida de ejecutar un comando de línea de comandos, utilizando el objeto del portapapeles:
Captura de salida de línea de comandos:
Function getCmdlineOutput(cmd As String)
CreateObject("WScript.Shell").Run "cmd /c """ & cmd & "|clip""", 0, True ''output>clipbrd
With CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}") ''latebound clipbrd obj
.GetFromClipboard ''get cmdline output from clipboard
getCmdlineOutput = .GetText(1) ''return clipboard contents
End With
End Function
Ejemplo de uso:
Sub Demo1()
MsgBox getCmdlineOutput("w32tm /tz") ''returns the system Time Zone information
End Sub
Utiliza el comando WShell Run
porque opcionalmente permite la ejecución asíncrona, lo que significa que esperará a que el comando termine de ejecutarse antes de que continúe VBA, lo cual es importante cuando se trata del portapapeles.
También utiliza una utilidad de línea de comandos incorporada pero a menudo olvidada llamada clip.exe
, en este caso como un destino para la salida de línea de datos del canal .
La manipulación del portapapeles requiere una referencia a la biblioteca de Microsoft Forms 2.0 , que en este caso creé con una referencia de enlace tardío (que se ve diferente ya que MS Forms, también fm20.dll
como fm20.dll
) es una biblioteca de Windows, no VBA.
Preservando los datos existentes del portapapeles:
En mi caso, fue un problema que la función anterior borra los datos existentes del portapapeles, por lo que la siguiente función se modifica para conservar y reemplazar el texto existente en el portapapeles.
Si hay algo más que texto en el portapapeles, se le avisará de que se perderá. Algunos códigos pesados podrían permitir que se devuelva otro / cualquier tipo de datos del portapapeles ... pero la manipulación avanzada del portapapeles es mucho más compleja de lo que la mayoría de los usuarios se dan cuenta, y francamente no tengo la necesidad o el deseo de hacerlo. Más información here .
Tenga en cuenta que, en este método, MS Forms es Early-Bound pero podría cambiarse si lo desea. (Pero recuerde que como regla general, la vinculación tardía generalmente duplica el tiempo de procesamiento).
Function getCmdlineOutput2(cmd As String)
''requires Reference: C:/Windows/System32/FM20.DLL (MS Forms 2.0) [Early Bound]
Dim objClipboard As DataObject, strOrigClipbrd As Variant
Set objClipboard = New MSForms.DataObject ''create clipboard object
objClipboard.GetFromClipboard ''save existing clipboard text
If Not objClipboard.GetFormat(1) Then
MsgBox "Something other than text is on the clipboard.", 64, "Clipboard to be lost!"
Else
strOrigClipbrd = objClipboard.GetText(1)
End If
''shell to hidden commandline window, pipe output to clipboard, wait for finish
CreateObject("WScript.Shell").Run "cmd /c """ & cmd & "|clip""", 0, True
objClipboard.GetFromClipboard ''get cmdline output from clipboard
getCmdlineOutput2 = objClipboard.GetText(1) ''return clipboard contents
objClipboard.SetText strOrigClipbrd, 1 ''Restore original clipboard text
objClipboard.PutInClipboard
End Function
Ejemplo de uso:
Sub Demo2()
MsgBox getCmdlineOutput2("dir c:/") ''returns directory listing of C:/
End Sub
Siempre puede redirigir la salida de shell a un archivo y luego leer la salida del archivo.
Puede StdOut
y StdOut
la aplicación redirigiendo su StdOut
a una canalización, y luego leer esa tubería directamente; http://pastebin.com/CszKUpNS
dim resp as string
resp = redirect("cmd","/c dir")
resp = redirect("ipconfig","")
Sub StdOutTest()
Dim objShell As Object
Dim objWshScriptExec As Object
Dim objStdOut As Object
Dim rline As String
Dim strline As String
Set objShell = CreateObject("WScript.Shell")
Set objWshScriptExec = objShell.Exec("c:/temp/batfile.bat")
Set objStdOut = objWshScriptExec.StdOut
While Not objStdOut.AtEndOfStream
rline = objStdOut.ReadLine
If rline <> "" Then strline = strline & vbCrLf & CStr(Now) & ":" & Chr(9) & rline
'' you can handle the results as they are written to and subsequently read from the StdOut object
Wend
MsgBox strline
''batfile.bat
''ping 1.1.1.1 -n 1 -w 2000 > nul
''echo 2
''ping 1.1.1.1 -n 1 -w 2000 > nul
''echo 4
''ping 1.1.1.1 -n 1 -w 2000 > nul
''echo 6
''ping 1.1.1.1 -n 1 -w 2000 > nul
''echo 8
End Sub