1
if ($PSVersionTable.PSVersion.Major,$PSVersionTable.PSVersion.Minor -join "." -gt 5.1) {   
    
Start-Process -FilePath "PowerShell.exe" -ArgumentList 'if((get-WindowsOptionalFeature -Online -FeatureName SmbDirect).state -eq "disabled"){Enable-WindowsOptionalFeature -Online -FeatureName SmbDirect -norestart}'
}

else
{
if((get-WindowsOptionalFeature -Online -FeatureName SmbDirect).state -eq "disabled"){Enable-WindowsOptionalFeature -Online -FeatureName SmbDirect -norestart}
}

even though I made it a one-liner, It still won't work. the result is a new PowerShell window opening for a split second and closing.

how can I make it work?

I studied the Argumentlist but couldn't find a way.

  • 1
    Why use start-process or call powershell again? – js2010 Dec 25 '22 at 17:38
  • @js2010 because https://github.com/PowerShell/PowerShell/issues/13866 –  Dec 25 '22 at 17:50
  • 1
    This works for me: `start-process powershell "if(1 -eq 1) {echo hi} ; pause"` – js2010 Dec 25 '22 at 17:59
  • 1
    You can call powershell.exe without `Start-Process` then all output goes to your console. – Santiago Squarzon Dec 25 '22 at 18:00
  • 1
    `get-WindowsOptionalFeature` runs smoothly in my *PowerShell 7.3.1*. Update your `pwsh`… – JosefZ Dec 25 '22 at 18:03
  • 1
    @SantiagoSquarzon Thank you so much, I didn't know I could do that, well that solved my problem and made it easier –  Dec 25 '22 at 18:06
  • @JosefZ I am using PowerShell 7.3.1 and when I type `Get-WindowsOptionalFeature -online` it waits for like 20 seconds and then show `Get-WindowsOptionalFeature: Class not registered`. installed from Microsoft store app. –  Dec 25 '22 at 18:07
  • 1
    glad to hear it :) normally one would use `Start-Process` for async calls or in some cases it is required like with `msiexec` where the `-Wait` is needed. but most of the times it is as simple as calling the executable from the shell itself – Santiago Squarzon Dec 25 '22 at 18:08

2 Answers2

0

Addressing the title of the question, this works ok for me. "powershell -command" is implied.

start-process powershell "if(1 -eq 1) {echo hi} ; pause"

hi
Press Enter to continue...:

This looks like a "losing the doublequotes problem". See also PowerShell stripping double quotes from command line arguments

powershell '"a"'

a : The term 'a' 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.

Swap the single and doublequotes.

Start-Process -FilePath "PowerShell.exe" -ArgumentList "if((get-WindowsOptionalFeature -Online -FeatureName SmbDirect).state -eq 'disabled'){Enable-WindowsOptionalFeature -Online -FeatureName SmbDirect -norestart}"

Not that start-process is needed.

PowerShell.exe "if((get-WindowsOptionalFeature -Online -FeatureName SmbDirect).state -eq 'disabled'){Enable-WindowsOptionalFeature -Online -FeatureName SmbDirect -norestart}"

That Enable-WindowsOptionalFeature command works for me in powershell 7.2.8.

js2010
  • 23,033
  • 6
  • 64
  • 66
  • Sorry but i still see a new PowerShell window flash. I added `;pause` to it to see what happens and all I saw was "Press Enter to continue...:" –  Dec 25 '22 at 18:24
  • Works for me. If state is enabled nothing will happen. – js2010 Dec 25 '22 at 18:28
  • You're right, it was enabled and I was expecting for output saying it's enabled, forgetting it's an `if` statement. –  Dec 25 '22 at 18:33
  • Your solutions are effective, but note that the problem you link to is a separate issue, which is unrelated to the use of `Start-Process` – mklement0 Dec 25 '22 at 23:24
0

tl;dr

  • To make your command work, escaped the embedded " chars in your -ArgumentList argument as \".

    • Alternatively, use embedded ' chars escaped as '', or use "..." for the outer quoting, in which case you can use ' as-is - but note that this bears the risk of up-front expansion of variable references and expressions, unless you escape $ instances as `$.
  • However, Start-Process may not be the best way to invoke your command; if synchronous invocation with output to the caller's console is desired / acceptable, use a script block to pass your command as-is (works from inside PowerShell only):

    PowerShell.exe { 
      if((get-WindowsOptionalFeature -Online -FeatureName SmbDirect).state -eq "disabled"){Enable-WindowsOptionalFeature -Online -FeatureName SmbDirect -norestart} 
    }
    

Background information:

js2010's answer provides effective solutions, but let me try to provide a systematic overview of the issues involved, to avoid potential misconceptions:

  • Calling powershell.exe, the Windows PowerShell CLI (or its PowerShell (Core) equivalent, pwsh, from PowerShell is rarely necessary.

  • As with any external program, you have two basic invocation choices:

    • Direct invocation (possibly via &, the call operator, which is situationally required for syntactic reasons).

      • Direct invocation is usually the right approach, as it executes the program synchronously, with the program's output streams connected to PowerShell's, which by default displays them in the caller's console.

      • From inside PowerShell, irrespective of whether you're calling the respective other edition on Windows, using a script block ({ ... }) is the best choice - see this answer.

    • Invocation via Start-Process, which by default executes asynchronously, in a new window, and is therefore only needed in special circumstances.


The fundamental problem with your own approach is that any " characters in the -ArgumentList argument must be escaped as \":

  • Passing a piece of PowerShell code to a PowerShell CLI's -Command (-c) parameter (by default with powershell.exe only, explicitly with pwsh) employs two parsing passes:

    • In short: unescaped " chars. are stripped during command-line parsing, and only then are the argument(s) interpreted as PowerShell code.

    • Any " characters you want to be part of the PowerShell code to evaluate, must be escaped.

      • Using embedded '...' quoting, if possible, is a way to avoid this need.
      • PowerShell itself, in both editions, always recognizes \" as an escaped ", but when calling from cmd.exe, different forms are needed in edge cases - see this answer.
  • When using Start-Process with a single-string -ArgumentList argument:

    • The (possibly preprocessed by PowerShell) string's verbatim value becomes part of the raw process command line that is constructed behind the scenes, so using \" to escape " chars. is enough.
  • When using direct invocation:

    • It is reasonable to expect PowerShell to automatically escape embedded " characters, such as when calling powershell.exe '"hi"'

      • However, due to a long-standing bug up to v7.2.x, this does not work as expected - see this answer.
    • In PowerShell 7.3.0 and 7.3.1, specifically, it does work as expected by default, however, a future update will likely require opt-in - see GitHub issue #18694

mklement0
  • 382,024
  • 64
  • 607
  • 775