912

Suppose I have the following snippet:

$assoc = New-Object PSObject -Property @{
    Id = 42
    Name = "Slim Shady"
    Owner = "Eminem"
}

Write-Host $assoc.Id + "  -  "  + $assoc.Name + "  -  " + $assoc.Owner

I'd expect this snippet to show:

42 - Slim Shady - Eminem

But instead it shows:

42 + - + Slim Shady + - + Eminem

Which makes me think the + operator isn't appropriate for concatenating strings and variables.

How should you approach this with PowerShell?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ninja Cowgirl
  • 10,421
  • 8
  • 33
  • 41
  • 49
    Your code works **if** all elements are strings and you enclose the expression in parentheses: `Write-host ($assoc.Id.ToString() + " - " + $assoc.Name + " - " + $assoc.Owner)` here $assoc.Id is an `Int32` so we have to use its string representation. Otherwise PS tries to perform an arithmetic addition instead of concatenation. – bouvierr Nov 30 '13 at 22:59
  • 8
    Given the number of views I thought it was appropriate to repair the text of this question, even though my edits changed the contents quite a bit. I've tried to keep the terminology/wording and spirit of the question intact, while improving it enough so that it may be reopened. – Jeroen Oct 22 '14 at 11:19

22 Answers22

980
Write-Host "$($assoc.Id) - $($assoc.Name) - $($assoc.Owner)"

See the Windows PowerShell Language Specification Version 3.0, p34, sub-expressions expansion.

