$(yntax)

rated by 0 users
This post has 7 Replies | 5 Followers

Not Ranked
Posts 2
Chadrat Posted: 11-11-2011 2:06 AM

Don,

First, thanks for all that you do for the community. You've been one of my favorite since I started learning PowerShell and I really appreciate all the effort you put into teaching as well as the material you produce.

Now on to my question. I was recently writing a script to process some data from a CSV file using Import-CSV. I was having an issue so I went out and looked for an example that I could use. The example I found used syntax that I haven't seen explained but seems to be used frequently and I was hoping you could explain. I've simplified the example as it really is the syntax that throws me.

What is with the "$()" syntax wrapping the column1 value as both methods seem to work just fine?

foreach($Line in $CSVFile)
{
$Column1 = "$($line.Column1)"
$Column2 = $line.Column2
}

Regards,
Chad

Top 10 Contributor
Posts 640

Yeah, I really need to do a blog article on that syntax. For some reason I'm seeing it more and more.

So, here's the short of it: The $ isn't part of a variable name, ever. It's actually a cue to the shell that what follows is a variable name. So when the shell sees a $, either alone or within double quotes, it knows that whatever comes next needs special treatment.

Parentheses work just like they do in algebra: They tell the shell to "execute this first." 

So when the shell sees...

$Column1 = "$($line.Column1)"

It says, "ok, there's something after this first dollar sign I need to pay attention to. Ah, it's parentheses - I need to execute that expression." So it's kinda, sorta, a "parser hack" rather than "this is syntax everyone can and should use." Not that it's unsupported - it's definitely a supported syntax - but it's not necessarily something I think the PowerShell team was hoping everyone would use.

Why would you use it? Consider this:

$Column1 = "$line.Column1"

That won't work. The period isn't a legal character for a variable name. So the shell sees the $, consumes everything to the first non-legal variable-name character, and ends up replacing "line" with some approximation of its contents. In your case, $line contains multiple objects imported from a CSV, so the above code would produce something like:

{PSObject}.Column1

As the shell does its best to represent the object in $line. The code you're using wants to grab just the Column1 property from $line, though, and that's what the parentheses are letting it do.

I'm personally not a big fan of the syntax. It's concise, but it's hard to read, it's not really documented anywhere within the shell, it's not discoverable, and so on. There are a lot of mental problems with it :). Any of the following would be better:

$Column1 = $line.Column1
$Output = "This is $Column1"

...or...

$Output = "This is {0}" -f ($line.Column1)

In either case, both of those strike me as easier to read, and they're both fully-documented techniques.

Now, you probably never actually see your example in real life. What you're doing with Column2 is perfectly normal, and you could do the same thing with Column1. What's more common is my example, just above, where you want to insert the value of $line.Column1 into a string that also contains literal text. In your example there's no need whatsoever to use the double quotes or the parentheses. In my example, it's the combination of the value being drawn from a property plus the literal text ("this is") that makes the quotes+parentheses combo needed.

Sadly, this is the exact kind of syntax that people dig up on blogs and re-use without really understanding - which leads to a lot of unnecessary use. I'm glad you asked :). 

Top 100 Contributor
Posts 25

Don did a great job explaing what this is about.  I have used this a lot in generating output for files, html and emails and found the simple rule to work well: if it's in double quotes, wrap it in $() to evaluate it.  When you place a variable in a set of double quotes you need to wrap anything you want evaluated in $().  For me, the double quotes queue me in to this because it's something I've seen so often.  Another way to look at is this:

$line.Column1 -eq "$($line.Column1)"

will return $true.  So, I see quotes I automatically something needing evaluation unless you are using the -f approach Don noted.

Here's a set of examples that helped me.  I learn best when I see what works and what doesn't work.

PS > $date = Get-Date

PS > $date

Friday, November 11, 2011 10:36:19 AM

PS > "$date"

11/11/2011 10:36:19

PS > $date.Date

Friday, November 11, 2011 12:00:00 AM

PS > "$date.Date" # This one demonstrates why you need the $() wrapper within the quotes.

11/11/2011 10:36:19.Date

PS > "$($date.Date)"

11/11/2011 00:00:00

Notice when you use "$date.Date" it returns the value of the DateTime object, but, not the property.  It sees a variable and a string because it is not evaluation the whole expression, only what it recognizes as the variable.  To return the Date property of the $date object, you have to wrap it.

Not Ranked
Posts 2

Thanks to both of you, that helps clear things up quite nicely.

 I did a lot of looking around to try and find an answer before asking the question however looking for precise answers in regards to syntax when you don't know the proper term for the syntax has been difficult at best.

Thanks again!

 

Regards,

Chad

Not Ranked
Posts 1

Hey Don,

what about using $($yntax.value) in here strings, is there also a alternative?

 

Lines = @"    

     $($line.Column1)

     $($line.Column2)

"@

 

Regards, Bernd

Top 10 Contributor
Posts 640

There's no place where that syntax is required.

$variable = 4

$Lines = @"
  This many $lines
"@

You can always use just a plain variable. 

Not Ranked
Posts 2

The $( ) syntax comes from the UNIX shells (see http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html section 2.6.3 Command Substitution).  It was added to PowerShell based on feedback from users who wanted to be able to reference property and array elements directly in string expansions. Rather than add complex parsing rules to try and extract expressions, we added command substitution which is a more general solution to the problem but with explicit delimiters. An alternative approach for expanding properties in strings without introducing intermediate variables is to use the  -f format operator. Something like

"Today is $((get-date).DayOfWeek)."

could be written as

"Today is {0}" -f (get-date).DayOfWeek

which may feel more familiar to people.

If you are only using a calculated value once in a string, it's generally better to use one of these techniques rather than cluttering up the variable drive with a temporary name that will live on long past it's domain of use.

-bruce [MSFT]

 

Top 10 Contributor
Posts 640

Interesting. I always figured it was an artifact, not something you guys did on purpose. Figures - everything Unix looks like a hack to me :).

Page 1 of 1 (8 items) | RSS
Copyright 2012 PowerShell.com. All rights reserved.