Command Prompt executes this correctly but Powershell doesn't? - powershell

Having some weirdness where Powershell is not executing this command correctly. It can't understand the INSTALLFOLDER or TARGETDIR and just throws up the Windows Installer help box.
Whereas command prompt processes this just fine??? It's very weird. PS version is 5.1.18362 I believe.
the command is
msiexec.exe /i "C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi" INSTALLFOLDER="C:\Installation Test" /qn
My msi is a Wix installer msi and has the property INSTALLFOLDER

This is not how you run this command in PowerShell. There are several ways to run external command in PowerShell and it a well-documented use case. Links to follow
• Using PowerShell and external commands and their parameters or
switches.
Running external commands, always require special consideration.
• PowerShell: Running Executables
• Solve Problems with External Command Lines in PowerShell
• Top 5 tips for running external commands in Powershell
• Using Windows PowerShell to run old command-line tools (and their
weirdest parameters)
• Execution of external commands in PowerShell done right
https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right
https://mnaoumov.wordpress.com/2015/03/31/execution-of-external-commands-native-applications-in-powershell-done-right-part-2
https://mnaoumov.wordpress.com/2015/04/05/execution-of-external-commands-native-applications-in-powershell-done-right-part-3
http://edgylogic.com/blog/powershell-and-external-commands-done-right
• Quoating specifics
https://trevorsullivan.net/2016/07/20/powershell-quoting
PowerShell has a list of characters that mean specific things, vs what cmd.exe implementation means, and if you need those, they must be properly terminated. See the below
About Special Characters
PowerShell - Special Characters And Tokens
So, to make your command work in the PowerShell consolehost, ISE, VSCode, do something like this...
# Using teh Call operator
& 'msiexec.exe /i "C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi" INSTALLFOLDER="C:\Installation Test" /qn'
or
... this...
# Using Start-Process
$ConsoelCommand = 'msiexec.exe /i "C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi" INSTALLFOLDER="C:\Installation Test" /qn'
Start-Process powershell -ArgumentList "-NoExit","-Command &{ $ConsoleCommand }" -Wait

