Powershell: Support `-l` for pwsh.exe

Created on 19 Apr 2017  Â·  9Comments  Â·  Source: PowerShell/PowerShell

I hit a problem with vscode integrated terminal after changing my default shell to powershell on macOS.

https://github.com/Microsoft/vscode/issues/25035

I don't know how common is it to use -l as -noprofile in other shells, but if it's a fairly common interface we may want to support the same in ps.

Issue-Discussion Resolution-Fixed

Most helpful comment

It's a shame that at least -l can't be tackled. I've attempted to switch to using Powershell as my login shell but at least 2 tools I use on a daily basis expect -l to at least not reply with an "Invalid argument" error.

All 9 comments

It looks like sh, bash, fish, and zsh all support -l to mean it is a logon shell. ksh does not seem to support this parameter.

That said, it looks like -l is roughly the opposite of -noprofile, see:

https://unix.stackexchange.com/questions/38175/difference-between-login-shell-and-non-login-shell
http://stackoverflow.com/questions/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

Note that because PowerShell doesn't use the environment for variables, non-login interactive shells need to run the profile whereas *nix shells mostly don't need to.

Based on this - it feels like adding -l would make things more confusing.

Just to add some more background (note that ksh _does_ support -l, at least as of v93u+):

If you pass a command (with -c) or script filename to a POSIX-like shell binary on startup, you get a _non-interactive non-login_ shell in which _no_ profile/initialization files are sourced by default - which is the opposite of PowerShell's default behavior.

While the POSIX shell spec doesn't specify -l for sh, all major POSIX-compatible shells support it (dash, bash, ksh, zsh).

Note that fish neither is nor aspires to be POSIX-compatible.

-l indeed explicitly creates a _login_ shell, which invariably sources the login profile(s) (e.g., ~/.bash_profile in Bash) - whether the shell is interactive or not.
bash, ksh and zsh (but not dash) also support the more verbose alias --login.

This is in contrast with the initialization file that is by default sourced for _non-login_ shells - but _only if they're interactive_ - such as ~/.bashrc.
While not commonly used, you can force a shell to be interactive with option -i even when passing it a command / script filename.


To contrast PowerShell's command-line behavior with that of POSIX-like shells:

  • PowerShell neither distinguishes between login and non-login shells, nor does it have distinct initialization files associated with that distinction (thankfully).

  • PowerShell _always_ loads its profiles by default, and -noprofile must be used to suppress that.
    POSIX-like shells do _not_ load any profiles/initialization files by default in _non-interactive_ shells (when a command or script filename is passed, when a script is executed via its shebang line, when a non-interactive shell creates subshells or child shells).
    _This discrepancy is problematic, I think._

    • POSIX-like shells started with -l / --login _always_ source their *profile file(s), even for non-interactive shells.

    • _Interactive_ POSIX-like shells _not_ started with -l / --login source their *rc file(s). (zsh, as the (habitual) lone dissenter, also does so even with -l / --login); you can force creation of an interactive shell with -i.

  • PowerShell and POSIX-like shells alike automatically exit the session when a command / script filename is passed.

    • POSIX-like shells do so _invariably_; curiously, there is no (directly supported) way to execute commands on startup and then enter an interactive session.

    • PowerShell only exits _by default_ and commendably allows the session to stay open via its -NoExit switch.

  • PowerShell's -Command / -File parameters _roughly_ work like their POSIX counterparts (-c / by default), but there are problematic differences.

Sure enough, I didn't look closely enough at the error message I saw when running ksh -l under WSL - it was an error running /etc/profile.

I do believe loading profiles for non-interactive sessions is a theoretical if not real problem. For example, my IT department forgets to use -noprofile all the time, meaning I can interfere with their scripts easily, on purpose or not.

@mklement0 thank you for the detailed overview. Indeed, I was not aware of interactive vs login difference.

Maybe we should rename the issue in something like "Should we change default loading profile behavior"?

My €.02 is that the unix shells have a more sensible default, and we should take the opportunity now with the move to core to get things right.

@vors Happy to have this issue renamed/repurposed - my apologies for hijacking it somewhat: I hadn't fully read the initial problem, so just to address that:

In the context of specifying a shell for VSCode, you _do_ want the profile/initialization scripts to be sourced (using the example of bash):

  • On Linux, terminal programs create interactive _non-login_ shells by default. Linux users keep their initialization commands in ~/.bashrc, because that file is the _only_ (user-level) file that is sourced automatically when such a shell is created.

    • VSCode's default configuration emulates this behavior by passing _no_ arguments to /bin/bash on Linux, which creates the same type of shell (interactive, non-login - when run in a terminal).
  • On macOS, by contrast, the default terminal program creates interactive _login_ shells - that is, /bin/bash is always implicitly started with -l. The _only_ (user-level) file that Bash _login_ shells automatically source is ~/.bash_profile (not also ~/.bashrc). Therefore, some macOS users keep all their initialization commands in ~/.bash_profile only.

    • VSCode's default configuration emulates this behavior by passing -l to /bin/bash on macOS, which creates a logon shell.

These platform differences are unfortunate, but they ultimately go back to the questionable behavior of not _also_ loading ~/.bashrc in an interactive _login_ shell.
Arguably, the aspects of login and interactive should be separate: source ~/.bashrc whenever the shell is _interactive_ (whether also a login shell or not), and source ~/.bash_profile whenever the shell is a _login_ shell (whether also interactive or not).
(Presumably, Bash was following historical practice there.)

To compensate for this quirk, it is common practice to _manually_ source ~/.bashrc from ~/.bash_profile (sometimes complemented with a check inside ~/.bashrc that aborts its loading if the shell is found to be _non-interactive_), by adding an _explicit command_ to ~/.bash_profile.
However, this is nothing more than a (inconsistently adhered-to) _convention_.

Fortunately, PowerShell has no technical debt in that specific regard, but it is already at odds with respect to -NoProfile and -NonInteractive, both of which invert the logic of POSIX-like shells.

I've created #3743 for a more comprehensive look at aligning the PowerShell CLI with that of POSIX-like shells.

It's a shame that at least -l can't be tackled. I've attempted to switch to using Powershell as my login shell but at least 2 tools I use on a daily basis expect -l to at least not reply with an "Invalid argument" error.

The whole login vs non-login shell semantics isn't something easily solved since we didn't take the breaking change with 6.0. However, it seems a workaround to enable users to use pwsh as their default (like in VSCode) would be to have -l as -LoadProfile which is effectively a no-op for now.

Was this page helpful?
0 / 5 - 0 ratings