Powershell: Is it worth adding an additional line continuation mechanism?

Created on 1 Dec 2016  路  22Comments  路  Source: PowerShell/PowerShell

Line continuation in PowerShell suffers from at least two problems:

  1. The backtick char is hard to see in PowerShell script:
Get-Module pester ` 
| Foreach { Get-ChildItem $_.ModuleBase -recurse about_*.txt} `  
| Foreach {$_.BaseName -replace '\.help',''} | Select-Object -Unique | Sort-Object

Escaping a char like _ makes the line continuation more obvious:

Get-Module pester `_ 
| Foreach { Get-ChildItem $_.ModuleBase -recurse about_*.txt} `_  
| Foreach {$_.BaseName -replace '\.help',''} | Select-Object -Unique | Sort-Object
  1. The current line continuation mechanism is an escape of a newline (invisible) character. As a result, line continuation is broken if the user has any sort of whitespace (space or tabs) after the backtick since <backtick><space>|<tab> does not signify line continuation.

The second issue is impossible to see via casual inspection of script and is common when you copy/paste script from a website. By using an escaped char like <backtick>_ there is no longer any issue with whitespace after this line continuation char. That whitespace can safely be ignored by the parser. For an example of this problem, copy the first script sample above and paste into a PowerShell console. It will fail to run because there are spaces after the backticks. :-)

FWIW I'm not sure escaping _ is the best choice but I do think escaping a "visible" character has the benefit of being more obvious. I would love to ditch the escaping and just use a dedicated continuation character like _ (which VB uses) but that is tough with PowerShell where I can have a function named _.

Another option might be to use a double escape:

Get-Module pester `` 
| Foreach { Get-ChildItem $_.ModuleBase -recurse about_*.txt} ``  
| Foreach {$_.BaseName -replace '\.help',''} | Select-Object -Unique | Sort-Object

I believe such a change would not interfere with the existing line continuation mechanism so existing scripts would continue to work.

Migrated from #2751

Issue-Discussion Resolution-Answered WG-Language

Most helpful comment

It's pretty hard to create a good shell that doesn't have some way to split command lines because they can get pretty long. I chose backtick because it was _similar_ to \ (and I couldn't choose \ for obvious reasons). Comparing PS to C# is a bit disingenuous they target different usage patterns - C# has a single statement end operator - requiring ; at the end of every command would be a pretty rotten interactive shell experience.
Bruce and I did think a lot about this in the early days, and _tried_ to be faithful to Unix shell traditions and concepts, even if we weren't always faithful to their _implementation_, given all the platform considerations.

All 22 comments

Why? I like the used of the backtick. I've been doing this for a long time and I like it. So, why change something that works?

By the way, a script with code having backtick copy/paste to a console work. I use it all the time.

:)

If you read the proposal all the way through, it mentions that this would not change the existing mechanism. You can't because that would break too many scripts.

This issue is meant to discuss if it is worth adding another mechanism for line continuation. I know, right? Having more than one way to do something in PowerShell is SHOCKING I tell you. Completely unheard of. ;-)

How about adding ... as an optional continuation token? 馃

Sure, there could be a function with that name, but it would kinda make sense...
(pun intended)

The general best practice is to not use the backtick for line continuation. Some times you can just restructure your command like this:

Get-Module pester | 
    Foreach-Object { Get-ChildItem $_.ModuleBase -recurse about_*.txt} | 
    Foreach-Object {$_.BaseName -replace '\.help',''} | 
    Select-Object -Unique | 
    Sort-Object

This is also where Here-Strings and Splatting come in handy. I tend to teach people these alternate approaches instead of introducing the backtick.

@KevinMarquette Keep in mind that neither breaking on | or using splatting is an option when invoking a native executable with a lot of lengthy parameters. That said, when I can avoid backtick I too try to avoid it.

There are options for that too.

$MSIArguments = @(
    "/i"
    ('"{0}"' -f $file.fullname)
    "/qn"
    "/norestart"
    "/L*v"
    $logFile
)               
Start-Process "msiexec.exe" -ArgumentList $MSIArguments

I should clarify that I am not opposed to having a much better way to express line continuation.

I personally don't feel it's necessary to add a new language construct. Instead of that first what came on my mind was better text editors support.

A lot of tokens is highlighted in editors, so why not backtick (with Cyan, Salmon, or any other nice color)? And the rest of the line behind the backtick might be in other color with meaning "warning, this might be wrong".

Backticks are the devil. We all have widescreen monitors nowadays and know how to use the horizontal scroll bar :). Joking aside, I have found long running lines could be broken into separate lines entirely and often run faster since they are avoiding the pipeline. Multiple pipes just equal slow and lazy IMO.