Make sure you check for existence of that actual folder, but it's likely the problem is the space
instead of this:
msiexec.exe /i "C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi" INSTALLFOLDER="C:\Installation Test" /qn
try this:
invoke-command -scriptblock {cmd /c "msiexec.exe /i `"C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi`" INSTALLFOLDER=`"C:\Installation` Test`" /qn"}
Notice that all the double-quotes are escaped AND a the space in "C:\Installation Test" is escaped. Your problem will boil down to this space and how you handle it. Also try changing double to single quotes. If you decide to use variables in your command, definitely add the strings (""+"") to create the command so that it will expand the variables correctly.

Related

Powershell Elevated and pass a command

So I am building a script that launches an executable in the common files folder of x86 folder. The issue I am having is the script uses the (x86) as if the x86 is a command that should be run first. Does anyone have any ideas on how to prevent that?
$Pulse = "${env:CommonProgramFiles(x86)}\Pulse Secure\JamUI\JamCommand.exe"
$ImportFile = '"-importfile "C:\Program Files (x86)\MyFile.preconfig"'
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{Start $Pulse, $ImportFile}" -Verb Runas
As Mathias notes, there is no strict need to launch another PowerShell instance with elevation (run as admin, -Verb RunAs), because you can directly elevate the target executable, JamCommand.exe; however, if you want to run the executable in another PowerShell session that stays open afterwards - as your use of -noexit suggests - you indeed do:
$pulse = "${env:CommonProgramFiles(x86)}\Pulse Secure\JamUI\JamCommand.exe"
$importFileArg = '-importfile "C:\Program Files (x86)\MyFile.preconfig"'
Start-Process -Verb RunAs powershell #"
-noexit -noprofile -c & "$pulse" $importFileArg
"#
Note:
This runs JamCommand.exe directly in the elevated PowerShell session, synchronously (assuming it is a console application), and the interactive session is only entered after that program exits. (Your attempt contains another Start-Process call (via alias Start), which would make a console program run in yet another console window, which auto-closes when the program exits.)
All pass-through arguments are encoded in a single (here)-string passed to the (positionally implied) -ArgumentList parameter, which allows direct control of the command line that is used to launch process, and is generally the most robust approach due to a long-standing bug in how passing arguments individually, as elements of an array is handled - see this answer.

Trying to run a headless executable command through Powershell that works on cmd line

I am trying to run an executable through powershell to run headless, to install a program onto a VM/LocalHost machine. I can get the wizard to open, but for whatever reason I cannot get it to run headless. Here is the cmd line that I run that works:
start /WAIT setup.exe /clone_wait /S /v" /qn"
This is my attempts in powershell
Start-Process .\setup.exe /S -Wait -PassThru
Start-Process .\setup.exe /S /v /qn -Wait -PassThru
Start-Process setup.exe -ArgumentList '/clone_wait /S /v /qn' -Wait
In the cmd line instance the application installs without issue - in the powershell instance the wizard opens and is on the first "Next" prompt. Any help would be appreciated!
I also attempted to add the additional parameters "/v" and "/qn" which return an error : Start-Process : A positional parameter cannot be found that accepts argument '/v'
The bottom attempt runs but it's not waiting for the installation to complete
You may be overthinking it. Remember that PowerShell is a shell. One of the purposes of a shell is to run commands that you type.
Thus: You don't need Start-Process. Just type the command to run and press Enter.
PS C:\> .\setup.exe /clone_wait /S /v /qn
Now if the executable (or script) you want to run contains spaces in the path or name, then use the call/invocation operator (&) and specify the quotes; for example:
PS C:\> & "\package files\setup.exe" /clone_wait /S /v /qn
(This behavior is the same no matter whether you are at the PowerShell prompt or if you put the command in a script.)
This worked for me. You need to quote the whole argumentlist, plus embed double quotes to pass what you want to /v.
start-process -wait SetupStata16.exe -ArgumentList '/s /v"/qb ADDLOCAL=core,StataMP64"'
Running the command normally and then using wait-process after might be a simpler alternative, if you're sure there's only one process with that name:
notepad
wait-process notepad
To follow-up to all that you have been given thus far. Running executables via PowerShell is a well-documented use case.
PowerShell: Running Executables
Solve Problems with External Command Lines in PowerShell
Top 5 tips for running external commands in Powershell
Using Windows PowerShell to run old command-line tools (and their
weirdest parameters)
So, from the first link provides more validation of what you've been given.
5. The Call Operator &
Why: Used to treat a string as a SINGLE command. Useful for dealing with spaces.
In PowerShell V2.0, if you are running 7z.exe (7-Zip.exe) or another command that starts with a number, you have to use the command invocation operator &.
The PowerShell V3.0 parser do it now smarter, in this case you don’t need the & anymore.
Details: Runs a command, script, or script block. The call operator, also known as the "invocation operator," lets you run commands that are stored in variables and represented by strings. Because the call operator does not parse the command, it cannot interpret command parameters
Example:
& 'C:\Program Files\Windows Media Player\wmplayer.exe' "c:\videos\my home video.avi" /fullscreen
Things can get tricky when an external command has a lot of parameters or there are spaces in the arguments or paths!
With spaces you have to nest Quotation marks and the result it is not always clear!
In this case it is better to separate everything like so:
$CMD = 'SuperApp.exe'
$arg1 = 'filename1'
$arg2 = '-someswitch'
$arg3 = 'C:\documents and settings\user\desktop\some other file.txt'
$arg4 = '-yetanotherswitch'
& $CMD $arg1 $arg2 $arg3 $arg4
# or same like that:
$AllArgs = #('filename1', '-someswitch', 'C:\documents and settings\user\desktop\some other file.txt', '-yetanotherswitch')
& 'SuperApp.exe' $AllArgs
6. cmd /c - Using the old cmd shell
** This method should no longer be used with V3
Why: Bypasses PowerShell and runs the command from a cmd shell. Often times used with a DIR which runs faster in the cmd shell than in PowerShell (NOTE: This was an issue with PowerShell v2 and its use of .Net 2.0, this is not an issue with V3).
Details: Opens a CMD prompt from within powershell and then executes the command and returns the text of that command. The /c tells CMD that it should terminate after the command has completed. There is little to no reason to use this with V3.
Example:
#runs DIR from a cmd shell, DIR in PowerShell is an alias to GCI. This will return the directory listing as a string but returns much faster than a GCI
cmd /c dir c:\windows
7. Start-Process (start/saps)
Why: Starts a process and returns the .Net process object Jump if -PassThru is provided. It also allows you to control the environment in which the process is started (user profile, output redirection etc). You can also use the Verb parameter (right click on a file, that list of actions) so that you can, for example, play a wav file.
Details: Executes a program returning the process object of the application. Allows you to control the action on a file (verb mentioned above) and control the environment in which the app is run. You also have the ability to wait on the process to end. You can also subscribe to the processes Exited event.
Example:
#starts a process, waits for it to finish and then checks the exit code.
$p = Start-Process ping -ArgumentList "invalidhost" -wait -NoNewWindow -PassThru
$p.HasExited
$p.ExitCode
#to find available Verbs use the following code.
$startExe = new-object System.Diagnostics.ProcessStartInfo -args PowerShell.exe
$startExe.verbs

Why doesn't this msiexec.exe command work in powershell?

I am trying to execute the following command through powershell, in a script invoked by an Advanced Installer generated installation program. The problem is that when the script executes, it chokes on a call to MSIEXEC.exe. More specifically, it puts up a windows dialog of the msiexec help screen.
Ok so maybe it doesn't like the way advanced installer is executing it. So I take the actual line that is causing problems:
msiexec.exe /q /i 'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' ADDLOCAL='all'
And when I execute this directly in powershell, I still get the same stupid help screen. I have tried every conceivable variation of this command line:
/a and /passive instead of /i and /q
with double quotes
with single quotes
the msi unquoted
in an admin elevated shell
in a normal privilege shell
the msi located on the desktop instead of the temp folder
using /x to uninstall in case it was already installed
In all cases, I get the damnable "help" dialog. The only thing that appears to make a difference is if I leave off the INSTALLLOCATION and ADDLOCAL options. (These are apparently used as per "Unattended Installation part 2" found here: https://docs.mongodb.com/tutorials/install-mongodb-on-windows/). In that case it just exits quietly without installing anything.
I'm honestly at my wits' end having been beating my head against the wall on this all afternoon.
By the way, the reason I'm installing mongo in such an absurd way is I need a method of having a single-install system for my company's product. It depends on Mongo, and we have to have it run as a server and use authentication, so I have to have scripts to create the admin and database user and put it into authenticated mode. Since I needed to know where mongo was installed (to execute mongod.exe and mongo.exe) I need to query the user first for the location, then pass on the install location to the mongo installer. If I'm completely off the rails here please let me know that there's a better way.
Thanks
EDITED: I forgot to mention I wrote my complete powershell script and tested it before trying to execute it through advanced installer. The script worked until I tried to run it through the installer. Strange that I still can't execute the command though manually now.
It seems that in order to pass paths with embedded spaces to msiexec, you must use explicit embedded "..." quoting around them.
In your case, this means that instead of passing
INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\', you must pass INSTALLLOCATION='"C:\Program Files\MongoDB\Server\3.4\\"'[1]
Note the embedded "..." and the extra \ at the end of the path to ensure that \" alone isn't mistaken for an escaped " by msiexec (though it may work without the extra \ too).
To put it all together:
msiexec.exe /q /i `
'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' `
INSTALLLOCATION='"C:\Program Files\MongoDB\Server\3.4\\"' ADDLOCAL='all'
Caveat:
This embedded-quoting technique relies on longstanding, but broken PowerShell behavior - see this answer; should it ever get fixed, the technique will stop working; by contrast, the
--% approach shown below will continue to work.
A workaround-free, future-proof method is to use the PSv3+ ie helper function from the Native module (in PSv5+, install with Install-Module Native from the PowerShell Gallery), which internally compensates for all broken behavior and allows passing arguments as expected; that is, simply prepending ie to your original command would be enough:
# No workarounds needed with the 'ie' function from the 'Native' module.
ie msiexec.exe /q /i 'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' ADDLOCAL='all'
The alternative is to stick with the original quoting and use --%, the stop-parsing symbol, but note that this means that you cannot use PowerShell variables in all subsequent arguments:
msiexec.exe /q /i `
'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' `
--% INSTALLLOCATION="C:\Program Files\MongoDB\Server\3.4\\" ADDLOCAL='all'
Note that msiexec, despite having a CLI (command-line interface), is a GUI-subsystem application, so it runs asynchronously by default; if you want to run it synchronously, use
Start-Process -Wait:
$msiArgs = '/q /i "C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi" INSTALLLOCATION="C:\Program Files\MongoDB\Server\3.4\\" ADDLOCAL=all'
$ps = Start-Process -PassThru -Wait msiexec -ArgumentList $msiArgs
# $ps.ExitCode contains msiexec's exit code.
Note that the argument-list string, $msiArgs, is used as-is by Start-Process as part of the command line used to invoke the target program (msiexec), which means:
only (embedded) double-quoting must be used.
use "..." with embedded " escaped as `" to embed PowerShell variables and expressions in the string.
conversely, however, no workaround for partially quoted arguments is needed.
Even though Start-Process technically supports passing the arguments individually, as an array, this is best avoided due to a longstanding bug - see GitHub issue #5576.
[1] The reason that INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' doesn't work is that PowerShell transforms the argument by "..."-quoting it as a whole, which msiexec doesn't recognize; specifically, what is passed to msiexec in this case is:
"INSTALLLOCATION=C:\Program Files\MongoDB\Server\3.4\"

Running CMD command in PowerShell

I am having a bunch of issues with getting a PowerShell command to run. All it is doing is running a command that would be run in a CMD prompt window.
Here is the command:
"C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386\CmRcViewer.exe" PCNAME
I have tried the following with no success (I have tried many iterations of this to try and get one that works. Syntax is probably all screwed up):
$TEXT = $textbox.Text #$textbox is where the user enters the PC name.
$CMDCOMMAND = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386\CmRcViewer.exe"
Start-Process '"$CMDCOMMAND" $TEXT'
#iex -Command ('"C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386\CmRcViewer.exe"' $TEXT)
The command will just open SCCM remote connection window to the computer the user specifies in the text box.
Try this:
& "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386\CmRcViewer.exe" PCNAME
To PowerShell a string "..." is just a string and PowerShell evaluates it by echoing it to the screen. To get PowerShell to execute the command whose name is in a string, you use the call operator &.
To run or convert batch files externally from PowerShell (particularly if you wish to sign all your scheduled task scripts with a certificate) I simply create a PowerShell script, e.g. deletefolders.ps1.
Input the following into the script:
cmd.exe /c "rd /s /q C:\#TEMP\test1"
cmd.exe /c "rd /s /q C:\#TEMP\test2"
cmd.exe /c "rd /s /q C:\#TEMP\test3"
*Each command needs to be put on a new line calling cmd.exe again.
This script can now be signed and run from PowerShell outputting the commands to command prompt / cmd directly.
It is a much safer way than running batch files!
One solution would be to pipe your command from PowerShell to CMD. Running the following command will pipe the notepad.exe command over to CMD, which will then open the Notepad application.
PS C:\> "notepad.exe" | cmd
Once the command has run in CMD, you will be returned to a PowerShell prompt, and can continue running your PowerShell script.
Edits
CMD's Startup Message is Shown
As mklement0 points out, this method shows CMD's startup message. If you were to copy the output using the method above into another terminal, the startup message will be copied along with it.
For those who may need this info:
I figured out that you can pretty much run a command that's in your PATH from a PS script, and it should work.
Sometimes you may have to pre-launch this command with cmd.exe /c
Examples
Calling git from a PS script
I had to repackage a git client wrapped in Chocolatey (for those who may not know, it's a package manager for Windows) which massively uses PS scripts.
I found out that, once git is in the PATH, commands like
$ca_bundle = git config --get http.sslCAInfo
will store the location of git crt file in $ca_bundle variable.
Looking for an App
Another example that is a combination of the present SO post and this SO post is the use of where command
$java_exe = cmd.exe /c where java
will store the location of java.exe file in $java_exe variable.
You must use the Invoke-Command cmdlet to launch this external program. Normally it works without an effort.
If you need more than one command you should use the Invoke-Expression cmdlet with the -scriptblock option.

How to execute a PowerShell script from Notepad++

I am using Notepad++ to edit a PowerShell file and want to be able to execute that file from inside Notepad++.
How can I set that up?
It took me a little fiddling, but I finally got this working. (I am using version 1.0 but this should work in other versions as well.)
Notepad++ can be set up to run commands, and assign shortcuts to those commands, as follows:
From the menu, click Run → Run
Add the command
C:\NotepadRun.bat "$(FULL_CURRENT_PATH)"
Save the command, giving it a name and a key shortcut.
Below are the contents of the batch file. I named mine NotepadRun.bat, but you can name it whatever.
#echo off
GOTO %~sx1
:.ps1
cd "%~d1%~p1"
powershell.exe .\%~n1%~sx1
GOTO end
:.rb
ruby "%~f1"
GOTO end
:.php
php "%~f1"
GOTO end
:end
pause
As a note upgrading to Windows7 and Powershell 2 I found some Issues with this and have updated to passing in an ExecutionPolicy to ensure I can run the script I am editing.
:.ps1
cd "%~d1%~p1"
powershell -ExecutionPolicy Unrestricted -File "%~n1%~sx1"
GOTO end
See Using Notepad++ to Compile and Run Java Programs and replace "javac" with "C:Windows\system32\WindowsPowerShell\v1.0\powershell.exe" (or your path to PowerShell). (Caveat: I'm not a Notepad++ user and haven't tried this.)
That said, I'd just use PowerShell ISE (installs with PowerShell) or one of the other dedicated PowerShell IDEs instead.
I would recommend using PowerShell ISE which comes as part of PowerShell and designed specifically for Powershell.
You can run a saved script from "Run" -> "Run" menu in Notepad++ with the following command:
powershell.exe -noexit -command . \"$(FULL_CURRENT_PATH)\"
Based on the answers before:
powershell.exe -ExecutionPolicy Unrestricted -NoLogo -File "$(FULL_CURRENT_PATH)"
You can also add the -NoExit parameter to keep PowerShell from closing automatically:
powershell.exe -ExecutionPolicy Unrestricted -NoExit -NoLogo -File "$(FULL_CURRENT_PATH)"
Note: File has to be saved.