【问题标题】:VBA - WScript.Shell - Executing A PowerShell CommandVBA - WScript.Shell - 执行 PowerShell 命令
【发布时间】:2021-01-12 23:42:30
【问题描述】:

我想使用 WScript.Shell 从 VBA 执行 PowerShell 命令

这是发送到 WScript.Shell 的字符串:

Powershell -ExecutionPolicy Bypass -Command $FilesInPathway = get-childitem -path "C:\Users\Me\Desktop\NewFolder" -recurse -attributes !directory; foreach ($file in $FilesInPathway){$tempName = $file.name; $tempName = $tempName.replace(".TXT",".txt"); Rename-Item $file.fullname $tempName}

这是我要执行的 PowerShell 命令。请注意,此命令确实在 PowerShell 中成功执行。

$FilesInPathway = get-childitem -path "C:\Users\Me\Desktop\NewFolder" -recurse -attributes !directory; foreach ($file in $FilesInPathway){$tempName = $file.name; $tempName = $tempName.replace(".TXT",".txt"); Rename-Item $file.fullname $tempName}

不获取执行 PowerShell 命令时出现问题的信息,但命令未执行且文件未重命名。 Msgbox 不返回任何信息。空白表格。希望避免切换到 PowerShell 脚本或使用 DIR 和 NAME 编写此脚本。

strCommand = "Powershell -ExecutionPolicy Bypass -Command $FilesInPathway = get-childitem -path " & """" & dataPacket.filePath & """" & " -recurse -Attributes !Directory; foreach ($file in $FilesInPathway){$tempName = $file.name; $tempName = $tempName.replace(" & """" & dataPacket.stringOld & """" & "," & """" & dataPacket.stringNew & """" & "); Rename-Item $file.fullname $tempName}"
Set WshShell = CreateObject("WScript.Shell")
Set WshShellExec = WshShell.Exec(strCommand)
strOutput = WshShellExec.StdOut.ReadAll
MsgBox strOutput

更新我

新命令确实会遍历目录树并返回该树中正确数量的文件。一定是我传递的 foreach 函数没有执行。但是当我直接在 Powershell 中运行 debug.print 版本的字符串时,它执行得很好——所有文件都被重命名了。

strCommand = "Powershell $FilesInPathway = get-childitem -path " & """" & dataPacket.filePath & """" & " -recurse -attributes !directory; $FilesInPathway.count"
Set WshShell = CreateObject("WScript.Shell")
Set WshShellExec = WshShell.Exec(strCommand)
Do While WshShellExec.Status = 0
     Application.Wait (Now() + TimeValue("0:00:01"))
Loop
strOutput = WshShellExec.StdOut.ReadAll
MsgBox strOutput

传递给 WshShell 的字符串:

Powershell $FilesInPathway = get-childitem -path "C:\Users\Me\Desktop\NewFolder" -recurse -attributes !directory; $FilesInPathway.count

传递给 PowerShell 的字符串:

$FilesInPathway = get-childitem -path "C:\Users\Me\Desktop\NewFolder" -recurse -attributes !directory; $FilesInPathway.count

注意,我尝试使用 StdIn.Write 方法隔离命令,但出现错误。

WshShellExec.StdIn.Write "$FilesInPathway.count"

更新 II

使用声明的类型或枚举运行 Shell() 可能会生成恶意宏警告并杀死 excel 应用程序?没有隔离导致这种情况的代码。

如果命令中包含-noexit,以下将进入无限循环 在 WshShellExec.Status = 0 时执行 Application.Wait (Now() + TimeValue("0:00:01")) 循环

回答

当然,这将是一个语法问题。需要将 Replace 方法的参数用 ' ' 括起来。如果文件路径包含空格,它也需要带有“ ”的附件。如果直接输入 PowerShell,带有“”的附件可以正常工作。所以命令是:

strCommand = "Powershell $FilesInPathway = get-childitem -path " & "'" & dataPacket.filePath & "'" & " -recurse -attributes !directory; foreach ($file in $FilesInPathway){$tempName = $file.name; $tempName = $tempName.replace('" & dataPacket.stringOld & "','" & dataPacket.stringNew & "'); Rename-Item $file.fullname $tempName}"

这似乎解释了原因:https://stackoverflow.com/a/45762288/9721351

