The params keyword lets you add parameters to a Powershell script, like so:
param (
[switch] $UseDefaults = $false
)
But it has to be added to the first line of the script. A shebang allows us 'nix users to have an easier, more intuitive time starting the script (./script.ps1 instead of pwsh script.ts1) and that can be done by adding this to the start of the script:
#!/usr/bin/env pwsh
However, that also has to be added to the first line of the script. Hence, these two features don't seem to be able to work together. Is there a way to use both these features in one script?
Comments and
usingstatements are allowed to precedeparam()declarations, and since a shebang line is technically a comment, it works just fine; for example:If you save the above to, say,
sample.ps1, make it executable withchmod a+x sample.ps1, then invoke with./sample.ps1 -Foo bar, you'll see:Important: As you note, in order for Unix-like platforms to recognize a shebang line as such, the file must not have a BOM. That is, be sure to save your files as UTF-8 without a BOM.[1]
Note:
The use of
/usr/bin/envto launchpwshassumes that the latter is in one of the directories listed in thePATHenvironment variable.-noprofilesuppresses loading of PowerShell's profile files, which - unfortunately - are loaded by default.To support passing this extra argument via the shebang line,
-Smust be passed toenv, for technical reasons (the system passes everything after the executable path as a single argument to that executable;-Smakesenvsplit that string into individual arguments).There is no technical need to use filename extension
.ps1in naming your script; in fact, if you want your script to simply function as a general purpose CLI that can be called from any shell, you may choose to use no filename extension, e.g. to simply name your scriptsample.Conversely, however, if you want to retain the ability to execute your script in-process from inside a PowerShell session, extension
.ps1is a must.A caveat is that calling shebang line-based scripts from outside PowerShell or - also from inside PowerShell - a shebang line-based script without extension
.ps1invariably has syntax and data-type limitations (the latter also invariably run in a child process), due to the fact that given a shebang line results in invocation via the-Fileparameter ofpwsh, the PowerShell (Core) CLI; notably:You can not pass array arguments (
foo, bar).You can not pass hashtables (
@{ ... }) or script blocks ({ ... })Generally, it seems that shebang line-based PowerShell scripts haven't really caught on (yet?), possibly due to a combination of the following factors:
pwshCLI.Closedlink to also see still-relevant issues that have simply been closed due to inactivity.Guidance re if and when to use shebang line-based PowerShell scripts:
Do NOT create shebang line-based scripts...
... if you're primarily calling your scripts from inside PowerShell.
PowerShell runs
.ps1scripts in-process, which not only means faster execution, but also enables rich (non-serialized) data type-support based on .NET types.PowerShell permits invocation of
.ps1scripts even without their filename extension, so that you can call./sample.ps1as just./sample, for instance.DO create shebang line-based scripts...
... if you primarily use a different shell, say
bash, but need to invoke PowerShell-based code on occasion, and want the convenience of invoking via, say,./sample.ps1rather thanpwsh -noprofile sample.ps1... if you want to implement a general-purpose CLI whose implementation language just so happens to be PowerShell.
In either case:
The script needs to be designed with the syntax and data-type limitations of a
-File-based PowerShell CLI invocation in mind.Callers must be aware of PowerShell's parameter syntax, which generally differs from that of POSIX (single-letter option names such as
-f) and GNU utilities (with multi-letter option names requiring--, e.g.--color).That said, even though PowerShell-idiomatically parameters are multi-letter but only require
-(e.g.-Path) in-File-based invocations (only), which includes shebang line-based invocations,--is accepted too (e.g,--Path).However, an important difference is that in PowerShell
-?rather than--hor--helpmust be used to invoke command-line help.[1] It is only Windows PowerShell, the legacy, Windows-only edition of PowerShell that requires a BOM in order to recognize a UTF-8 file as such.