【问题标题】:How to execute a PowerShell function by passing parameters using VBA如何通过使用 VBA 传递参数来执行 PowerShell 函数
【发布时间】:2016-11-14 21:02:45
【问题描述】:

我一直在尝试执行一个 PowerShell 函数 (countLines),该函数从 VBA 获取输入参数 fileName。我已经参考了thisthis 的一些答案,但它们对我不起作用。以下是我做同样的尝试(fname 是一个 VBA 变量,我将其作为参数发送到包含以下代码行的 VBA 例程):

fileReader.ps1的内容是:

function countLines {
    param([string]$logFile);
      #The directory path may differ for others according to the machine's source code repository
    $scriptPath = Resolve-Path "..\..\testFolder";
    $varHolder = -join($scriptPath, "\logFileCount.txt");
    $linesb4Exec = Get-Content $logFile;
    $nLines = $linesb4Exec.Count;
    Clear-Content $varHolder
    $nLines | Out-File $varHolder
    #For second attempt, I am adding the following too
    #echo $nLines;
}

第一次尝试:

Call Shell("powershell  -ExecutionPolicy Unrestricted ""D:\Temp\fileReader.ps1"";countLines -fileName """ & fname & """", vbMaximizedFocus))

第二次尝试:

Dim strCommand As Variant, WshShell As Variant, WshShellExec As Variant, output As Variant
strCommand = "powershell -command ""& { . ""D:\Temp\fileReader.ps1"";countLines -logFile """ & fname & """ }"""
Set WshShell = CreateObject("WScript.Shell")
Set WshShellExec = WshShell.Run(strCommand)
output = WshShellExec.StdOut.ReadAll

使用这个,我得到一个错误:类型不匹配。

第三次尝试(我尝试硬编码我的 PowerShell 文件中的所有内容并删除了该函数并只运行了一段代码):

Call Shell("powershell -ExecutionPolicy Bypass -file ""D:\Temp\fileReader.ps1""", vbMaximizedFocus)

并将上面的 powershell 脚本更改为:

$logFile = "C:\somepath\Logs\Log.txt";
$varHolder = "D:\testing\testFolder\logFileCount.txt";
$linesb4Exec = Get-Content $logFile;
$nLines = $linesb4Exec.Count;
Clear-Content $varHolder
$nLines | Out-File $varHolder

这似乎有效。

如果有人可以帮助我使用 VBA 调用任何带参数的 PowerShell 函数,我会很高兴。

【问题讨论】:

    标签: excel powershell parameter-passing vba


    【解决方案1】:

    你的尝试基本上有两个错误:

    • powershell "D:\Temp\fileReader.ps1";countLines -fileName ...
      这是行不通的,因为当您在父上下文中调用函数时,脚本文件是在子上下文中执行的。您需要 dot-source 文件以使其内容在当前上下文中可用。
    • Set WshShellExec = WshShell.Run(strCommand)
      在这里,您混淆了 RunExec 方法。前者返回一个整数(外部命令的退出代码),后者返回一个WshScriptExec 对象(并且只有该对象提供对外部进程的StdInStdOutStdErr 的访问)。您会遇到类型不匹配,因为您尝试使用 Set 关键字分配整数返回值,该关键字只能在将对象分配给变量时使用。

    如果您想坚持使用-Command 并点源文件,则第二次尝试的命令行是正确的方法。在 PowerShell 脚本块中使用单引号来避免引用融合,并使用 Shell 函数执行命令行:

    cmd = "powershell -ExecutionPolicy Bypass -Command ""&{. 'D:\Temp\fileReader.ps1';countLines -logFile '" & fname & "'}"""
    Shell cmd
    

    话虽如此,如果函数是脚本中唯一的代码,那么参数化脚本并直接从中运行代码会更简单:

    [CmdletBinding()]
    Param(
      [string]$LogFile
    )
    
    $scriptPath = Resolve-Path "..\..\testFolder"
    $varHolder  = Join-Path $scriptPath "logFileCount.txt"
    @(Get-Content $logFile).Count | Out-File $varHolder
    

    使用参数-File 运行脚本,如下所示:

    cmd = "powershell -ExecutionPolicy Bypass -File ""D:\Temp\fileReader.ps1"" -LogFile """ & fname & """"
    Shell cmd
    

    请注意,..\..\testFolder 路径是相对于您当前的工作目录的,而不是相对于脚本位置的。对于后者,请使用$MyInvocation.MyCommand.Path


    如果出现问题,请将参数 -NoExit 添加到命令行(因此 PowerShell 窗口不会自动关闭),并使用选项 vbNormalFocus 调用 Shell 函数以在前台运行进程.

    cmd = "powershell -ExecutionPolicy Bypass -NoExit -File ""D:\Temp\fileReader.ps1"" -LogFile """ & fname & """"
    Shell cmd, vbNormalFocus
    

    【讨论】:

    • 哎呀...对不起,我的错...我会尽快编辑...感谢您指出
    • 成功了! :) 你含蓄地解决了我的一个疑问,我忘了问我如何才能阻止 PowerShell 窗口自动关闭。我无法确定路径没有得到解决的问题,因为你正确地指出了它......为这样一个描述性很好的答案太棒了!我需要做很多阅读......
    • 如果您能指定如何在我的 VBA 中正确捕获 PowerShell 函数的返回值,那就太好了。正如您已经指出的那样,使用上面的返回值实际上为我获取了退出代码,所以这对我不起作用。
    • 请为此发布一个新问题。
    猜你喜欢
    • 1970-01-01
    • 2021-04-17
    • 1970-01-01
    • 2021-04-21
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 2023-03-27
    • 2015-07-21
    相关资源
    最近更新 更多