Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the magic behind "$(($i++))"?

Tags:

powershell

Here's a little piece of code that outputs 1 2 3 ... with 1 second interval.

while ($true) {
  sleep -s 1
  "$(($i++))"
}

How is it possible?

like image 545
manidos Avatar asked Dec 23 '22 22:12

manidos


1 Answers

There are good pointers in the comments, but let me dig a little deeper:

Explanation of $i++:

  • $i++ uses ++, the increment operator, to increment the value of variable $i by 1, as may be familiar from languages such as C# and C/C++. As expected, a complemementary decrement operator, --, exists too).

    • Since the ++ is positioned after the variable (postfix form), incrementing happens after the variable's value has been used in a statement; placing it before the variable - ++$i (prefix form) would perform incrementing first; if an increment / decrement operation in used in isolation, that distinction is irrelevant.

    • $i is assumed to contain an instance of a numeric type, otherwise an error occurs; if variable $i has not been initialized, its value is effectively $null, which PowerShell coerces to an [int]-typed 0. Thus, $i++ evaluates to 0 in the context of its statement and is incremented to 1 afterwards.

  • An increment / decrement expression such as $i++ is treated like an assignment - you can think of it as $i = $i + 1 - and assignments in PowerShell produce no output (they do not return anything; they only update the variable's value).

Explanation of (...) around $i++:

  • By enclosing an assignment in parentheses ((...)) you turn it into an expression, which means that the value of the assignment is passed through, so that it can participate in a larger expression; e.g.:
    • $i = 0 ... no output - just assigns value 0 to variable $i.
    • ($i = 1) ... outputs 1: due to (...), the assigned value is also output.
    • (++$i) ... pre-increment: increments the value of $i to 2 and outputs that value.
    • ($i++) ... post-decrement: outputs 2, the current value, then increments the value to 3.

Explanation of $(...) around ($i++):

  • $(...), the subexpression operator, is needed for embedding the output from one or even multiple statements in contexts where statements aren't directly supported. Notably, you can use it to embed command output in an expandable string ("..."), i.e., to perform string interpolation.

    • Note that $(...) is only needed for embedding expressions (e.g., something enclosed in (...), property access ($foo.bar), indexing, ($foo[0]) and method calls ($foo.Baz())) and commands (e.g., Get-Date), not for mere variable references such as in "Honey, I'm $HOME". See this answer for more information about expandable strings in PowerShell.
  • While there is no strict need for an expandable string in your simple example - just ($i++) would produce output that looks the same[1] - the $(...) is useful for making the value of ($i++) part of a larger string; e.g., "Iteration #$(($i++))" to print "Iteration #0", "Iteration #1", ...


[1] ($i++) is a number, whereas "$(($i++)" is a string, where the to-string conversion of the number happened as part of the string interpolation. While that typically results in the same console output, it can actually differ for non-integral numbers such as 1.2, because direct output applies culture-sensitive stringification, whereas string interpolation is culture-invariant. Thus, with a culture in effect that uses , as the decimal mark -e.g, fr-FR, 1.2 prints - culture-appropriately - as 1,2 to the console, whereas "$(1.2)" always prints as 1.2

like image 51
mklement0 Avatar answered Jan 17 '23 18:01

mklement0