{"id":29993,"date":"2006-08-23T07:00:00","date_gmt":"2006-08-23T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2006\/08\/23\/environment-variable-expansion-occurs-when-the-command-is-read\/"},"modified":"2006-08-23T07:00:00","modified_gmt":"2006-08-23T07:00:00","slug":"environment-variable-expansion-occurs-when-the-command-is-read","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060823-00\/?p=29993","title":{"rendered":"Environment variable expansion occurs when the command is read"},"content":{"rendered":"<p>\nOn the command line (and in batch files),\nenvironment variable expansion occurs when the command is read.\nThis sounds obvious at first, but it has its own consequences.\n<\/p>\n<p>\nIn the online documentation for <code>SET<\/code>, one such\nconsequence is spelled out:\n<\/p>\n<pre>\n    set VAR=before\n    if \"%VAR%\" == \"before\" (\n        set VAR=after\n        if \"%VAR%\" == \"after\" @echo If you see this, it worked\n    )\n<\/pre>\n<p>\nwould never display the message, since the <code>%VAR%<\/code>\nin <i>both<\/i> &#8220;<code>if<\/code>&#8221; statements\nis substituted when the first &#8220;<code>if<\/code>&#8221; statement is read,\nsince it logically includes the body of the &#8220;<code>if<\/code>&#8220;,\nwhich is a compound statement.\n<\/p>\n<p>\nIn other words, the &#8220;<code>if<\/code>&#8221; command is not complete\nuntil the closing parenthesis is read.\nYou can see this if you type the commands interactively:\n<\/p>\n<pre>\nC:\\&gt;set VAR=before\nC:\\&gt;if \"%VAR%\" == \"before\" (\nMore? set VAR=after\nMore? if \"%VAR%\" == \"after\" @echo If you see this, it worked\nMore? )\nC:\\&gt;\n<\/pre>\n<p>\nNotice that the &#8220;<code>if<\/code>&#8221; command didn&#8217;t execute\nuntil you closed the parenthesis;\nthe command interpreter kept prompting &#8220;More?&#8221; to collect\nthe body of the &#8220;<code>if<\/code>&#8220;.\nThis means that everything you type as the body of the &#8220;<code>if<\/code>&#8221;\nis evaluated <strong>before the &#8220;<code>if<\/code>&#8221; condition or\nany of the lines in the body are evaluated<\/strong>.\nIt&#8217;s as if you had typed\n<\/p>\n<pre>\nC:\\&gt;if \"before\" == \"before\" (\nMore? set VAR=after\nMore? if \"before\" == \"after\" @echo If you see this, it worked\nMore? )\n<\/pre>\n<p>\nNote that this is different from most UNIX shells,\nwhich do not expand environment variables until the enclosing command\nis executed.\nFor example,\n<\/p>\n<pre>\n$ var=before\n$ var=after; echo $var\nafter\n<\/pre>\n<p>\nNotice that the <code>$x<\/code> is not expanded until the <code>echo<\/code>\ncommand&#8217;s arguments are being computed.\nThe analogous commands in the Windows command interpreter\nresult in something quite different:\n<\/p>\n<pre>\nC:\\&gt;set VAR=before\nC:\\&gt;set VAR=after &amp; echo %VAR%\nbefore\n<\/pre>\n<p>\nThat&#8217;s because the command interpreter expanded the environment\nvariables at the time the line was read (not at the time the line\nis executed), yielding\n<\/p>\n<pre>\nset VAR=after &amp; echo before\n<\/pre>\n<p>\nAs a result, the old value of <code>VAR<\/code> is echoed.\nSome people treat this as a feature, allowing them to &#8220;restore&#8221;\na variable without having to save it anywhere:\n<\/p>\n<pre>\nset VAR=newvalue &amp; call helper.cmd &amp; set VAR=%VAR%\n<\/pre>\n<p>\nThis command sets the <code>VAR<\/code> variable to a new value,\ncalls <code>helper.cmd<\/code> (which presumably uses the value\nof the <code>%VAR%<\/code> variable to control its behavior),\nthen magically restores the variable to its original value\nsince the <code>%VAR%<\/code> is expanded early, producing the\nold value.\n<\/p>\n<p>\nBut what if you want the variable to be expanded at execution\ntime rather than at parse time?\nFor that, you use &#8220;delayed expansion&#8221;, which is enabled by\nthe <code>\/V<\/code> command line option or by using the\n<code>SETLOCAL ENABLEDELAYEDEXPANSION<\/code> command in\na batch file.\n<\/p>\n<pre>\nC:\\&gt; copy con \"%TEMP%\\helper.cmd\"\nSETLOCAL ENABLEDELAYEDEXPANSION\nset VAR=before\nset VAR=after &amp; echo immediate:%VAR%, delayed:!VAR!\nENDLOCAL\n^Z\n        1 file(s) copied.\nC:\\&gt; \"%TEMP%\\helper.cmd\"\nC:\\&gt;SETLOCAL ENABLEDELAYEDEXPANSION\nC:\\&gt;set VAR=before\nC:\\&gt;set VAR=after   &amp; echo immediate:before, delayed:!VAR!\nimmediate:before, delayed:after\nC:\\&gt;ENDLOCAL\n<\/pre>\n<p>\nImmediate expansion is performed with percent signs, whereas\ndelayed expansion is performed with exclamation points.\n<\/p>\n<p>\nWhy is immediate expansion the default?\nBecause prior to Windows&nbsp;NT, that was the only type of\nexpansion supported by the command interpreter.\nRetaining immediate expansion as the default preserved backwards\ncompatibility with existing batch files.\n(The original command interpreter was written in assembly language.\nYou really didn&#8217;t want to be too clever or it would make your\nbrain hurt trying to maintain the code.\nAn interpreter loop of the form\n&#8220;Read a line, expand environment variables, evaluate&#8221;\nwas therefore simple and effective.)\n<\/p>\n<p>\nArmed with this understanding of immediate versus delayed\nexpansion, perhaps you can explain\n<a HREF=\"http:\/\/blogs.technet.com\/threekings\/archive\/2006\/02\/26\/420570.aspx\">\nwhat is really going on here<\/a>.\n(Hint: It has nothing to do with <code>ERRORLEVEL<\/code>.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>On the command line (and in batch files), environment variable expansion occurs when the command is read. This sounds obvious at first, but it has its own consequences. In the online documentation for SET, one such consequence is spelled out: set VAR=before if &#8220;%VAR%&#8221; == &#8220;before&#8221; ( set VAR=after if &#8220;%VAR%&#8221; == &#8220;after&#8221; @echo If [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-29993","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>On the command line (and in batch files), environment variable expansion occurs when the command is read. This sounds obvious at first, but it has its own consequences. In the online documentation for SET, one such consequence is spelled out: set VAR=before if &#8220;%VAR%&#8221; == &#8220;before&#8221; ( set VAR=after if &#8220;%VAR%&#8221; == &#8220;after&#8221; @echo If [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/29993","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=29993"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/29993\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=29993"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=29993"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=29993"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}