How can I run a a Powershell command in VBA which contains "$env:UserName"? - vba

I have the following macro which works just fine:
Private Sub Macro()
Dim ExecuteCommand As String
ExecuteCommand = "PowerShell -Command ""& 'C:\Program Files\R\R-4.2.2\bin\Rscript.exe' 'C:\Users\MyUser\Cool Calculations.R'"""
Shell ExecuteCommand
End Sub
What I want to do however, is to replace the name "MyUser" with $env:UserName instead.
However this macro does not seem to work:
Private Sub Macro()
Dim ExecuteCommand As String
ExecuteCommand = "PowerShell -Command ""& 'C:\Program Files\R\R-4.2.2\bin\Rscript.exe' 'C:\Users\$env:UserName\Cool Calculations.R'"""
Shell ExecuteCommand
End Sub
I have tried to run the command manually in PowerShell and it works correctly:
& "C:\Program Files\R\R-4.2.2\bin\Rscript.exe" "C:\Users\$env:UserName\Cool Calculations.R"
I am not sure what I am doing wrong when trying to add $env:UserName in my macro?

As Mathias points out, only "..." strings (expandable strings) perform variable expansion (interpolation) in Powershell; '...' strings (verbatim strings) do not.
Thus you must pass what PowerShell ultimately see as a "C:\Users\$env:UserName\Cool Calculations.R" argument (as an aside: consider simplifying to "$env:USERPROFILE\Cool Calculations.R"); since you're calling from VBA, this requires:
Using ""..."" to embed " chars. inside a VBA "..." string...
and \-escaping for the sake of powershell.exe, the Windows PowerShell CLI (which receives its -Command argument as a "..." string, so any " chars. to be retained as part of the command must be escaped)
That is, you need something like the following (sic; spaces added for readability)
" PowerShell -Command "" & '...' \""...\"" "" "
Specifically:
Private Sub Macro()
Dim ExecuteCommand As String
ExecuteCommand = "PowerShell -Command ""& 'C:\Program Files\R\R-4.2.2\bin\Rscript.exe' \""C:\Users\$env:UserName\Cool Calculations.R\"""""
Shell ExecuteCommand
End Sub

mklement0's response is very elegant, tested working. But I tested the command without escape, that seems not required under Windows Powershell, as Mathias suggested, it works also. I replaced Rscript.exe that I have not installed, by Get-ChildItem cmdlet like this:
Sub RunPSFromVba()
Dim ExecuteCommand As String
'ExecuteCommand = "PowerShell -Command ""& 'C:\Program Files\R\R-4.2.2\bin\Rscript.exe' 'C:\Users\$env:UserName\Cool Calculations.R'"""
ExecuteCommand = "PowerShell -NoExit -Command ""& 'Get-ChildItem' ""C:\Users\$env:UserName\Downloads"""""
Shell ExecuteCommand
End Sub
With PowerShell -NoExit, I keep the PowerShell Window alive after execution, I got this screenshot:
Hope this will also help.

Related

Powershell Console output not available

I am trying to run a pwershell script from excel, which i can do and it works, but the powershell window does not show the information from the write-host comands in the script. If i run the file from the cmd prompt i get all the write-host texts appearing in the console window, like below.
Powershell run from command prompt
[]
However if i use this code from excel.
Sub RunAndGetCmd()
strCommand = "Powershell -File ""C:\PSFiles\TestOutput.ps1"""
Set WshShell = CreateObject("WScript.Shell")
Set WshShellExec = WshShell.Exec(strCommand)
End Sub
I get no texts shown in the console window, just the a flashing cursor like the following
Same Powershell ran from excel
I know the script runs and dooes everything, but i would like to output the texts as the script runs so i can see how far it has gotten.
Any ideas greatly appreciated.
The WScript.Shell Exec command redirects the stdin, stdout and stderr streams so you can access them from your application. As a result, anything written to stdout by the external application (e.g. using write-host in PowerShell) gets redirected instead of being displayed in the external application's window.
If you want the output displayed in the application's window you can use the Run method instead - e.g.
Option Explicit
Sub RunAndGetCmd()
Dim strCommand As String
Dim WshShell As Object
strCommand = "Powershell -File ""C:\PSFiles\TestOutput.ps1"""
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run strCommand
End Sub
but if you're doing that, you might as well just use the built-in VBA Shell function:
Option Explicit
Sub RunAndGetCmd()
Dim strCommand As String
strCommand = "Powershell -File ""C:\PSFiles\TestOutput.ps1"""
VBA.Shell strCommand
End Sub
Note also that you might want an -ExecutionPolicy RemoteSigned in your command in case the machine has it set to Restricted:
strCommand = "Powershell -ExecutionPolicy RemoteSigned -File ""C:\PSFiles\TestOutput.ps1"""

Double quotes passing by VBA to Powershell string

I am trying to create a string, and inside I need to user some double quotes, I try many ways to do this, but all give me a error, please, anyone can help me?
Public Sub Script2()
Dim ScriptText As String
ScriptText = "[System.Windows.Forms.MessageBox]::Show(""""Message Text"""",""""Title"""",1)"
Call shell("PowerShell -noexit powershell.exe -Executionpolicy Bypass -Command " & ScriptText, vbNormalFocus)
End Sub
If I try this way I get:
Message : The term 'Message' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
No line:1 character:88
+ ... ypass -Command[System.Windows.Forms.MessageBox]::Show(Message Text,Ti ...
+ ~~~~~~~
+ CategoryInfo : ObjectNotFound: (Message:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
OK, here is a working code (it's VBS but there shouldn't be much troubles to use it in VBA)
ScriptText = """[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Message Text','Title',1)"""
createobject("wscript.shell").run "powershell.exe -noexit -Executionpolicy Bypass -Command " & ScriptText
Have you tried using ` for each " that you need? This way you'll get something like this:
ScriptText = "[System.Windows.Forms.MessageBox]::Show(`"`"Message Text`"`",`"`"Title`"`",1)"
" is normally reserved, and if you need to write it in a string, you need to escape it.
You have two levels of parameter expansion (one by VBA and one by Powershell) so you need to double escape your quotes. The doubled double-quotes get it past VBA. The backtick will escape it for Powershell.
ScriptText = "[System.Windows.Forms.MessageBox]::Show(`""Message Text`"",`""Title`"",1)"

VBS variable use to execute

Here's my vbs :
Myvar = "calc"
command = "powershell.exe -nologo -command Myvar"
set shell = CreateObject("WScript.Shell")
shell.Run command,0
It does not work. calc is not executed.
Later, i will substitue calc by a ps1 file with spaces.
Can you help me?
Thanks.
"... Myvar" is just a quoted string, there is no substitution of the variable's value.
You need to concatenate the variable, to append the value from the variable, using the concatenation operator &:
command = "powershell.exe -nologo -command " & Myvar

vba shell command executing but no output

I am having some problems running a shell command and checking the output of the data. I wish to check using vba if the current remote user of the DB is Active. In
command prompt =
for /f "tokens=1-8" %a in ('quser') do #if "%d"== "Active" echo %COMPUTERNAME% %a %d
returns the users logged on and their state I wish to check that none of them are disconnected ("Disc"). I used this function to check the shell and return the pipe value as a string in a message box
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
Debug.Print sCmd
'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
'example MsgBox ShellRun("cmd.exe /c" & "dir c:\")
End Function
Call Command used on click event
Dim CMDLineCommand As String
CMDLineCommand = "for /f ""tokens=1-8"" %a in ('quser') do #if ""%d""== ""Active"" echo %COMPUTERNAME% %a %d"
'(CMDLineCommand = "dir c:\")<------ THIS WORKS FINE
MsgBox ShellRun("cmd.exe /c " & CMDLineCommand)
This works fine for loads of command line commands I have tested it with but not query and therefore query user. The query user command works fine from command line but does not return anything when issued through a VBA Shell commands.
Any thoughts would be appreciated.
because shell does not know the path of the query.exe(quesr) it does not continue where as command prompt can use system variables to find exe's. solution find the query.exe and copy it to a working directory then run the shell command. mine was located in a hashed folder within C:\Windows\WinSxS be careful as here are 64bit versions and 32 bit.

Execute a command in command prompt using excel VBA

I have a fixed command which i need to pass to command prompt using VBA and then the command should run.
e.g. "perl a.pl c:\temp"
following is the command i am trying to use but it just opens command prompt and doesn't run the command.
Call Shell("cmd.exe -s:" & "perl a.pl c:\temp", vbNormalFocus)
Please check.
The S parameter does not do anything on its own.
/S Modifies the treatment of string after /C or /K (see below)
/C Carries out the command specified by string and then terminates
/K Carries out the command specified by string but remains
Try something like this instead
Call Shell("cmd.exe /S /K" & "perl a.pl c:\temp", vbNormalFocus)
You may not even need to add "cmd.exe" to this command unless you want a command window to open up when this is run. Shell should execute the command on its own.
Shell("perl a.pl c:\temp")
-Edit-
To wait for the command to finish you will have to do something like #Nate Hekman shows in his answer here
Dim wsh As Object
Set wsh = VBA.CreateObject("WScript.Shell")
Dim waitOnReturn As Boolean: waitOnReturn = True
Dim windowStyle As Integer: windowStyle = 1
wsh.Run "cmd.exe /S /C perl a.pl c:\temp", windowStyle, waitOnReturn