Use variable for filepath parameter of Start-Process - powershell

I'd like run a .exe which could be in a number of locations.
$runpath = "$servicepackfolder\SQLServer2008SP1-KB968369-IA64-ENU.exe"
Start-Process -FilePath $runpath -arg "/x:.\$buildfolder\PCU"
Or this way, specifying the WorkingDirectory:
Start-Process 'SQLServer2008SP1-KB968369-IA64-ENU.exe' -WorkingDirectory $servicepackfolder -arg "/x:.\$buildfolder\PCU"
But it seems the variables are not being interpreted as strings.
Start-Process : This command cannot be
executed due to the error: The system
cannot find the file specified.
I am in the correct directory and if I take the output from the $runpath variable and substitute it for the variable in the Start-Process call, I get the expected behavior.
Will this work, or am I stuck hardcoding these paths. Trying to automate the slipstream build process for SQL 2008.

I can duplicate the behavior you see if I add -NoNewWindow but if I don't specify that parameter it works as expected for my test case:
start-process sizeof.exe -WorkingDirectory C:\temp -ArgumentList 1
The new window flashes up and goes away but I can see it is running the specified exe from my temp dir.

Better late than never, but I've found a workaround for this when having the same issue, not sure if it is classed as a bug or not -
Powershell doesn't always handle un-escaped backslashes or quotes in the strings that are stored in a variable / created by string processing all that well for -FilePath, so for your line:
$runpath = "$servicepackfolder\SQLServer2008SP1-KB968369-IA64-ENU.exe"
Try the following (or equivalent) before using $runpath:
$cleanpath = $runpath.replace("\","\\").replace('"',"")
The .replace("\","\\").replace('"',"") escapes the slashes and eliminates the quotes that the string handling and passing introduce, which seems to clear this issue (for some cases).
Bit late for you I imagine but hopefully this helps other people googling for this one.

Related

How to pass an array to the arguments in Start-Process in Powershell?

I am writing a script to play certain files in a player. I use Get-ChildItem to get an array of file names. Then I want to use Start-Process to play these files. However, how can I add these file names to the arguments of the player program?
I used Start-Process -FilePath "C:\Program Files\DAUM\PotPlayer\PotPlayerMini64.exe" -ArgumentList $selected_items but it seems it doesn't work and the files are not played.
Notice there are spaces in the file names.
Syntax-wise, your approach should work, but doesn't, due to an unfortunate bug, still present in PowerShell 7.2 - see GitHub issue #5576.
While passing an array of arguments to Start-Process's -ArgumentList parameter does cause the array elements to be passed as individual arguments (which is usually how external CLIs expect multiple file arguments), the necessary double-quoting around elements with spaces is not applied when the command line ultimately used for invocation is constructed behind the scenes.
Also, for robustness you should use the .FullName property of the objects stored in $selected_items, so as to ensure that full paths are passed, because - in Windows PowerShell, situationally - Get-ChildItem's output objects may stringify to the file name only - see this answer.
Workaround: Pass a single argument to -ArgumentList, in which you encode all pass-through arguments, using embedded double-quoting.
Start-Process `
-FilePath "C:\Program Files\DAUM\PotPlayer\PotPlayerMini64.exe" `
-ArgumentList ($selected_items.ForEach({ '"{0}"' -f $_.FullName }) -join ' ')
Taking a step back:
If PotPlayerMini64.exe is a Windows GUI(-subsystem) application, you don't need Start-Process at all, because even direct invocation will then act asynchronously (i.e., the program will launch, and control will return to PowerShell right away; conversely, if you wanted to wait for the program to exit, use Start-Process -Wait).
& "C:\Program Files\DAUM\PotPlayer\PotPlayerMini64.exe" $selected_Items.FullName
Note that in direct invocations such as this, PowerShell does perform the necessary double-quoting behind the scenes, on demand.
Note: I'm unclear on whether passing multiple file paths to PotPlayerMini64.exe alone also starts playback - the alternative solution in the next section may ensure that.
Alternative, clipboard-based solution:
Judging by PotPlayerMini64.exe's available command-line options[1], the following may work (I cannot personally verify):
/clipboard :Appends content(s) from clipboard into playlist and starts playback immediately.
# Copy the full names of the files of interest to the clipboard.
Set-Clipboard -Value $selected_items.FullName
# Launch the player and tell it to start playback of the files on the clipboard.
# Parameters -FilePath and -ArgumentList are positionally implied.
Start-Process 'C:\Program Files\DAUM\PotPlayer\PotPlayerMini64.exe' /clipboard
There are file-arguments-related options such as /new, /insert, and /add, but it's unclear to me whether they - or omitting them altogether, as in your attempt - automatically start playback (may depend on the application's persistent configuration).
[1] Note that this is not the official documentation; I couldn't find the latter.
You can ForEach-Object:
Get-ChildItem . | ForEach-Object {Start-Process -FilePath "C:\Program Files\DAUM\PotPlayer\PotPlayerMini64.exe" -ArgumentList $_.FullName}
You don't need start-process (plus, -argumentlist doesn't handle filenames with spaces as easily).
& "C:\Program Files\DAUM\PotPlayer\PotPlayerMini64.exe"
C:\Program` Files\DAUM\PotPlayer\PotPlayerMini64.exe
$env:path += ';C:\Program Files\DAUM\PotPlayer'; PotPlayerMini64
Even using start-process, it varies with the program. For example, Emacs can take multiple file arguments, separated by spaces. If the filename has a space, it would need to be double quoted. External programs don't know what arrays are, and start-process converts them to one string with spaces in between each element.
start emacs file1,file2,'"my file"'
get-wmiobject win32_process | ? name -eq emacs.exe | % commandline
"c:\program files\emacs\bin\emacs.exe" file1 file2 "my file"
ps emacs | % commandline # ps 7
"c:\program files\emacs\bin\emacs.exe" file1 file2 "my file"

Command Line Command Output in start-process from exe file

Here is the program. I am using dell command | configure. The command-line command is as follows:
"C:\Program Files (x86)\Dell\Command Configure\X86_64>cctk.exe" --wakeonlan
In Powershell you can navigate to the folder and run:
./cctk.exe --wakeonlan
I can pipe the above command into a variable and get the information I need. This requires my shell to cd into the folder accordingly and run accordingly.
$test = ./cctk.exe --wakeonlan
This will give you an output. However when you use start-process, you get no output as this is a command-line command. A cmd screen appears and runs the command. So, I added a -nonewwindow and -wait flags. The output now appears on the screen, but I can't seem to capture it.
$test = start-process "C:\Program Files (x86)\Dell\Command Configure\X86_64\cctk.exe" -ArgumentList #("--wakeonlan") -NoNewWindow -Wait
At this point test is empty. I tried using the Out-File to capture the information as well. No success. The command outputs to the screen but nowhere else.
I also tried the cmd method where you pipe the information in using the /C flag.
$test = Start-Process cmd -ArgumentList '/C start "C:\Program Files (x86)\Dell\Command Configure\X86_64\cctk.exe" "--wakeonlan"' -NoNewWindow -Wait
However, I have tried many variations of this command with no luck. Some say C:\Program is not recognized. Some just open command prompt. The above says --wakeonlan is an unknown command.
Any pointers would help greatly.
There are various ways to run this without the added complication of start-process.
Add to the path temporarily:
$env:path += ';C:\Program Files (x86)\Dell\Command Configure\X86_64;'
cctk
Call operator:
& 'C:\Program Files (x86)\Dell\Command Configure\X86_64\cctk'
Backquote all spaces and parentheses:
C:\Program` Files` `(x86`)\Dell\Command` Configure\X86_64\cctk
To elaborate on js2010's helpful answer:
In short: Because your executable path is quoted, direct invocation requires use of &, the call operator, for syntactic reasons - see this answer for details.
To synchronously execute console applications or batch files and capture their output, call them directly ($output = c:\path\to\some.exe ... or $output = & $exePath ...), do not use Start-Process (or the System.Diagnostics.Process API it is based on) - see this answer for more information.
If you do use Start-Process, which may be necessary in special situations, such as needing to run with a different user identity:
The only way to capture output is in text files, via the -RedirectStandardOutput / -RedirectStandardError parameters. Note that the character encoding of the output files is determined by the encoding stored in [Console]::OutputEncoding[1], which reflects the current console output code page, which defaults to the system's active legacy OEM code page.
By contrast, even with -NoNewWindow -Wait, directly capturing output with $output = ... does not work, because the launched process writes directly to the console, bypassing PowerShell's success output stream, which is the one variable assignments capture.
[1] PowerShell uses the same encoding to decode output from external programs in direct invocations - see this answer for details.

How to run an executable (exe) by providing a config file in powershell

I'm trying to run an exe in the background by providing a config file (yml in my case)
Tried the below, however this is not pushing the execution to background. -
./my.exe start --config-file $my_config_file
Found 'start-process' command which are specifically used for this case. With argument list is there any way to send the config file?
Start-Process -Wait -FilePath "my.exe" -ArgumentList
Remove the -Wait argument and pass the process arguments as an array via -ArgumentList parameter:
Start-Process -FilePath "my.exe" -ArgumentList 'start', '--config-file', "`"$my_config_file`""
The strange quoting for $my_config_file is required because a path may contain spaces. Start-Process does not do automatic quoting. From the docs:
If parameters or parameter values contain a space, they need to be surrounded with escaped double quotes.
Note that you won't receive output of the started process, if that matters to you. You can redirect to a file, using parameters -RedirectStandardOutput and -RedirectStandardError, but you can't (easily) store the output in a variable.
A way to start a process in the background, while being able to receive its output, is to create a job.

PowerShell run Ultraedit Script

Is it possible to run an UltraEdit macro or script from the PowerShell? Something like following:
uedit64.exe c:\temp\test.txt /s,e="c:\temp\script.js"
I have nothing special. I just want to open the log file with UltraEdit and as soon the log file is opened the UltraEdit Script should be executed on that. The following code opens the log file but does not execute the script on that.
$ultraEdit = "C:\...\UltraEdit\uedit64.exe"
$logFile = "C:\...\res.log"
$scriptFile = "C:\...\ultraEditScript.js"
Start-Process -FilePath $ultraEdit -ArgumentList "$logFile /s=`"$scriptFile`""
Absolutely! Powershell has a few different "call" operators.
https://ss64.com/ps/call.html
Take a look at the documentation for Start-process.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-6
Start-Process -FilePath "c:\pathtoexe\uedit64.exe" -ArgumentList "c:\temp\test.txt /s,e=`"c:\temp\script.js`""
Should work for you (change the path of course.
Yes, it is possible. The problem with your current example is surrounding quoting rules with arguments:
uedit64.exe c:\temp\test.txt '/s,e="c:\temp\script.js"'
This form should work. When you use commas, powershell will interpret that as an array. The safest way to pass arguments to an external executable is to use the stop-parser operator (--%) to avoid powershell's interpretation, but note this falls back to the cmd parser on Windows:
#requires -Version 3
uedit64.exe --% C:\Temp\test.txt /s,e="C:\Temp\script.js"
What the difference in parsers means is that you can't expand variables (if you wanted $path\script.js) in the arguments after the stop-parser, but you can still utilize environment variables using the batch syntax %VAR%.
As a best-practice, it's recommended to fully-qualify your path and use the call operator for clarity:
& C:\Temp\uedit64.exe
Thanks everyone,
The problem was with Select-String that split the matched lines, therefore, the script did not perform any action due to improper file structure.
These two works great :-)
1. & $ultraEdit /fni $logFile /S=$scriptFile
2. Start-Process -FilePath $ultraEdit -ArgumentList "$logFile /S=$scriptFile"

Invoke-Expression -Command Not Finding File Even Though It Exists

all,
I am trying to run a command in Power Shell and I am told the file does not exist as it stands right now for the Power Shell code. If I do the same in DOS, it finds the file and executes correctly. The only difference is that there is an additional open and closed double quote around the file name. Can this be done in Power Shell the same way? If so, how can this be done as I am not familiar with Power Shell.
DOS:
IF EXISTS F:\lvcecstos.CSV F:\LaserVault\ContentExpress\ContentExpress.exe /CID:1 /CSV:<b>"F:\lvcecstos.CSV"</b> /ClearSubscription
Power Shell:
Invoke-Expression -Command "F:\LaserVault\ContentExpress\ContentExpress.exe /CID:1 /CSV:<b>F:\lvcecstos.csv</b> /ClearSubscription"
Thanks in advance for any suggestions. I am told by the Laservault engineers that created this software package that we use that the double quotes around the file name is required. Doesn't make sense to me as to why though, but this is something I am unable to get around.
You can add double quotes around the file path as follows:
Invoke-Expression -Command 'F:\LaserVault\ContentExpress\ContentExpress.exe /CID:1 /CSV:"F:\lvcecstos.csv" /ClearSubscription';
Notice how the command is wrapped in single quotes instead of double quotes.
Ultimately, it depends on how the ContentExpress.exe program is interpreting the file path, though. For all you know, it could be appending the valued passed to "CSV" to the "current working directory."
You can also use the Start-Process cmdlet, instead of Invoke-Expression or Invoke-Command.
$Program = 'F:\LaserVault\ContentExpress\ContentExpress.exe';
$ArgumentList = '/CID:1 /CSV:"F:\lvcecstos.csv" /ClearSubscription';
Start-Process -Wait -NoNewWindow -FilePath $Program -ArgumentList $ArgumentList;
If you have PowerShell v3 (which you should) you could use the "magic parameter" --% (see here).
& F:\LaserVault\ContentExpress\ContentExpress.exe --% /CID:1 /CSV:"F:\lvcecstos.csv" /ClearSubscription
Otherwise trying to preserve double quotes around arguments could become very, very painful. See for instance this answer to a similar question.
$lvcecstos = "F:\lvcecstos.CSV"
If(Test-Path $lvcecstos)
{
Invoke-Expression -Command "& F:\LaserVault\ContentExpress\ContentExpress.exe /CID:1 /CSV:$lvcecstos /ClearSubscription"
}