none
String with spaces fails in start-process PowerShell.exe - what am i missing? RRS feed

  • Question

  • This is a very simplified version of a real world problem. (The real world script file is many thousands of lines (6800). I mention this is case someone is drawn to suggest that the code in script.ps1 in the problem below, is passed in as a scriptblock.)

    I am starting a new process from PowerShell.exe. It is another PowerShell instance. I am starting it as a different user, but that is probably not significant to the question. Into that process I am passing a script file name with a SPACE in the path. This fails.

    Here is the code content for the script.ps1 file.

    Write-Host "Sleeping"

    Start-Sleep -seconds 2

    Here is the code for the parent which starts script.ps1

    $PScred = get-credential 'domain\username'

    $ScriptFile = "C:\test space\script.ps1"

    Start-Process PowerShell.exe -Wait -Credential $PScred -WorkingDirectory 'C:\' -ArgumentList "-file $ScriptFile" -PassThru

    Expected behaviour is that PowerShell starts and the word sleeping is printed for two seconds.

    Actual behaviour is PowerShell starts and terminates with no output.

    I also tried this command - with the same results.

    Start-Process PowerShell -wait -credential $cred -ArgumentList "-file 'C:\test space\script.ps1'"

    If I specify a path with no spaces, it works no problems.

    I am wondering what I can do to overcome this behaviour, or if this is expected?

    Thank you.

     

    Wednesday, October 31, 2018 10:01 AM

