VBA - WScript.Shell - 执行 PowerShell 命令
VBA - WScript.Shell - Executing A PowerShell Command
我想使用 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 returns 没有信息。空白表格。希望避免切换到 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
更新我
新命令确实遍历了目录树并且returns正确了该树中的文件数。一定是我传递的 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
传递给Wsh的字符串Shell:
Powershell $FilesInPathway = get-childitem -path "C:\Users\Me\Desktop\NewFolder" -recurse -attributes !directory; $FilesInPathway.count
传递给Power的字符串Shell:
$FilesInPathway = get-childitem -path "C:\Users\Me\Desktop\NewFolder" -recurse -attributes !directory; $FilesInPathway.count
请注意,我曾尝试使用 StdIn.Write 方法来隔离命令,但出现错误。
WshShellExec.StdIn.Write "$FilesInPathway.count"
更新二
运行 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}"
这似乎可以解释原因:
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 & """"
@learnAsWeGo 我不想 post 这作为答案(请参阅我之前的评论)但无法将其标记为代码并将其添加到评论中。就是说,如果这适合您,那么您可以专注于编辑 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,根据您的意见,以下代码可能更适合您。 -Filter 只能使用 * 和 ?但是 -replace 表达式可以使用正则表达式。打开会话 window 中的输出显示重命名的文件。输出可以通过管道传输到格式命令以满足您的喜好。您可以查看输出并在完成后退出 window。
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
当然这是一个语法问题。需要用 ' ' 将 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}"
这似乎可以解释原因:
我想使用 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 returns 没有信息。空白表格。希望避免切换到 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
更新我
新命令确实遍历了目录树并且returns正确了该树中的文件数。一定是我传递的 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
传递给Wsh的字符串Shell:
Powershell $FilesInPathway = get-childitem -path "C:\Users\Me\Desktop\NewFolder" -recurse -attributes !directory; $FilesInPathway.count
传递给Power的字符串Shell:
$FilesInPathway = get-childitem -path "C:\Users\Me\Desktop\NewFolder" -recurse -attributes !directory; $FilesInPathway.count
请注意,我曾尝试使用 StdIn.Write 方法来隔离命令,但出现错误。
WshShellExec.StdIn.Write "$FilesInPathway.count"
更新二
运行 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}"
这似乎可以解释原因:
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 & """"
@learnAsWeGo 我不想 post 这作为答案(请参阅我之前的评论)但无法将其标记为代码并将其添加到评论中。就是说,如果这适合您,那么您可以专注于编辑 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,根据您的意见,以下代码可能更适合您。 -Filter 只能使用 * 和 ?但是 -replace 表达式可以使用正则表达式。打开会话 window 中的输出显示重命名的文件。输出可以通过管道传输到格式命令以满足您的喜好。您可以查看输出并在完成后退出 window。
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
当然这是一个语法问题。需要用 ' ' 将 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}"
这似乎可以解释原因: