{"id":2003,"date":"2014-02-15T00:01:00","date_gmt":"2014-02-15T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2014\/02\/15\/string-formatting-in-windows-powershell\/"},"modified":"2014-02-15T00:01:00","modified_gmt":"2014-02-15T00:01:00","slug":"string-formatting-in-windows-powershell","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/string-formatting-in-windows-powershell\/","title":{"rendered":"String Formatting in Windows PowerShell"},"content":{"rendered":"<p><b>Summary<\/b>: Learn about using formatted strings in Windows PowerShell.\nMicrosoft Scripting Guy, Ed Wilson, here. Today we have another guest blog by June Blender. To read more of June&rsquo;s previous posts, see these <a href=\"http:\/\/social.technet.microsoft.com\/Search\/en-US?query=Scripting%20Guy!%2C%20June%20Blender&amp;rn=Hey,%20Scripting%20Guy!%20Blog&amp;rq=site:blogs.technet.com\/b\/heyscriptingguy\/&amp;beta=0&amp;ac=5\" target=\"_blank\">Hey, Scripting Guy Blog posts<\/a>. Now, here&rsquo;s June&hellip;\nI recently had the opportunity to help the Windows Azure and ASP.NET product teams with some Windows PowerShell automation. Although I was supposed to be the helper, I probably learned more from the interaction than anyone else. I learned about Windows Azure and the Azure module for Windows PowerShell. And I learned a whole bunch about Windows PowerShell. I&#8217;m convinced that helping always benefits the helper. You always learn by interacting. This case was no exception.\nOne of the most fun things I learned was about string formatting.\nWhen I saw the first drafts of the scripts, they had a lot of <b>Write-Verbose<\/b> calls like this one:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$VerbosePreference = &#8220;Continue&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$filepath = &#8220;C:ps-testtest.txt&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$owner = &#8220;juneb&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$result = $true<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Write-Verbose (&#8220;Created {0} by {1}. Result is {2}.&#8221; &ndash;f $filepath, $owner, $result)\nHere&#8217;s the result:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">VERBOSE: Created C:ps-testtest.txt by juneb. Result is True.\nThis statement uses <i>string formatting<\/i>, that is, it creates a little template called a &#8220;format string&#8221; in the quoted string:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&#8220;Created {0} by {1}. Result is {2}.&#8221;\nThe placeholders in the template are integers enclosed in curly braces. The integers must begin with 0 and increment by 1.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">{0}, {1}, {2} &#8230;\nYou can put the placeholders anywhere in the format string and use a placeholder multiple times in the same string.\nTo assign values to the placeholders, type <b>-f<\/b> (for <b>format<\/b>), and then type a comma-separated list of the values in the order that you want them to appear. The first value replaces the first placeholder {0} in the string, the second values replaces the {1}, and so on.\nAny valid expression can be a value.<\/p>\n<p style=\"margin-left:30px\">PS C:&gt; &#8220;Created {0} on {1} by {2}. Result is {3} times {4}.&#8221; &ndash;f $filepath, [datetime]&#8221;1\/12\/2014&#8243;, $owner, &#8220;Success&#8221;, 14\/7<\/p>\n<p style=\"margin-left:30px\">Created C:ps-testtest.txt on 1\/12\/2014 12:00:00 AM by juneb. Result is Success times 2.\nYou can use string formatting anywhere in Windows PowerShell where a string value is permitted. When you use a formatted string as the value of the <b>Message<\/b> parameter of <b>Write-Verbose<\/b>, you must enclose it in parentheses. Otherwise, Windows PowerShell tries to interpret <b>-f<\/b> as a parameter of <b>Write-Verbose<\/b>.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:ps-test&gt; Write-Verbose -Message &#8220;{0}&#8221; -f &#8220;Hi&#8221; -Verbose<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Write-Verbose : A parameter cannot be found that matches parameter name &#8216;f&#8217;.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">At line:1 char:30<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">+ Write-Verbose -Message &#8220;{0}&#8221; -f &#8220;Hi&#8221; -Verbose<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;~~<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + CategoryInfo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : InvalidArgument: (:) [Write-Verbose], ParameterBindingException<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.WriteVerboseCommand<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:ps-test&gt; Write-Verbose -Message (&#8220;{0}&#8221; -f &#8220;Hi&#8221;) -Verbose<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">VERBOSE: Hi\nThe critical element here is the cool <b>-f<\/b>, which is the Windows PowerShell <b>format<\/b> operator. It&#8217;s described briefly in <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/hh847732.aspx\" target=\"_blank\">about_Operators<\/a>, but the real info is on the following sites:<\/p>\n<ul>\n<li><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=166450\" target=\"_blank\">String.Format method<\/a><\/li>\n<li><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=166451\" target=\"_blank\">Composite Formatting<\/a><\/li>\n<\/ul>\n<p>These docs are really worth reading. Someone put a lot of time into them and wrote extensive and very useful remarks. I&#8217;ve had them bookmarked for years.\nTo use the <b>-f<\/b> operator, you put the format string (with placeholders) on the left of the <b>-f<\/b> and the values on the right:<\/p>\n<p style=\"margin-left:30px\">&lt;format string&gt; -f &lt;values&gt;<\/p>\n<p style=\"margin-left:30px\">&#8220;{0} is the {1}.&#8221; &#8211; f&nbsp; &#8220;ScriptingWife&#8221;, &#8220;best&#8221;\nString formatting is really familiar to people who code in languages that don&#8217;t resolve expressions or variables that occur in strings. In these languages, you use a format string or you use the plus operator (<b>+<\/b>) to concatenate strings with expressions, like in this Python example:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&gt;&gt;&gt; filepath = &#8220;C:ps-testfile.txt&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&gt;&gt;&gt; owner = &#8220;juneb&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&gt;&gt;&gt; result = True<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&gt;&gt;&gt;&nbsp;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&gt;&gt;&gt; &#8220;Created &#8221; + filepath + &#8221; by &#8221; + owner + &#8220;. Result is &#8221; + str(result) + &#8220;.&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&#8216;Created C:\\ps-testfile.txt by juneb. Result is True.&#8217;\nGood luck getting the spaces right on the first try! Of course, you can do this in Windows PowerShell, too. Fortunately, it&#8217;s not required.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $filepath = &#8220;C:ps-testfile.txt&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $owner = &#8220;juneb&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $result = $True<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; &#8220;Created &#8221; + $filepath + &#8221; by &#8221; + $owner + &#8220;. Result is &#8221; + $result + &#8220;.&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Created C:ps-testfile.txt by juneb. Result is True.\nString formatting, <b>-f<\/b>, and string concatenation with the plus operator (<b>+<\/b>) work in Windows PowerShell, but I always thought that they were only included to help people who are familiar with other languages work successfully in Windows PowerShell.\nThis is fun stuff, I thought. But, honestly, there&#8217;s no need to do this in Windows PowerShell, because you can include expressions right in a double-quoted string.\n<b>&nbsp; &nbsp;Note<\/b>&nbsp; Single-quoted strings are different. Windows PowerShell prints them as-is with no substitutions.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; &#8216;Created $filepath by $owner. Result is $result.&#8217;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Created $filepath by $owner. Result is $result.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; &#8220;Created $filepath by $owner. Result is $result.&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Created C:ps-testfile.txt by juneb. Result is True.<\/p>\n<p style=\"margin-left:30px\">\/NOTE]\nSo, when I encountered formatted strings in the Windows Azure module scripts, I replaced them with the &#8220;PowerShell way&#8221; to do it. For example, I changed:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$VerbosePreference = &#8220;Continue&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$filepath = &#8220;C:ps-testtest.txt&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$owner = &#8220;juneb&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$result = &#8220;Success&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Write-Verbose (&#8220;Created {0} by {1}. Result is {2}.&#8221; &ndash;f $filepath, $owner, $result)\n&nbsp; &nbsp;To:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$VerbosePreference = &#8220;Continue&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$filepath = &#8220;C:ps-testtest.txt&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$owner = &#8220;juneb&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$result = &#8220;Success&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Write-Verbose &#8220;Created $filepath by $owner. Result is $result.&#8221;\nAnd it worked perfectly. So, I went through the script replacing the formatted strings with double-quoted strings.\nThen I came upon statements like these, which use properties and nested properties of objects.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $p = Get-Process PowerShell<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt;Write-Verbose (&#8220;The {0} process uses the {1} window style.&#8221; -f $p.Name, $p.StartInfo.WindowStyle)\nThese statements return:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">VERBOSE: The powershell process uses the Normal window style.\nBut, when I changed this <b>Write-Verbose<\/b> message to use the variables in a double-quoted string, it didn&#8217;t work:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $p = Get-Process PowerShell<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; Write-Verbose &#8220;The $p.Name process uses the $p.StartInfo.WindowStyle window style.&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">VERBOSE: The System.Diagnostics.Process (powershell).Name process uses the System.Diagnostics.Process (powershell).StartInfo.WindowStyle window style.\nIn formatted strings, any expression can be a value. That&#8217;s not true in double-quoted strings. When the values are expressions, the value type replaces the variable in the double-quoted string.\nYou can wrap the variable properties in another variable by using parentheses or braces. For example, change:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$&lt;name&gt;\n&nbsp; &nbsp;To:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">$($&lt;name&gt;)\n&nbsp; &nbsp;Or:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">${$&lt;name&gt;}\nSo, <strong>$p.Name<\/strong> becomes <strong>$($p.Name)<\/strong> or <strong>${$p.Name}<\/strong>.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; Write-Verbose &#8220;The $($p.Name) process uses the $($p.StartInfo.WindowStyle) window style.&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">VERBOSE: The powershell process uses the Normal window style.\nIt works, but it&#8217;s not pretty.\nAnother obvious solution is to evaluate the expressions and save the results in variables before using them in the double-quoted string.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $p = Get-Process PowerShell<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $pName = $p.Name<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $pStyle = $p.StartInfo.WindowStyle<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; Write-Verbose &#8220;The $pName process uses the $pStyle window style.&#8221;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">VERBOSE: The powershell process uses the Normal window style.\nThat works, of course, but it&#8217;s not more efficient. And, more importantly, when you&#8217;re writing scripts that you want people to read and interpret, it&#8217;s not always clearer.\nInstead, the original formatted string is efficient, and when you understand string substitution, much clearer.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $p = Get-Process PowerShell<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt;Write-Verbose (&#8220;The {0} process uses the {1} window style.&#8221; -f $p.Name, $p.StartInfo.WindowStyle)\nThe format string is also handy when creating strings that have a syntax that is different from the Windows PowerShell syntax. In this case, Windows PowerShell tries to interpret the variable that precedes the colon as a drive name:<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $urlHost = $url.Host<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">PS C:&gt; $ValuesPort = $values.Port<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Write-Verbose (&#8220;Add-AzureVM: The url is https:\/\/ $urlHost:$valuesPort\/msdeploy.axd&#8221; -Verbose<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">At line:1 char:49<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">+ Write-Verbose &#8220;Add-AzureVM: Publish Url https:\/\/$urlHost:$valuesPort\/msdeploy &#8230;<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~~~~~~~~~<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Variable reference is not valid. &#8216;:&#8217; was not followed by a valid variable name character. Consider using ${} to delimit the name.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + CategoryInfo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : ParserError: (:) [], ParentContainsErrorRecordException<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + FullyQualifiedErrorId : InvalidVariableReferenceWithDrive\nAny of the following statements work as expected without creating extra variables, but I think that the format string makes the intended syntax much clearer than the alternatives.<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Write-Verbose (&#8220;Add-AzureVM: The url is https:\/\/{0}:{1}\/msdeploy.axd&#8221; -f $url.Host, $Values.Port) -Verbose<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Write-Verbose (&#8220;Add-AzureVM: The url is https:\/\/$($url.Host):$($Values.Port)\/msdeploy.axd&#8221; -Verbose<\/p>\n<p class=\"Code\" style=\"margin-left:30px\">Write-Verbose (&#8220;Add-AzureVM: The url is https:\/\/${$url.Host}:${$Values.Port}\/msdeploy.axd&#8221; -Verbose<\/p>\n<p style=\"margin-left:30px\">Write-Verbose (&#8220;Add-AzureVM: The url is https:\/\/&#8221; + $url.Host + &#8220;:&#8221; + $Values.Port + &#8220;\/msdeploy.axd&#8221;) -Verbose\nSo, when the message string includes simple variables, I use double-quoted strings. When the message string includes a property value that is used repeatedly, I save the property value in a new variable and use the variable a double-quoted string.\nBut when a string includes multiple expressions or an alternate syntax, I prefer a formatted string.\nAnd I learned an important lesson about using formatted strings and the <b>-f<\/b> operator in Windows PowerShell. It&#8217;s not just an interesting artifact. It&#8217;s really useful.\n~June\nThanks for writing this informative post, June!\nI invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.\n<b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Learn about using formatted strings in Windows PowerShell. Microsoft Scripting Guy, Ed Wilson, here. Today we have another guest blog by June Blender. To read more of June&rsquo;s previous posts, see these Hey, Scripting Guy Blog posts. Now, here&rsquo;s June&hellip; I recently had the opportunity to help the Windows Azure and ASP.NET product teams [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[56,370,3,4,486,45],"class_list":["post-2003","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-june-blender","tag-scripting-guy","tag-scripting-techniques","tag-string","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Learn about using formatted strings in Windows PowerShell. Microsoft Scripting Guy, Ed Wilson, here. Today we have another guest blog by June Blender. To read more of June&rsquo;s previous posts, see these Hey, Scripting Guy Blog posts. Now, here&rsquo;s June&hellip; I recently had the opportunity to help the Windows Azure and ASP.NET product teams [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2003","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=2003"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2003\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=2003"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=2003"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=2003"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}