如何在Shell中运行SSH以将值返回到VBAvariables中?

我试图运行一个ssh命令,并捕获输出到VBA中的variables(使用OpenSSH)。 我可以得到这个在命令行中正常工作:

ssh user @ ip python C:\ Temp \ Remote.py

结果是返回到命令行窗口的值的列表。 我想读这个到一个VBAvariables。

我发现这个 , 这个和这个 。 前两个似乎没有正确地发送或接收命令,因为代码被挂在oShell.Exec(…)上。 shell看起来像正在执行,但它只是挂起。 如果closures挂起的命令窗口,VBA中的结果是空白的。

Dim sCmd As String sCmd = "ssh user@ip python C:\Temp\Remote.py" Dim oShell As Object Set oShell = CreateObject("WScript.Shell") Dim oExec As Object Dim oOutput As Object Set oExec = oShell.Exec(sCmd) Set oOutput = oExec.StdOut Dim s As String Dim sLine As String While Not oOutput.AtEndOfStream sLine = oOutput.ReadLine If sLine <> "" Then s = s & sLine & vbCrLf Wend 

第三个似乎部分工作(使用'retVal = Shell("ssh user@ip python C:\Temp\Remote.py", vbNormalFocus)' ),但我不能得到的价值。 我可以看到命令窗口打开,值返回,但我得到一些整数在retVal。

任何帮助?

WScript.Shell对象的.Exec()方法是devise用于(a)运行控制台命令/应用程序,(b)通过返回的WshScriptExec实例的属性捕获其输出的方法

在这一点上,为什么你的基于ssh的命令不起作用是一个谜 – 你的代码通常与控制台命令一起工作。

也就是说,还有一个原因,就是在从一个GUI应用程序(如VBA托pipe应用程序.Exec()运行时, 不要使用.Exec() ,因此在执行过程中控制台窗口始终可见 ,这可能会造成视觉上的破坏。

下面的select(可选)允许隐藏命令 (没有可见的窗口),但是请注意, 既不直接支持从被调用的命令捕获输出

  • VBA自己的Shell()函数 ,然而,它总是asynchronous执行,所以确定命令何时完成是很麻烦的。

  • WScriptShell对象的.Run()方法 ,可以select等待命令完成。

同时执行不可见的执行输出捕获 ,您可以:

  • 结合WScriptShell对象的.Run()方法
  • 将命令的输出发送到一个临时文件
  • 并在命令完成时将临时文件读入内存。
 Dim cmd as String Dim exitCode As Integer Dim tempFile As String Dim capturedOutput As String ' Construct the name of a temporary file to capture the command's stdout output in. tempFile = Environ$("TEMP") & "\" & CreateObject("Scripting.FileSystemObject").GetTempName() ' Define the command to invoke. cmd = "ssh user@ip python C:\Temp\Remote.py" ' Use .Run() to invoke the command. ' - In order to use output redirection, the command must be prefixed with 'cmd /c '. ' - Setting the last argument to `True` makes the invocation synchronous. ' - Replace vbNormalFocus with vbHidden to run the command *invisibly*. exitCode = CreateObject("WScript.Shell").Run("cmd /c " & cmd & " >" & tempFile, vbNormalFocus, True) If exitCode = 0 Then ' Command succeeded. ' Read the output file, then delete the temporary file. capturedOutput = CreateObject("Scripting.FileSystemObject").OpenTextFile(tempFile).ReadAll() CreateObject("Scripting.FileSystemObject").DeleteFile(tempFile) ' Display the output. MsgBox "Captured Output:" & vbNewLine & capturedOutput Else ' Command indicated failure. MsgBox "An unexpected error occurred.", vbExclamation End If 

这是一个基于Shell()替代scheme – 受此实现的启发。

正如你所看到的,使Shell()同步需要更多的努力,并且需要使用Windows API:

 Private Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long Private Declare Function WaitForSingleObject Lib "kernel32.dll" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Private Declare Function GetExitCodeProcess Lib "kernel32.dll" (ByVal hProcess As Long, ByRef lpExitCodeOut As Long) As Integer Sub Main() Dim cmd As String Dim taskId As Integer Dim exitCode As Integer Dim tempFile As String Dim capturedOutput As String ' Construct the name of a temporary file to capture the command's stdout output in. tempFile = Environ$("TEMP") & "\" & CreateObject("Scripting.FileSystemObject").GetTempName() ' Define the command to run. cmd = "ssh user@ip python C:\Temp\Remote.py" ' Use the SyncShell() helper function defined below to invoke the command ' synchronously and to obtain its exit code. ' - In order to use output redirection, the command must be prefixed with 'cmd /c '. ' - Add a 3rd argument with a timeout value in seconds if you don't want to wait ' indefinitely for the process to complete. ' - Replace vbNormalFocus with vbHidden to run the command *invisibly*. exitCode = SyncShell("cmd /c " & cmd & " >" & tempFile, vbNormalFocus) If exitCode = 0 Then ' Command succeeded. ' Read the output file and delete the temporary file. capturedOutput = CreateObject("Scripting.FileSystemObject").OpenTextFile(tempFile).ReadAll() CreateObject("Scripting.FileSystemObject").DeleteFile (tempFile) ' Display the output. MsgBox "Captured Output:" & vbNewLine & capturedOutput Else ' Command indicated failure. MsgBox "An unexpected error occurred.", vbExclamation End If End Sub ' Helper function Private Function SyncShell(ByVal cmd As String, Optional ByVal windowStyle As VbAppWinStyle = vbMinimizedFocus, Optional ByVal timeoutInSecs As Double = -1) As Long Dim pid As Long ' PID (Process ID) as returned by Shell(). Dim h As Long ' Process handle Dim sts As Long ' WinAPI return value Dim timeoutMs As Long ' WINAPI timeout value Dim exitCode As Long ' Invoke the command (invariably asynchronously) and store the PID returned. ' Note that the invocation may fail. pid = Shell(cmd, windowStyle) ' Translate the PIP into a process *handle* with the SYNCHRONIZE and PROCESS_QUERY_LIMITED_INFORMATION access rights, ' so we can wait for the process to terminate and query its exit code. h = OpenProcess(&H100000 Or &H1000, 0, pid) ' &H100000 == SYNCHRONIZE, &H1000 == PROCESS_QUERY_LIMITED_INFORMATION If h = 0 Then Err.Raise vbObjectError + 1024, , "Failed to obtain process handle for process with ID " & pid & "." ' Now wait for the process to terminate. If timeoutInSecs = -1 Then timeoutMs = &HFFFF ' INFINITE Else timeoutMs = timeoutInSecs * 1000 End If sts = WaitForSingleObject(h, timeoutMs) If sts <> 0 Then Err.Raise vbObjectError + 1025, , "Waiting for process with ID " & pid & " to terminate timed out, or an unexpected error occurred." ' Obtain the process's exit code. sts = GetExitCodeProcess(h, exitCode) ' Return value is a BOOL: 1 for true, 0 for false If sts <> 1 Then Err.Raise vbObjectError + 1026, , "Failed to obtain exit code for process ID " & pid & "." CloseHandle h ' Return the exit code. SyncShell = exitCode End Function 

我想你应该尝试在命令开始时使用“命令规范”。 在你的机器上,这可以在VBA中使用VBA.Environ$("comspec") ,在我的机器上它返回C:\WINDOWS\system32\cmd.exe

所以试试

  sCmd = VBA.Environ$("comspec") & " /C " & " ssh user@ip python C:\Temp\Remote.py" 

命令规格需要在某些情况下切换cmd开关

我没有安装Python,所以无法testing。