@stej there is help for this in Visual Studio Code. I put this setting in my user settings file:

 "files.trimTrailingWhitespace": true,

And PSScriptAnalyzer has a rule PSMisleadingBacktick that will catch these. But not everyone uses VSCode or PSScriptAnalyzer.

Nice one @rkeithhill!

I'm using VS Code for lightweight coding and I like it. Thanks for sharing the "files.trimTrailingWhitespace": true setting.

:)

You most certainly can splat to native commands.
The catch is that native _switch_ commands don't accept "true" values ;-)

E.g.

tf vc status /format:detailed

Could just as easily be:

$fmt = @{"format"="detailed"}
$cmd = "vc","status"
tf @cmd @fmt

I don't like line continuation characters. I wish we didn't have the one we have (Yay C# -- Boo VB).

One problem with line continuation characters is that the only reason for them to exist is to enable people to stay within some arbitrary line width. Next thing you know you've got people trying to make a 249 character file path somehow fit on 80 character lines by splitting it into many strings and appending them together -- as if that actually _improved_ readability.

The reason that I like staying within some "arbitrary line width" is because a lot of devs run portrait monitors. Having a 100 or 120 character limit means that my code fits in the width of the window.

I would love to have a switch to enable a line terminator as opposed to implementing yet another continuation character.

Is there any language that has the concept of "line continuation" that has done it in a way that is better?

PowerShell's "line continuation" at first implied to me that the line was simply spliced together with the next one (as if the line ending was ignored). But that's not turned out to be the way it works. Try
'Power`
Shell'

It's pretty hard to create a good shell that doesn't have some way to split command lines because they can get pretty long. I chose backtick because it was _similar_ to \ (and I couldn't choose \ for obvious reasons). Comparing PS to C# is a bit disingenuous they target different usage patterns - C# has a single statement end operator - requiring ; at the end of every command would be a pretty rotten interactive shell experience.
Bruce and I did think a lot about this in the early days, and _tried_ to be faithful to Unix shell traditions and concepts, even if we weren't always faithful to their _implementation_, given all the platform considerations.

@LarryWeiss

Is there any language that has the concept of "line continuation" that has done it in a way that is better?

On the visibility front yes. Bash and Python use \ for line continuation. But neither tolerates whitespace after \. VB, which uses _, does tolerate whitespace after _.

some of those characters still have trouble in a windows environment where you're executing a command. _ is an allowed character for a filename - would I need to escape _ in this context?
get-content _

Agreed. That's where an escaped character would be better.

FWIW, a number of international keyboards don't even have the backtick key. In the Italian keyboard it's ALT+TN96 or nothing. FWIW again, many laptops don't have TN at all, so there is NO way to type a backtick

What about ## ?

`` is promising - the only existing code it would break is commands (argument-mode statements) that:

  • print a _single_ `, _individually_ escaped,
  • as the _last_ argument

E.g., Write-Host You should not end a sentence with a ``. Existing code doing that strikes me as not too likely.

@spaghettidba brings up a good point, but it is outweighed by the long tradition of ignoring other languages and keyboard layouts in computing :) (e.g., \, @)

@iSazonov: It's quite likely that ## is used in existing code for comments, and even if you limited recognition to ##<newline> sequences, the potential for confusion with a comment is great.

[Tangent alert]

@LarryWeiss: Re line continuation in other languages (to add to @rkeithhill's comment):

Good point; to elaborate on the fundamental difference between POSIX-compatible shells such as Bash and PowerShell with respect to line continuation:

  • In Bash, \<newline> is _removed_ from the lines of a command / inside a double-quoted string; this allows splitting command lines at arbitrary points and composing single-line strings spread across several lines inside double quotes.

  • In PowerShell, `<newline> _preserves_ the newline; this requires splitting to occur _between_ arguments and makes use inside a (double-quoted) _string_ pointless, because a <newline> by itself will do; to get line continuation with a string in a POSIX sense, you must use the workaround of an _array_ of substrings with unary -join.

Note that in both environments there is _no_ line continuation in _single_-quoted strings, where all content is treated _literally_ (with the exception of '' in PS) - so, strictly speaking, your example doesn't apply (inside '...', both the ` and the <newline> are retained, and, as with double-quoted strings, just <newline> will do).

Two years later, I think I'm going to close this. The current situation is "somewhat" sub-optimal but I'm thinking the line-terminator sequence is not worth changing at this point.

Was this page helpful?
0 / 5 - 0 ratings