David Brabant
  • 41,623
  • 16
  • 83
  • 111
  • 1
    This technique works with `Write-Output` *et al* too, so it is probably the most useful one to have in your toolbox. – Fenton Dec 02 '15 at 15:15
  • 13
    Why the $($name) vs just $name? Is this just defensive in the same way as in bash using ${name}? – Danny Staple Feb 26 '16 at 17:04
  • 130
    This isn't technically concatenation. – TravisEz13 Jun 14 '16 at 17:59
  • 14
    @DannyStaple the question has [been edited](http://stackoverflow.com/posts/15113413/revisions) so much that the answer doesn't make sense anymore. In the slim shady example, yes `"... $name ..."` will work all the same, but in the original question for which this answer addresses, the question is how to access object properties. so if `$name.id -eq 42` then `"... $name.id ..."` would not work like you want because it would render like `... @{id=42}.id ...` instead of the desired `... 42 ...` For that, use the answer's method `"... $($name.id) ..."`. I'm bookmarking question to edit later. – Jeff Puckett Jun 17 '16 at 03:20
  • 4
    This doesn't work for distributing strings across multiple lines. Instead use `( $string1, $string2, $string3 ) -join ""` or `($string1 + $string2 + $string3)`. With these methods, you can have whitespace (spaces, tabs, and newlines) between the strings, but be sure that (1) each string has a comma/space after it on the same line, (2) you use the back-tick character ` at the end of any empty lines between your code, and (3) if you use `-join`, the end bracket and the `-join` operator must be on the same line – Dave F Dec 27 '18 at 01:18
  • 1
    you sure it's on page 34? Literal page or page no in the down-right corner? I checked both places and didn't find it... – Jing He Jul 18 '19 at 14:45
374

There is a difference between single and double quotes. (I am using PowerShell 4).

You can do this (as Benjamin said):

$name = 'Slim Shady'
Write-Host 'My name is'$name
-> My name is Slim Shady

Or you can do this:

$name = 'Slim Shady'
Write-Host "My name is $name"
-> My name is Slim Shady

The single quotes are for literal, output the string exactly like this, please. The double quotes are for when you want some pre-processing done (such as variables, special characters, etc.)

So:

$name = "Marshall Bruce Mathers III"
Write-Host "$name"
-> Marshall Bruce Mathers III

Whereas:

$name = "Marshall Bruce Mathers III"
Write-Host '$name'
-> $name

(I find How-to: Escape characters, Delimiters and Quotes good for reference).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
TinyRacoon
  • 4,655
  • 2
  • 23
  • 22
  • 3
    Works with 1.0 too. – Smit Johnth Jun 18 '16 at 11:06
  • This quote rule and demo for concatenation is applicable to 'nix shell scripting (e.g. Bash), in addition to PowerShell. The portability factor alone earns my vote. – BuvinJ Nov 20 '20 at 14:28
  • I am ashamed to say my script I was debugging with "" but writing commands with strings and variables using '' and it was hours of work and this answer to find what I'd done wrong. Unbelievable! – Kyle Burkett Sep 23 '21 at 16:40
  • 1
    `powershell` is neat and will put a space between the string and the content of the variable in `Write-Host 'My name is'$name` – Timo Oct 28 '21 at 08:36
261

You can also use -join

E.g.

$var = -join("Hello", " ", "world");

Would assign "Hello world" to $var.

So to output, in one line:

Write-Host (-join("Hello", " ", "world"))
ndemou
  • 4,691
  • 2
  • 30
  • 33
manyways
  • 4,386
  • 2
  • 23
  • 19
  • 10
    at least this one could be used in foreach – dEmigOd Jun 09 '20 at 07:51
  • 3
    Please add that if you are using commands as inputs, wrap the command inside $().....eg... -join($(cmd1), ",", $(cmd2)) ...took me too long to figure out as I am new to powershell – S. Melted Sep 11 '20 at 15:49
  • 1
    @S.Melted why wrap inside `$()`? – AlbatrossCafe Nov 17 '20 at 23:19
  • Upon further review, only `()` is necessary for my need. For example: `-join("Timestamp: ", Get-Date)` gives an error, while `-join("Timestamp: ", (Get-Date))` runs fine. However, [this](https://stackoverflow.com/a/49031158/11012871) explains when `$()` would be necessary. – S. Melted Dec 23 '20 at 19:20
  • `Write-Host -join("Hello", " ", "world")` will not join as the whole `join` argument needs brackets including the name of the arg = join. – Timo Oct 28 '21 at 08:39
  • Thanks, this is what I needed for something like `$name = -join("✅ ", $_.Substring(2))` – Michael Murphy Jul 11 '22 at 10:27
154

One way is:

Write-Host "$($assoc.Id)  -  $($assoc.Name)  -  $($assoc.Owner)"

Another one is:

Write-Host  ("{0}  -  {1}  -  {2}" -f $assoc.Id,$assoc.Name,$assoc.Owner )

Or just (but I don't like it ;) ):

Write-Host $assoc.Id  "  -  "   $assoc.Name  "  -  "  $assoc.Owner
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
CB.
  • 58,865
  • 9
  • 159
  • 159
  • 10
    I don't think that last one works exactly as you'd expect. See [pastebin here](http://pastebin.com/DD66QhCt). Powershell will add one (even though you have two here) space to the output for the whitespace between `$assoc.Id` and `"` (et al). That is, option 1 gives you `Id__-__Name__-__Owner` (two spaces on each side of each `-`), but option 3 gives `Id___-___Name___-___Owner` (**three** spaces). – ruffin Dec 17 '14 at 15:10
  • 5
    [Perhaps a more useful pastebin](http://pastebin.com/Q0EcAkxR). `$assoc.Id"###"$assoc.Name` **will** add spaces to either side of the ###, where `"$($assoc.Id)###$($assoc.Name)"` **won't**. – ruffin Dec 17 '14 at 15:20
98

Try wrapping whatever you want to print out in parentheses:

Write-Host ($assoc.Id + "  -  "  + $assoc.Name + "  -  " + $assoc.Owner)

Your code is being interpreted as many parameters being passed to Write-Host. Wrapping it up inside parentheses will concatenate the values and then pass the resulting value as a single parameter.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
goric
  • 11,491
  • 7
  • 53
  • 69
  • 4
    +1, I prefer this format when using other methods like `Write-Debug` or `Write-Verbose` for including double quotes and variable values, such as `Write-Debug ('Running "cmdlet" with parameter $MyVar = ' + $MyVar + " at time " + (Get-Date -DisplayHint Time))` – IT Bear Jan 09 '17 at 18:25
  • 2
    Yes, this is string concatenation. It does not add newlines or extra spaces. It lets you use either single or double-quoted strings. – LexH Oct 24 '18 at 21:28
  • 3
    This trick is true concatenation and allows you to do multi-line strings, like you would in .net. Very good for doing multiline SQL statements! Why doesn't powershell do this by default!? – Brain2000 Dec 12 '18 at 05:26
  • Thanks a bunch for this! I was having a hard time building up the URI parameter for Invoke-WebRequest and your solution was the only one that actually worked: `Invoke-WebRequest -uri ('http://mysvr/guestauth/app/rest/...'+$param1+'_'+$param2+'_something,count:10')` – Andreas Jun 22 '20 at 11:42
  • I had a use case where I was already in a double-quote("") and couldn't use the "accepted" answer, because it would break my string in an upper level of the command chain. Your answer was very helpful and solved my problem. Thanks! – ZaxLofful Oct 23 '20 at 06:49
43

While expression:

"string1" + "string2" + "string3"

will concatenate the string, you need to put a $ in front of the parenthesis to make it evaluate as a single argument when passed to a PowerShell command. Example:

Write-Host $( "string1" + "string2" + "string3" )

As a bonus, if you want it to span multiple lines, then you need to use the awkward backtick syntax at the end of the line (without any spaces or characters to the right of the backtick).

Example:

Write-Host $(`
    "The rain in "        +`
    "Spain falls mainly " +`
    "in the plains"       )`
    -ForegroundColor Yellow

(Actually, I think PowerShell is currently implemented a little bit wrong by requiring unnecessary backticks between parentheses. If Microsoft would just follow Python or Tcl parenthesis rules of allowing you to put as many newlines as you want between the starting and ending parenthesis then they would solve most of the problems that people don't like about PowerShell related to line continuation, and concatenation of strings.

I've found that you can leave the backticks off sometimes on line continuations between parenthesis, but it's really flaky and unpredictable if it will work... It's better to just add the backticks.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bimo
  • 5,987
  • 2
  • 39
  • 61
42

Another option is:

$string = $assoc.ID
$string += " - "
$string += $assoc.Name
$string += " - "
$string += $assoc.Owner
Write-Host $string

The "best" method is probably the one C.B. suggested:

Write-Host "$($assoc.Id)  -  $($assoc.Name)  -  $($assoc.Owner)"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Frode F.
  • 52,376
  • 9
  • 98
  • 114
  • 3
    You need to add the `StringBuilder` option if you're going concat-crazy. ;^) – ruffin Dec 17 '14 at 14:56
  • 2
    surely there is a way to pipe the desired properties into an external file, then deserialize them into a string of components joined with hyphens(?) – Code Jockey Feb 25 '15 at 16:43
  • It's hard to imagine an aproach worse than this. Strings are immutable, so each concatenation operation in fact produces a new string, copying characters of the original one. – wombatonfire Mar 10 '18 at 01:08
  • 1
    In response to Code Jockey Using += may produce a new string each time, but for a relatively small string adding additional small strings like this, who cares. The clarity of what is happening is most important and with PowerShell just getting the thing working is more important than anything else. – Action Dan Jan 24 '21 at 20:17
37

You need to place the expression in parentheses to stop them being treated as different parameters to the cmdlet:

Write-Host ($assoc.Id + "  -  "  + $assoc.Name + "  -  " + $assoc.Owner)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Richard
  • 106,783
  • 21
  • 203
  • 265
17

(Current PowerShell version 5.1.17134.407)

This also works as of now:

$myVar = "Hello"

echo "${myVar} World"

Note: this only works with double quotes

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Benjamin
  • 1,067
  • 20
  • 30
14

Here is another way as an alternative:

Write-Host (" {0}  -  {1}  -  {2}" -f $assoc.Id, $assoc.Name, $assoc.Owner)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dave Sexton
  • 10,768
  • 3
  • 42
  • 56
10

I just want to bring another way to do this using .NET String.Format:

$name = "Slim Shady"
Write-Host ([string]::Format("My name is {0}", $name))
Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
  • 3
    Is there any advantage of using .NET `String.Format` instead of old plain `"{0}" -f $var`? I don't see the point in using .NET code where plain PowerShell can do the job... – Dinei Jan 31 '17 at 14:53
  • 4
    @DineiRockenbach Probably not. Just wanted to bring up another example. – Martin Brandl Jan 31 '17 at 15:05
9

Concatenate strings just like in the DOS days. This is a big deal for logging so here you go:

$strDate = Get-Date
$strday = "$($strDate.Year)$($strDate.Month)$($strDate.Day)"

Write-Output "$($strDate.Year)$($strDate.Month)$($strDate.Day)"
Write-Output $strday
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kelly Davis
  • 354
  • 2
  • 6
  • 1
    For dates, I think the `toString` option is easier: `(Get-Date).toString('yyyyMMdd')` – Tony Jun 10 '20 at 21:22
8

From What To Do / Not to Do in PowerShell: Part 1:

$id = $assoc.Id
$name = $assoc.Name
$owner = $assoc.owner
"$id - $name - $owner"
Trisped
  • 5,705
  • 2
  • 45
  • 58
E.V.I.L.
  • 2,120
  • 13
  • 14
8

These answers all seem very complicated. If you are using this in a PowerShell script you can simply do this:

$name = 'Slim Shady'
Write-Host 'My name is'$name

It will output

My name is Slim Shady

Note how a space is put between the words for you

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
elev8ed
  • 183
  • 1
  • 7
8

I seem to struggle with this (and many other unintuitive things) every time I use PowerShell after time away from it, so I now opt for:

[string]::Concat("There are ", $count, " items in the list")
Luke Puplett
  • 42,091
  • 47
  • 181
  • 266
5

Write-Host can concatenate like this too:

Write-Host $assoc.Id" - "$assoc.Name" - "$assoc.Owner

This is the simplest way, IMHO.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mordechai
  • 718
  • 1
  • 8
  • 23
5

Personally I prefer this style:

[string]::Join(' - ', 42, 'Slim Shady', 'Eminem')

or based on any object with even different attribute types:

$assoc = [psCustomObject][ordered]@{
    Id    = 42
    Name  = 'Slim Shady'
    Owner = 'Eminem'
}
[string]::Join(' - ',$assoc.psObject.Properties.value)

which gives you this result:

42 - Slim Shady - Eminem
Carsten
  • 1,612
  • 14
  • 21
4
$assoc = @{
    Id = 34
    FirstName = "John"
    LastName = "Doe"
    Owner = "Wife"
}

$assocId = $assoc.Id
$assocFN = $assoc.FirstName
$assocLN = $assoc.LastName
$assocName = $assocFN, $assocLN -Join " "
$assocOwner = $assoc.Owner

$assocJoin = $assocId, $assocName, $assocOwner -join " - "
$assocJoin
#Output = 34 - John Doe - Wife
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
blayderunner123
  • 306
  • 2
  • 11
4

If you're concatenating strings to build file paths, use the Join-Path command:

Join-Path C:\temp "MyNewFolder"

It'll automatically add the appropriate trailing / leading slashes for you, which makes things a lot easier.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
msq
  • 146
  • 7
2

As noted elsewhere, you can use join.

If you are using commands as inputs (as I was), use the following syntax:

-join($(Command1), "," , $(Command2))

This would result in the two outputs separated by a comma.

See https://stackoverflow.com/a/34720515/11012871 for related comment

S. Melted
  • 253
  • 1
  • 10
1

Just for the fun. You can also access the values of the PSObject directly like below:

$assoc.psobject.Properties.value -join " - "

But if you do not specify that the object should be ordered, PowerShell will display the values in a random order. So you should add the flag [ordered]:

$assoc = [pscustomobject] [ordered] @{
    Id = 42
    Name = "Slim Shady"
    Owner = "Eminem"
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0

You can also get access to C#/.NET methods, and the following also works:

$string1 = "Slim Shady, "
$string2 = "The real slim shady"

$concatString = [System.String]::Concat($string1, $string2)

Output:

Slim Shady, The real slim shady
Ian Robertson
  • 2,652
  • 3
  • 28
  • 36