Answers

  • So the answer I was looking for is the following.

    PowerShell -file 'C:\test space\filename.ps1' works

    The problem is that when I pass an argument to a System.Diagnostics.Process object with a Startinfo block specified (from PowerShell), (which is what Start-Process is doing under the covers anyway) the argument list is being interpreted by the PowerShell script parsing engine. 

    Consequently the string is not being passed as a quoted line and therefore fails as you would expect. The solution is the old escaping quotes trick. Obvious really. '" spaced string "'

    This is the answer I was looking for.

    $scriptFile = '"C:\test space\1.ps1"'
    [string[]]$argumentList = "-file"
    $argumentList +=  $scriptFile

    $start_Process_info =  New-Object System.Diagnostics.ProcessStartInfo
    $start_Process_info.WorkingDirectory = 'C:\Users'
    $start_Process_info.FileName = "$PSHOME\PowerShell.exe"
    $start_Process_info.Arguments = $argumentList

    $newProcess = New-Object System.Diagnostics.Process
    $newProcess.StartInfo = $start_Process_info
    $newProcess.Start() | Out-Null

    You can also do:

    $scriptFile = 'C:\test space\1.ps1'

    "`"$scriptfile`""

    and it works.

    Saturday, November 3, 2018 8:36 AM

All replies

  • What do you need Start-Process to start a new PowerShell session? You're already in PowerShell.

    -- Bill Stewart [Bill_Stewart]

    Wednesday, October 31, 2018 2:10 PM
    Moderator
  • What do you need Start-Process to start a new PowerShell session? You're already in PowerShell.

    -- Bill Stewart [Bill_Stewart]

    I am really looking for an answer to the question, not a debate on why I am asking. However in the interests of being good spirited, here is the response to what you ask.

    If you run your script as admin across your most critical business assets. An attacker only need compromise one account and your done. The organisation in scope was rather publicly taken out, almost to extinction by the notpetya virus of June 2017 (There was four big companies hit, take your pick.) Consequently, all sensitive operations are divided up and run under many administrative accounts to limit the damage possible, were one of the accounts running the process compromised. So for 5000 servers batches of 100 are run under one account, the next 100 under another account etc. It might seem like overkill, but after you've been though an IT extinction event, the alternative is much less appealing. With the advances in PowerShell these days, managing such complexity programmatically   isn't actually too hard anymore.

    Friday, November 2, 2018 10:37 AM
  • Well Bill.  You have now met someone who knows how to totally misunderstand the issue  and how to further cloud the original question. 

    The issue seems to be a need to run with alternate credentials.

    The issue is how to get spaces to work on a command line.

    Simple:

    $arglist = '-file "C:\test space\script.ps1"'
    Start-Process PowerShell.exe -Wait -Credential $PScred -WorkingDirectory 'C:\' -ArgumentList $arglist  -PassThru

    Now we can find out the true issue.


    \_(ツ)_/



    • Edited by jrv Friday, November 2, 2018 10:44 AM
    Friday, November 2, 2018 10:43 AM
  • The purpose and explanation are obscure to me.

    To run a process using a different user, we already have a command for that (runas) ?


    -- Bill Stewart [Bill_Stewart]

    Friday, November 2, 2018 2:29 PM
    Moderator
  • Isn't runas a native windows executable? Isn't calling those withing PowerShell frowned upon? I am asking your view, I don't know. 

    When it comes to passing parameters and arguments to native windows executables, I mean many parameters like 15 in our case, PowerShell makes dealing with that a good deal easier in my view. Runas is fine if you simply want to run mmc.exe or compmgmt.msc.

    I am saying, its using something I know how to use as opposed to learning how to use runas and then having to spend time figuring out how to get all my parameters and arguments to work correctly, via that method. Added to which the solution is in production and they are depending on it currently. I was really just curious, but if you don't know the answer that's fine too.

    Cheers.

    Friday, November 2, 2018 8:19 PM
  • Isn't runas a native windows executable? Isn't calling those withing PowerShell frowned upon? I am asking your view, I don't know. 

    There is no such rule or BP.  PS was designed to execute all native Windows Commands.

    Try this to see what PowerShell accepts as a command:

    Get-Command runas
    Get-Command -CommandType Application


    \_(ツ)_/

    Friday, November 2, 2018 8:28 PM
  • (?) If you are running powershell.exe from PowerShell, you are already "running a native Windows executable" (if you will) from PowerShell.

    It would help if you were to explain the goal/purpose of your question. As it stands, it sounds very much like an XY problem.


    -- Bill Stewart [Bill_Stewart]

    Friday, November 2, 2018 9:04 PM
    Moderator
  • The purpose was to help my understanding. No more or less. I'm not a trained programmer and I often get caught out by seemingly banal errors. I spent ages wondering why the instance of PS was not returning anything until I figured out that the script file parameter wasn't being passed to the PowerShell executable. Nothing I tried seemed to make it accept a space in the path. i wondered if there was a special way of doing it, or if I was doing something wrong, or if its a design limitation of some sort. The example at the start does illustrate the problem.
    Friday, November 2, 2018 10:03 PM
  • The code in PowerShell obeys the command line rules of "PowerShell"

    Read: "powershell /?"

    Commandlines in Windows break on spaces and other delimiter characters.  This has always been true for all WIndows commands.

    In PS we can help this by using an "arguments list" which is the same list that Windows uses when calling any executable programmatically.

    $arglist = @(
        '-file',
        'c:\my file name with spaces'
        'other args'
    )

    Or by adding quotes as I did above.


    \_(ツ)_/

    • Marked as answer by Anthony Guyon Friday, November 2, 2018 11:15 PM
    • Unmarked as answer by Anthony Guyon Saturday, November 3, 2018 7:53 AM
    Friday, November 2, 2018 10:37 PM
  • Ok thanks that's useful.

    I was forgetting the major reason for using Start-Process is, so that I could programatically pass in the credential argument. This allows be to pulled from a DPAPI protected string under the context of the instantiating user. This enables the use of very large (maximum length) complex passwords. RunAS AFAIK deliberately does not allow the use of stored credentials or allow credentials to be passed in. In any case policy is set to not allow the use of any delegated credentials. So a DPAPI protected string is the best option and that requires a mechanism to build a credential from it - hence the need to use PS to do it. Or C# of course.

    Friday, November 2, 2018 10:48 PM
  • Again - you are wrong.  "RunAs" allows for well protected credentials.  Run it once with creds and they are remembered.  After that the credentials are not needed for that particular command line.

    ALWAYS read all of the help for any command before tossing it out.


    \_(ツ)_/

    • Marked as answer by Anthony Guyon Friday, November 2, 2018 11:15 PM
    • Unmarked as answer by Anthony Guyon Saturday, November 3, 2018 7:53 AM
    Friday, November 2, 2018 10:52 PM
  • So the answer I was looking for is the following.

    PowerShell -file 'C:\test space\filename.ps1' works

    The problem is that when I pass an argument to a System.Diagnostics.Process object with a Startinfo block specified (from PowerShell), (which is what Start-Process is doing under the covers anyway) the argument list is being interpreted by the PowerShell script parsing engine. 

    Consequently the string is not being passed as a quoted line and therefore fails as you would expect. The solution is the old escaping quotes trick. Obvious really. '" spaced string "'

    This is the answer I was looking for.

    $scriptFile = '"C:\test space\1.ps1"'
    [string[]]$argumentList = "-file"
    $argumentList +=  $scriptFile

    $start_Process_info =  New-Object System.Diagnostics.ProcessStartInfo
    $start_Process_info.WorkingDirectory = 'C:\Users'
    $start_Process_info.FileName = "$PSHOME\PowerShell.exe"
    $start_Process_info.Arguments = $argumentList

    $newProcess = New-Object System.Diagnostics.Process
    $newProcess.StartInfo = $start_Process_info
    $newProcess.Start() | Out-Null

    You can also do:

    $scriptFile = 'C:\test space\1.ps1'

    "`"$scriptfile`""

    and it works.

    Saturday, November 3, 2018 8:36 AM
  • Start-Process is a simplified wrapper around that class. Why don/t you use it.

    Note also that single quotes cannot be used in place of double quotes on a command line.

    Quotes around a string do not count as quotes on a command line.

    With an arguments array we do not use quotes as I showed you above.

    You asked for credentials and now you don't use them. 

    Arguments in a "cargs" array in Windows code never require quotes.  That is the point of the array.

    I think you want to take some time to learn Windows technology before digging in deeply or you will spend a lot of time being confused and will write a lot more code than is required.


    \_(ツ)_/



    • Edited by jrv Saturday, November 3, 2018 9:13 AM
    Saturday, November 3, 2018 9:09 AM
  • This is the correct method for creating a process.

    $processstartInfo =  New-Object System.Diagnostics.ProcessStartInfo
    $processstartInfo.WorkingDirectory = 'C:\Users'
    $processstartInfo.FileName = "$PSHOME\PowerShell.exe"
    $processstartInfo.Arguments = '-file', 'C:\test space\1.ps1'
    $process = [System.Diagnostics.Process]::Start($processstartInfo) 
    

    You then use the "$process" object to manage the process.

    $process.Wait()

    As noted all of this is summarized with the Start-Process parameters. They all just set these values and return a process object

    Same thing one line:

     $process = Start-Process -FilePath powershell -ArgumentList '-file', 'C:\test space\1.ps1' -WorkingDirectory C:\Users -PassThru


    \_(ツ)_/

    Saturday, November 3, 2018 9:21 AM
  • There is also a more PowerShell way to run an independent script.  THis is how most coders would do what you are trying to do.

    $j = Start-Job -FilePath 'C:\test space\1.ps1' 
    $j | Wait-Job | Receive-Job

    This runs in the background and returns its output to the current PowerShell session when needed.

    We can also persist a job across sessions

    help Register-ScheduledJob -online

    This is why you need to start by actually learning PowerShell because there is no way you are going to learn correctly by guessing.

    Microsoft Virtual Academy - Getting Started with Microsoft PowerShell


    \_(ツ)_/

    Saturday, November 3, 2018 9:34 AM