Skip to content

Script via STDIN does not exit with error code and does not stop correctly #15331

Closed

Description

@Scordo

Steps to reproduce

Link to a related Problem in GitLab:
https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27830#note_564599211

Test.ps1:

$ErrorActionPreference = 'Stop'
just bogus
Write-Host 'hello'

Batch-Call (working correctly with file)

pwsh -File Test.ps1

Exit-Code: 1

Batch-Call (working correctly with stdin)

type Test.ps1 | pwsh -noprofile -noninteractive -command "$input | iex"

Exit-Code: 1

Batch-Call (not working correctly with stdin)

type Test.ps1 | pwsh -Command -

Exit-Code: 0

Expected behavior

Powershell quits with non zero exit code and does not print "hello", but an exception for "just bogus"

Actual behavior

Powershell does execute all statement even though $ErrorActionPreference is set to "stop"

Activity

  1. added
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    on Apr 30, 2021
  2. mklement0 commented on Apr 30, 2021

    @mklement0
    Contributor

    Good catch.

    Unfortunately -Command - (and -File - too) have fundamental problems: #3223

    It is the line-by-line (statement-by-statement) execution described in the linked issue that explains the behavior.

  3. Scordo commented on Apr 30, 2021

    @Scordo
    Author

    Maybe you should introduce new parameters in 7.2 to fix the issues and remain compatible to all the usages living with the current buggy behavior.

  4. SteveL-MSFT commented on Apr 30, 2021

    @SteveL-MSFT
    Member

    Scordo I think a new parameter would be a better solution than making changes to how -Command - and -File - work today as someone else may be dependent on that behavior. So it seems the desire is to have a -Stdin switch that reads the entirety of stdin and executes as a single script rather than line by line and have a non-zero exit code if $? is $false indicating an error happened?

  5. Scordo commented on Apr 30, 2021

    @Scordo
    Author

    Yes -stdin as a new parameter would be fine. It should behave exactly the same as when running a .ps1 file. Respecting $ErrorActionPreference and so on. It would be perfect if we could also pass parameters to the script that was provided via `-stdin.

  6. mklement0 commented on Apr 30, 2021

    @mklement0
    Contributor

    Steve Lee (@SteveL-MSFT), the current behavior of -Command - and especially -File - is unlikely to be relied upon in code with long-term stability concerns - it isn't suited to robust scripting.

    Therefore, let me suggest we consider fixing the existing parameters an acceptable bucket 3: Unlikely Grey Area breaking change.

    To minimize the impact, fixing -File - to work as Scordo describes would be sufficient - though I suggest including argument support while we're at it (-Command - has no counterpart in POSIX-compatible shells; - as the (positionally implied) script file name treats stdin input as if it were the content of a script).

    Conversely, a -Stdin parameter would not only be nonstandard (not aligned with POSIX-compatible shells), but add to the already sizable number of parameters.

  7. added
    Review - CommitteeThe PR/Issue needs a review from the PowerShell Committee
    and removed
    Review - CommitteeThe PR/Issue needs a review from the PowerShell Committee
    on Apr 30, 2021
  8. JamesWTruher commented on Apr 30, 2021

    @JamesWTruher
    Collaborator

    I think in this case, you could use a trap statement - here's an example of something that may serve you.

    PS /> echo "trap { write-host `$_; exit 2 } `$ErrorActionPreference = 'stop';get-date;just bogus;get-location" | pwsh -nopro -command -       
    
    Friday, April 30, 2021 12:57:55 PM
    The term 'just' is not recognized as a name of a cmdlet, function, script file, or executable program.
    Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    
    PS /> $LASTEXITCODE                                                                                                                    
    2
    

    or a more complicated example:

    PS /> echo "trap { write-host `$_;if ( `$ErrorActionPreference -eq 'continue') {continue}else{exit 2} } `$ErrorActionPreference = 'continue';get-date;just bogus;get-location" | pwsh -nopro -command -  
    
    Friday, April 30, 2021 12:59:53 PM
    The term 'just' is not recognized as a name of a cmdlet, function, script file, or executable program.
    Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    
    Drive        : /
    Provider     : Microsoft.PowerShell.Core\FileSystem
    ProviderPath : /
    Path         : /
    
    
    PS /> $LASTEXITCODE                                                                                                                                                                                    
    0
    
  9. jborean93 commented on Apr 30, 2021

    @jborean93
    Collaborator

    You could also try running it in it's own scriptblock like the following.

    PS /> echo '& { $ErrorActionPreference = "stop"; get-date; just bogus; get-location }' | pwsh -nopro -command -
    

    This will encapsulate the command you want to run as 1 scriptblock and execute that rather than each statement as 1 line.

  10. daxian-dbw commented on Apr 30, 2021

    @daxian-dbw
    Member

    The Enter-AzShell cmdlet from the powershell sub kernel in .NET Interactive depends on the current behavior of -File -. It depends on the behavior that -File - prints the prompt to detect the end of a command line execution.

    There may be other uses that depend on the current behavior of -command - and -file -, so I think we should keep it unchanged and adding new syntax for reading the whole stdin content as file content. Maybe -file --?

  11. mklement0 commented on May 1, 2021

    @mklement0
    Contributor

    Dongbo Wang (@daxian-dbw):

    (On a technicality, I claim that the silence promised in #10238 (comment), doesn't take effect until after 11:59:59 PM US EDT.)

    While I can appreciate your due diligence here, what you're pointing out here is evidence of a fundamentally broken culture:

    A different department in the same company decided to release an official feature based on working around obviously broken/useless behavior in a product released by a different department in the same company - rather than seeking a shared understanding and joint resolution.

  12. pedropombeiro commented on May 1, 2021

    @pedropombeiro

    You could also try running it in it's own scriptblock like the following.

    PS /> echo '& { $ErrorActionPreference = "stop"; get-date; just bogus; get-location }' | pwsh -nopro -command -
    

    This will encapsulate the command you want to run as 1 scriptblock and execute that rather than each statement as 1 line.

    This might work for small scripts, but if one is running 3rd-party scripts (such as in a CI solution), there's no telling how long those lines could become, so this doesn't seem like an all-round solution.

  13. 11 remaining items

  14. FaustinCarter commented on Jan 19, 2022

    @FaustinCarter

    Is there any movement on this? This is making using pwsh on docker containers in Gitlab completely unusable.

  15. jborean93 commented on Jan 19, 2022

    @jborean93
    Collaborator

    I wouldn't say it's completely unusable there are workarounds that make this entirely possible. The method I usually use is this

    docker run \
      --rm \
      --interactive \
      mcr.microsoft.com/powershell pwsh -Command - << 'EOF'
    & {
        if ($true) {
            echo "hi"
        }
    }
    
    EOF
    
    Basically make sure there is an empty line between `}` and `EOF` so the input is accepted by pwsh and also put all your code inside a `&{}` block to ensure it is run as 1 script rather than the line by line parser that is the standard console input code.
  16. FaustinCarter commented on Jan 20, 2022

    @FaustinCarter

    That's fine for when I'm the one launching the image. The real issue is when I'm trying to use GitLab's pipeline features where I don't really have control over how they launch the image and inject the script. I just get to add lines to a script tag in a yaml file that gets executed by a runner.

  17. God-damnit-all commented on Nov 25, 2022

    @God-damnit-all
    Contributor

    Here's what I came up with:
    type Test.ps1 | pwsh -NoP -c "$code=$Input|&{process{$_}};[ScriptBlock]::Create($code-join[char]10).Invoke()"

    And a longer alternative that avoids having to use [char]10:
    type Test.ps1 | pwsh -NoP -c "$code=[Text.StringBuilder]'';$Input|&{process{$null=$code.AppendLine($_)}};[ScriptBlock]::Create($code.ToString()).Invoke()"

    If you want to have an argument, set an environmental variable to something like ARG and call Invoke like this: .Invoke($env:ARG)

    Or if you have multiple arguments, ARG1 and ARG2, calling .Invoke like this: .Invoke($env:ARG1,$env:ARG2)

    You could also put a long string into an env variable with some sort of separator and then call .Split(seperator) or use -split seperator within Invoke

  18. SteveL-MSFT commented on Nov 28, 2022

    @SteveL-MSFT
    Member

    Michael Klement (@mklement0) I understand the desire to fix the behavior of -file - to align with precedent of other shells, but given the examples of using it as a REPL, it seems less likely to be a bucket 3 item.

    I think the best solution at this time is to document the workaround as the supported way to have a multi-line script executed at once vs line-by-line. This would work on all versions of PowerShell whereas a new switch would only work on the latest while the workaround can still be used anyways.

  19. FaustinCarter commented on Nov 29, 2022

    @FaustinCarter

    Steve Lee (@SteveL-MSFT) Do you mind summarizing what the recommended way to handle this is? There have been a few suggestions in this thread so far, but it's not entirely clear to me what the right way to proceed is.

  20. SteveL-MSFT commented on Dec 5, 2022

    @SteveL-MSFT
    Member

    Faustin Carter (@FaustinCarter) I think the best practice would be to have the script in a .ps1 and execute it that way instead of using STDIN. Jordan Borean (@jborean93) has a viable workaround #15331 (comment) for cases where you can't write to a temporary .ps1

  21. God-damnit-all commented on Dec 5, 2022

    @God-damnit-all
    Contributor

    Faustin Carter (@FaustinCarter) I think the best practice would be to have the script in a .ps1 and execute it that way instead of using STDIN. Jordan Borean (@jborean93) has a viable workaround #15331 (comment) for cases where you can't write to a temporary .ps1

    It's a great stop-gap solution for what I hope will only be a temporary problem.

  22. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  23. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  24. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  25. microsoft-github-policy-service commented on Nov 23, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions