4

How do I know when I can use a New Lines/Carriage returns in my Powershell scripts? All of the search results when searching for this answer all point to the Output. I don't care about the output in this case. I am more interested in my ability to format my Powershell scripts for readability.

For Example. Two versions of a Powershell command line below. One works and one doesn't. What the command does is unimportant in this case. The point is I am needing to know when I'm allowed to create a new line and when I am not.

This command line Works as it's just one long single line:

& 'C:\Program Files\ArangoDB3 3.3.3\usr\bin\arangoimp.exe' --file 'C:\Program Files\ArangoDB3 3.3.3\usr\bin\tstImportJSON.json' --type json --collection users --progress true --overwrite true --server.username root --server.password password

This command line does NOT work due to the fact that there is a New Line in the middle of the script.

& 'C:\Program Files\ArangoDB3 3.3.3\usr\bin\arangoimp.exe' --file 
'C:\Program Files\ArangoDB3 3.3.3\usr\bin\tstImportJSON.json'
--type json --collection users --progress true --overwrite true
--server.username root --server.password password

In my case I'm just running different versions of the same command line after adding line breaks to see if they work or not. I know that I can begin a New Line when using an IF statement. I can also use New Lines when piping an object |. My assumption is that somewhere there is a list of Powershell scripting rules. I thought I had seen them somewhere once upon a time when I originally began getting into Powershell but no clue where it is now.

6
  • Actually it's not a script. It's only a command line. A simple command line is not allowed to have a line break. If you use a Powershell cmdlet like Start-Process you can use line breaks after commata, semikola, opening parenthesis or curly braces ... try it like this: Start-Process -FilePath 'C:\Program Files\ArangoDB3 3.3.3\usr\bin\arangoimp.exe' -ArgumentList "--file 'C:\Program Files\ArangoDB3 3.3.3\usr\bin\tstImportJSON.json'", "--type json", "--collection users", "--progress true", "--overwrite true", "--server.username root", "--server.password password" Commented Mar 12, 2018 at 21:07
  • And of course you can use a line break after the pipe "|". Commented Mar 12, 2018 at 21:13
  • @Olaf: Using ` at the very end of the line does allow you to spread a command across multiple lines. Start-Process serves a different purpose and is not a replacement for direct invocation of a console program. Splatting is the answer, if you want to avoid the tricky end-of-line ` syntax. Commented Mar 12, 2018 at 22:34
  • Good point. What I posted was just a single command line that I am saving into a script which I am running via Task Scheduler. Commented Mar 12, 2018 at 22:54
  • 1
    Addendum: while array-based splatting is an option, @PetSerAl correctly points out that you can simply pass an array directly (when calling external programs). Commented Mar 12, 2018 at 22:56

3 Answers 3

9

You can use ` as the line-continuation character[1] at the very end of a line (not even whitespace is allowed after it) in order to spread it across multiple lines.

Here's a simplified example:

& cmd.exe /c echo `
 hi `
 there

This is the equivalent of & cmd.exe /c echo hi there and yields hi there.

Note:

  • Since the ` character is both visually subtle and the syntax is easy to break by accidentally placing characters after it, consider using an array as an alternative - see below.

  • Individual commands must be on a single line and therefore need ` if you want to spread them across multiple lines, as shown above.

  • However, in a pipeline you may end the line with | and continue on the next line, without needing the `; e.g.:

      Get-Date | # Because the line ends with |, parsing continues on the next line.
        Select-Object Day
    
    • In PowerShell [Core] v7+, you may alternatively place the | at the start of the (very) next line:

          Get-Date  # PS v7+ only
          | Select-Object Day
      
  • Additionally, if individual arguments create a new parsing context in expression mode - such as an inherently multi-line capable (...) expression or a script block ({...}) passed to a cmdlet - you're also free to spread the expression across multiple lines; e.g.:

      1, 2, 3 | ForEach-Object { # { starts a multiline-aware context
        $_ + 1
      }
    
  • A hybrid case is an array-literal argument, which allows you to break a command after an interior element's , separator:

      Get-Date | Select-Object -Property Day,
                                         Year
    
  • Statements that start in expression mode always allow spreading across multiple lines (though embedded command-mode statements are subject to the usual limitations):

      $foo =         # an assignment is parsed in expression mode
              'bar'  
    

Alternatively, consider the use of an array[2] to pass the arguments, which allows you to use multiline expression-mode syntax to define the arguments as individual array elements beforehand:

# Construct the array of arguments (using multiline expression syntax)...
$arguments = '/c',
             'echo',
             'hi there'

# ... and pass it to cmd.exe
& cmd.exe $arguments 

Note: Array element hi there is passed as "hi there" by PowerShell: it employs automatic double-quoting to ensure that the argument is recognized as a single argument by the target program.

As an aside: for calling PowerShell commands (as opposed to external programs, as in the case at hand), consider constructing the arguments in a hashtable for use with splatting, where each entry key specifies a target parameter name and the corresponding value the parameter value (argument); e.g.:

# Define the hashtable representing the named arguments.
$argsHash = @{
  Filter = 'T*'
  File = $true
}
# Note the use of "@" instead of "$"
# Equivalent of:
#    Get-ChildItem -Filter T* -File
Get-ChildItem @argsHash

[1] ` is PowerShell's general-purpose escape character. Placed at the very end of a line, its function is subtly different: instead of escaping the newline that follows (which would mean retaining it as a literal), it effectively tells PowerShell to remove it and treat the next line as the continuation of the current one.

[2] An earlier form of this answer recommended array-based splatting, before PetSerAl pointed out that for invoking external programs it's sufficient to use an array as-is.
While splatting can be used too, its semantics are subtly different if one of the array element is --%, the stop-parsing symbol (in short: only when splatting does --% have its special meaning).
Splatting is a useful technique when calling PowerShell commands, however, primarily in its hash-table form (see previous link).

Sign up to request clarification or add additional context in comments.

8 Comments

Actually the backtick is not a line continuation charachter. It's an esacape charachter. And the "charachter" you escape is a line break it works as an line continuation charachter. But usually it's really bad style to use it this way and when you accidently have a space before your line break there's almost no way to see the failure.
You do not need splatting when passing array of strings to executables. & cmd.exe $arguments will work as well.
@mklement0 ;-) ... as style is a matter of taste I'll keep my opinion about the use of backticks. But anyway it's technically possible to use it this way - so your answer is absolutely correct. I just wanted to recommend to not to use it if it's not really necessary.
As passing array do not require you to build variable beforehand you can as well write cmd /c,<newline> echo,<newline> hi there. But, of course, there is subtle case where splatting vs. passing array produce different results: $a = '/c', 'echo', 'something', '--%', 'something else' and cmd $a vs. cmd @a.
@mklement0 Windows PowerShell starting from v5 retain , if there are no space between comma and array element (array element can include comma): cmd /c echo 1,2,3 and cmd /c echo ( 1 ),( 2 ),( 3 ) vs. cmd /c echo 1,2 ,3. IMO it is implemented in very ugly way. I am not yet inspect any changes made in Core about that, but wrapping array literal with (...) or @(...) should likely disable that behavior: cmd /c echo (1,2,3) or cmd /c echo @(1,2,3).
|
2

The standard way of formatting PowerShell arguments for readability is through splatting, which consists of assigning a hash table to a named splatting variable carrying the options, and using the At symbol to dereference the arguments when needed.

Your specific example of running an application with arguments would be written like below, in idiomatic PowerShell.

$arangoImpOptions = @{
    FilePath = "$env:ProgramFiles\ArangoDB3 3.3.3\usr\bin\arangoimp.exe"
    Arguments = @{
        file = "$env:ProgramFiles\ArangoDB3 3.3.3\usr\bin\tstImportJSON.json"
        type = 'json'
        collection = 'users'
        progress = 'true'
        overwrite = 'true'
        'server.username' = 'root'
        'server.password' = 'password'
    }.GetEnumerator().ForEach({ '--{0} "{1}"' -f @($_.Key, $_.Value) }) -join ' '
}
Start-Process @arangoImpOptions

Comments

1

Break down the script a little bit more for additional readability.

$1 = "--file 'C:\Program Files\ArangoDB3 3.3.3\usr\bin\tstImportJSON.json'"
$2 = "--type json"
$3 = "--collection users"
$4 = "--progress true"
$5 = "--overwrite true"
$6 = "--server.username root"
$7 = "--server.password password"
$Arguments = "$1 $2 $3 $4 $5 $6 $7"
& 'C:\Program Files\ArangoDB3 3.3.3\usr\bin\arangoimp.exe' $Arguments

Your output will look like this.

C:\Program Files\ArangoDB3 3.3.3\usr\bin\arangoimp.exe --file 'C:\Program Files\ArangoDB3 3.3.3\usr\bin\tstImportJSON.json' --type json --collection users --progress true --overwrite true --server.username root --server.password password

typically I would normally move an uglier looking beast like this into its own function.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.