【问题讨论】:

  • 也许你解释一下你想做什么
  • 您是从 Word、Excel 等中的宏/vba 调用它吗?尽管你在哪里从 VBA 运行这个?这 [" & """" & cataPacket.filePath & """" & "] 不是我见过的用于变量使用的格式,也不是适当的扩展。如果您正在运行此表单 PowerShell,我很惊讶它完全成功,因为引用的那些是简单的字符串而不是 UNC 路径。然而,ABL(永远在学习)是我的口头禅。
  • returned = WshShell.Run(strCommand, 4, True) 失败?尽管 Dir 没有递归选项,但 VBA 已经看到 Loop Through All Subfolders Using VBA 并查看 FSO
  • 如果您想使用 WScript.Shell,这不是一个答案,但可能会帮助您调试您的 powershell 行。而且,这不是来自 Excel,而是从 Access 模块运行(但 VBA,所以我认为它应该在 Excel 中工作)。我不想通过重命名我自己的文件来进行测试,所以这只是使用豪华会话来列出打开会话的目录下的文件夹。两个示例,一个可能适合您 - 1)将输出写入文件,2)将输出写入屏幕但保持会话打开,以便您可以读取屏幕然后手动关闭它。
  • 我不知道降价 - 让我在几分钟内尝试发布可读代码

标签: excel vba powershell wsh


【解决方案1】:

1.等到脚本完成执行

您必须等到WshShell.Exec 完成执行,因为它不会向您发出信号。因此,您的代码继续运行,并且脚本仍在运行,它没有返回响应并且您的 MessageBox 保持为空。

最简单的方法是使用WshShell.Run 方法,因为它提供了一个等待执行完成的参数。

strCommand = "Powershell -ExecutionPolicy Bypass -Command $FilesInPathway = get-childitem -path " & """" & dataPacket.filePath & """" & " -recurse -Attributes !Directory; foreach ($file in $FilesInPathway){$tempName = $file.name; $tempName = $tempName.replace(" & """" & dataPacket.stringOld & """" & "," & """" & dataPacket.stringNew & """" & "); Rename-Item $file.fullname $tempName}"
Set WshShell = CreateObject("WScript.Shell")
strOutput = WshShell.Run(strCommand ,0 ,True)
MsgBox strOutput

也可以等待.Exec方法完成(WshShellExec.Status <> 0),通过循环有时检查.Status

strCommand = "Powershell -ExecutionPolicy Bypass -Command $FilesInPathway = get-childitem -path " & """" & dataPacket.filePath & """" & " -recurse -Attributes !Directory; foreach ($file in $FilesInPathway){$tempName = $file.name; $tempName = $tempName.replace(" & """" & dataPacket.stringOld & """" & "," & """" & dataPacket.stringNew & """" & "); Rename-Item $file.fullname $tempName}"
Set WshShell = CreateObject("WScript.Shell")
Set WshShellExec = WshShell.Exec(strCommand)
Do While WshShellExec.Status = 0
     Application.Wait(Now() + TimeValue("0:00:01"))
Loop
strOutput = WshShellExec.StdOut.ReadAll
MsgBox strOutput

