String concatenation with newline not working as expected - powershell

I want to add a line break without adding a whole 'nother line to do so, here be my code:
"-- MANIFEST COUNT -- " >> "C:\psTest\test1.txt"
$manCount = (Get-ChildItem -filter "*manifest.csv").Count
$manCount + " `n" >> "C:\psTest\test1.txt"
I thought that + " `n" would tack a line break onto the count, but it's not doing anything. I tried also + "`r`n" (I found this suggestion elsewhere on SO) but to no avail.

Let me complement your own solution with an explanation:
Because $manCount, the LHS, is of type [int],
$manCount + " `n"
is effectively the same as:
$manCount + [int] " `n".Trim()
or:
$manCount + [int] ""
which is effectively the same as:
$manCount + 0
and therefore a no-op.
In PowerShell, the type of the LHS of an expression typically[1]
determines what type the RHS will be coerced to, if necessary.
Therefore, by casting $manCount to [string], + then performs string concatenation, as you intended.
As Matt points out in a comment on your answer, you can also use string interpolation:
"$manCount `n"
[1]There are exceptions; e.g., '3' - '1' yields [int] 2; i.e., PowerShell treats both operands as numbers, because operator - has no meaning in a string context.

The integer needed to be cast as a string in order for the concatenation to take:
"-- MANIFEST COUNT -- " >> "C:\psTest\test1.txt"
$manCount = (Get-ChildItem -filter "*manifest.csv").Count
[string]$manCount + "`r`n" >> "C:\psTest\test1.txt"

Related

Using #Splat to send email attachment result and then delete after successful execution [duplicate]

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?
Write-Host "$($assoc.Id) - $($assoc.Name) - $($assoc.Owner)"
See the Windows PowerShell Language Specification Version 3.0, p34, sub-expressions expansion.
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).
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"))
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
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.
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)"
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.)
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)
(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
Here is another way as an alternative:
Write-Host (" {0} - {1} - {2}" -f $assoc.Id, $assoc.Name, $assoc.Owner)
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))
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
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")
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
From What To Do / Not to Do in PowerShell: Part 1:
$id = $assoc.Id
$name = $assoc.Name
$owner = $assoc.owner
"$id - $name - $owner"
Write-Host can concatenate like this too:
Write-Host $assoc.Id" - "$assoc.Name" - "$assoc.Owner
This is the simplest way, IMHO.
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.
$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
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
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
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"
}
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

foreach pipe in multipleline string

Here is my code:
$collection1 = "1","2","3","4"
"
collection1:
$($collection1 | % {$_})
"
The output is:
collection1:
1 2 3 4
However, I'm expecting:
collection1:
1
2
3
4
So I changed my code to:
"
$($collection1 | % {$_ + "`n"})
"
Now, it shows:
collection1:
1
2
3
4
Why there is always an extra space in the front of each line?
Any way to remove them?
I tried to use Trim(), [String]::Format(), and few other ways, none is working as expected.
I think I found the answer... For anyone's interest...
$collection1 = "1","2","3","4"
$body = [System.Text.StringBuilder]::new()
foreach($item in $collection1)
{
[void]$body.Append($item+"`n")
}
"
collection1:
$body
"
$collection1 | % {$_} takes the array $collection1 and outputs each of its elements, which gives you the exact same thing you strated with: an array of the elements of $collection1. Using a subexpression to put that array into a string auto-converts the array to a string, so that it can be inserted into the outer string. In PowerShell this array-to-string conversion is done by joining the array elements with the $OFS character (Output Field Separator, by default a single space), which for your example produces the string "1 2 3 4".
The simplest way of getting the result you expect is to convert the array to a formatted string yourself:
"
collection1:
$($collection1 | Out-String)
"
The Out-String cmdlet converts array input to a single string by joining all input strings with newlines. Alternatively join the array yourself:
"
collection1:
$($collection1 -join "`n")
"
You could also (temporarily) modify $OFS and use the variable directly:
$OFS_bak = $OFS
$OFS = "`n"
"
collection1:
$collection1
"
$OFS = $OFS_bak
although I wouldn't normally recommend that.
As a side note: when working with a format/template string it's usually a good idea to use here-strings (so that don't need to escpae quotes inside the strings) and the format operator (-f):
#"
collection1:
{0}
"# -f ($collection1 | Out-String)

Get string length that is created dynamically

Lets say I want to do something like this:
$string = 'arbitrary string' # << is random
(Do-Something -Name ($string + (Get-Random)).Substring(0, [math]::Min(20, ??)
How do I refer to current length of the result of the expression inside ($string + (Get-Random))?
Fails when using Substring(0, 20) when string shorter than 20 chars
Exception calling "Substring" with "2" argument(s): "Index and length
must refer to a location within the string. Parameter name: length"
You'll have to assign the new string to a variable:
Do-Something -Name ($s = $string + (Get-Random)).Substring(0, [math]::Min(20,$s.Length))
A less terse version of the above:
$Name = $string + (Get-Random)
if($Name.Length -gt 20){
$Name = $Name.Substring(0, 20)
}
Do-Something -Name $Name
As mentioned in the comments, you could also select the first 20 chars by index from the string and combine again with the -join operator (v3.0 and newer):
$Name = "$string$(Get-Random)"[0..19] -join ''
Feeling frisky? Use regex (as suggested by wOxxOm):
$Name = "$string$(Get-Random)" -replace '^(.{20}).+','$1'
If the concatenated string is less than 20 characters nothing will be replaced, otherwise the entire string will match and get replaced by the first 20 characters
Another approach would be to generate a random number of X digits where X is 20 - $string.Length (only works if $string is guaranteed to be between 2 and 19 characters long):
$Name = "$string$(Get-Random -Min ('1' * (20 - $string.Length)) -Max ('9' * (20 - $string.Length)))"

How do I concatenate strings and variables in PowerShell?

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?
Write-Host "$($assoc.Id) - $($assoc.Name) - $($assoc.Owner)"
See the Windows PowerShell Language Specification Version 3.0, p34, sub-expressions expansion.
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).
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"))
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
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.
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)"
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.)
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)
(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
Here is another way as an alternative:
Write-Host (" {0} - {1} - {2}" -f $assoc.Id, $assoc.Name, $assoc.Owner)
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))
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
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")
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
From What To Do / Not to Do in PowerShell: Part 1:
$id = $assoc.Id
$name = $assoc.Name
$owner = $assoc.owner
"$id - $name - $owner"
Write-Host can concatenate like this too:
Write-Host $assoc.Id" - "$assoc.Name" - "$assoc.Owner
This is the simplest way, IMHO.
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.
$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
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
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
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"
}
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

Powershell Using Backtick for new lines - weirdness

What exaxctly is happening in example 1? How is this parsed?
# doesnt split on ,
[String]::Join(",",("aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa," + `
"aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa".Split(',') `
| foreach { ('"' + $_ + '"') }))
# adding ( ) does work
[String]::Join(",",(("aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa," + `
"aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa").Split(',') `
| foreach { ('"' + $_ + '"') }))
In you first example you may remove the backtick, because Powershell knows that the string will continue (there is a + sign).
What posh does
takes string "aaaa,aaaa..." (1) from first
evaluates the expression with split - it returns array of strings (from "aaaa,...aaaa".Split(','))
converts the array of strings to string, which returns again string "aaaa,...aaaa"
adds results from 1. and 3.
Note: when posh converts array to string, it uses $ofs variable. You will see it better in action when you will try this code:
$ofs = "|"
[String]::Join(",", ("aaaaa,aaaaa" + "bbbb,bbbb,bbbb".Split(',') | foreach { ('"' + $_ + '"') }))
Your first example only has the Split method applied to the second string of a's. The parentheses are necessary for order of operations. The Split method is performed before the concatenation in your first example.