2。你不能从 shell 传递多个 powershell 命令(没有一些技巧,阅读Docs

只需使用命令创建一个 ps1 文件,然后像上面一样执行该脚本。

这将使您能够使用 Powershell IDE 进行编码,并且您可以在其他地方重用该脚本! 所有动态代码都可以作为参数传递给您在脚本中创建的匹配参数(保存为 RenameFiles.ps1)。

param ($FilePath, $StringOld, $StringNew)
$FilesInPathway = get-childitem -path $FilePath -recurse -Attributes !Directory 
foreach ($file in $FilesInPathway)
{
    $tempName = $file.name 
    $tempName = $tempName.replace($StringOld,$StringNew) 
    Rename-Item $file.fullname $tempName
}

执行该脚本的命令:

strCommand = "Powershell -ExecutionPolicy Bypass -File ""path\to\RenameFiles.ps1"" """ & dataPacket.filePath & """ """ & dataPacket.stringOld & """ """ & dataPacket.stringeNew & """"

【讨论】:

  • 太棒了。感觉我现在对此有了更好的掌握。发送了“Powershell -ExecutionPolicy Bypass -Command $FilesInPathway = get-childitem -path”&“”“”&dataPacket.filePath&“”“”&“-recurse -attributes !directory;$FilesInPathway.count 和 msgbox 返回了正确的文件数量。因此,我的 PowerShell 命令的后半部分肯定存在问题,但正如 OP 中所述,当我从即时窗口复制它并将其粘贴到 PowerShell 时,字符串会运行。
  • 您提供绝对路径,因为 WDcript 可能有不同的.CurrentFolder?试过VBA.Shell strCommand (但没有waitonreturn选项)?
  • filePath 工作正常,因为我能够发送其他命令并返回正确的信息,例如我正在遍历的目录树中的文件数。问题似乎与我发送到 PowerShell 的 foreach 函数有关。我正在尝试使用 WshShellExc.StdIn.Write 方法来隔离命令,但出现错误。无论如何,我现在走在正确的轨道上。欣赏它。
  • 似乎你不能执行多个powershell命令,只需将它们传递给shell!您在 powershell 中测试了命令,而不是命令行?您应该考虑使用 ps 代码执行文件,通过 shell,而不是 rhe 命令。作为好处,您可以使用 powershell ide,并且可以在其他地方重用该文件。然后只需执行 ps1 文件并将动态值作为参数传递给它!
  • 很抱歉造成这个 x y 问题 - 但我真的很想从 PowerShell 发送命令。在这种情况下,我需要将 .Replace 方法的参数包装在“”而不是“”中。如果文件路径包含空格,它也必须用“”而不是“”包裹。如果 -noexit 包含在命令中,Do While WshShellExec.Status = 0 永远不会退出,这是一个无限循环。
【解决方案2】:

@learnAsWeGo 我不想将此作为答案发布(请参阅我之前的 cmets),但在将其标记为代码并将其放入评论时遇到了麻烦。也就是说,如果它为您运行,那么您可以专注于编辑 strPoshLine 以便它执行您想要的操作。可能是您的替代方案。

Dim strPoshLine As String
Dim Retval As Variant

'Example1 to execute a powershell line from VBA and write host output to a file
    strPoshLine = "get-childitem -directory | Select-object fullname | out-file 'c:\PoshFromVbaTeat.txt'"
    Retval = Shell("Powershell.exe -noexit -Command " & strPoshLine, 0)

'Example2 to execute a powershell line from VBA and not exit the session so
    strPoshLine = "get-childitem -directory | Select-object fullname "
    Retval = Shell("Powershell.exe -noexit -Command " & strPoshLine, 1)
    MsgBox "Enter 'exit' in powershell window to close the session."

@learnAsWeGo,根据你的cmets,下面的代码可能对你来说更灵活。 -Filter 只能使用 * 和 ?但 -replace 表达式可以使用正则表达式。在打开的会话窗口中有输出显示重命名的文件。输出可以通过管道传输到格式命令以满足您的偏好。您可以查看输出并在完成后退出窗口。

Public Sub VbaToPwsh()

Dim strGetFiles As String
Dim strRename As String
Dim varRetval As Variant

strGetFiles = "Get-Childitem -Path 'C:\zDev\*' -Filter '*.txt' -Recurse -File "
' Notes:  If using -Recurse then the last '\*' in the path is not needed, otherwise needed if using -Filter.
'         The use of -Filter and -File is just to reduce the number of files going through the pipeline
'           and not needed if the -replace operator provides all the constraint required.

strRename = " Rename-Item -NewName {$_.Name -replace '.txt', '.TXT'} -PassThru "
' Notes:  The -replace comparison operator is not case sensitive on selection but writes as shown.
'         If case sensitivity on selection is needed use -creplace instead.
'         Rename-Item does not provide output and that is way the -PassThru param is added.
          
varRetval = Shell("Powershell.exe -noexit -Command " & strGetFiles & "|" & strRename, 1)

MsgBox "Enter 'exit' in PowerShell window to close the session."

End Sub

【讨论】:

  • 有趣的是,当从我正在开发的工作簿中调用 Shell() 时,excel 会生成恶意宏警告并杀死应用程序。没有触发这个的隔离组件,可能是因为我声明了一个类型或枚举?然而,在另一个我想运行的带有命令的工作簿 Shell() 中确实有效。最终我不得不替换 Replace 方法的 " " 周围参数。您的代码帮助我获得了错误消息,向我展示了需要更改的内容。
  • 我删除了一条注释,其中一行代码不起作用。问题@learnAsWeGo,您是否有兴趣仅重命名扩展名或文件名的任何部分?而且,您正在寻找的输出是旧文件名和新文件名在同一行吗?
  • msgbox 旨在了解 PowerShell 中发生了什么。命令用于重命名文件,可以是扩展名或文件名的任何部分。
【解决方案3】:

当然,这将是一个语法问题。需要将 Replace 方法的参数用 ' ' 括起来。如果文件路径包含空格,它也需要带有“ ”的附件。如果直接输入 PowerShell,带有“”的附件可以正常工作。所以命令是:

strCommand = "Powershell $FilesInPathway = get-childitem -path " & "'" & dataPacket.filePath & "'" & " -recurse -attributes !directory; foreach ($file in $FilesInPathway){$tempName = $file.name; $tempName = $tempName.replace('" & dataPacket.stringOld & "','" & dataPacket.stringNew & "'); Rename-Item $file.fullname $tempName}"

这似乎解释了原因:https://stackoverflow.com/a/45762288/9721351

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 2021-05-10
    • 2014-11-03
    • 2018-12-24
    • 2011-09-19
    • 2017-04-23
    相关资源
    最近更新 更多