{"id":6973,"date":"2012-08-02T07:00:00","date_gmt":"2012-08-02T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2012\/08\/02\/exiting-a-batch-file-without-exiting-the-command-shell-and-batch-file-subroutines\/"},"modified":"2012-08-02T07:00:00","modified_gmt":"2012-08-02T07:00:00","slug":"exiting-a-batch-file-without-exiting-the-command-shell-and-batch-file-subroutines","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20120802-00\/?p=6973","title":{"rendered":"Exiting a batch file without exiting the command shell -and- batch file subroutines"},"content":{"rendered":"<p>\nPrepare your party hats: Batch File Week is almost over.\n<\/p>\n<p>\nIn your batch file,\nyou may want to exit batch file processing\n(say, you encountered an error and want to give up),\nbut if you use the <code>exit<\/code> command,\nthat will exit the entire command processor.\nWhich is probably not what you intended.\n<\/p>\n<p>\nBatch file processing ends when execution reaches\nthe end of the batch file.\nThe trick therefore is to use the <code>goto<\/code> command\nto jump to a label right before the end of the file,\nso that execution &#8220;falls off the end&#8221;.\n<\/p>\n<pre>\n@echo off\nif \"%1\"==\"\" echo You must provide a file name.&amp;goto end\nif NOT EXIST \"\\\\server\\backup\\%USERNAME%\\<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2003\/10\/22\/55388.aspx\">nul<\/a>\" mkdir \"\\\\server\\backup\\%USERNAME%\"\nif NOT EXIST \"\\\\server\\backup\\%USERNAME%\\nul\" echo Unable to create output directory.&amp;goto end\ncopy \"%1\" \"\\\\server\\backup\\%USERNAME%\"\n:end\n<\/pre>\n<p>\nHere, there are two places where we abandon batch file execution.\nOne is on an invalid parameter,\nand another is if the output directory couldn&#8217;t be created\n(or if it isn&#8217;t a directory at all).\n<\/p>\n<p>\nThe batch command interpreter provides a courtesy label\nto simply this technique:\nThe special goto target\n<code>goto :eof<\/code> (with the colon)\njumps to the end of the batch file.\nIt&#8217;s as if every batch file had a hidden goto label\ncalled <code>:eof<\/cODE> on the very last line.\n<\/p>\n<p>\nThe <code>goto :eof<\/code> trick becomes even more handy\nwhen you start playing with batch file subroutines.\nOkay, let's back up: Batch file subroutines?\n<\/p>\n<p>\nBy using the <code>call<\/cODE> command,\na batch file can invoke another batch file and regain\ncontrol after that other batch file returns.\n(If you forget the <code>call<\/code>, then control\ndoes not return. In other words, the default mode\nfor batch file invocation is <i>chain<\/i>.)\nIn other words, the <code>call<\/code> command\nlets you invoke another batch file as a subroutine.\nThe command line parameters are received by the\nother batch file as the usual numbered parameters\n<code>%1<\/code>, <code>%2<\/code>, <i>etc<\/i>.\n<\/p>\n<p>\nIt's annoying having to put every subroutine inside\nits own batch file,\nso the command interpreter folks added a way to\ncall a subroutine <i>inside the same batch file<\/i>.\nThe syntax for this is\n<code>call :label parameter parameter parameter<\/code>.\nThis is logically equivalent to a batch file recursively\ncalling itself, except that execution begins at the\nspecified label instead of the first line of the file.\n(It's as if a secret <code>goto label<\/code> were added\nto the top of the file.)\n<\/p>\n<p>\nAnd since it is a batch file, execution of the called\nsubroutine ends when execution falls off the end of the file.\nAnd that's where the special <code>goto<\/code> target\ncomes in handy.\nAt the end of your subroutine,\nyou can jump to the end of the batch file (so that\nexecution falls off the end) by doing a\n<code>goto :eof<\/code>.\n<\/p>\n<p>\nIn other words,\n<code>goto :eof<\/code> is the <code>return<\/cODE> statement\nfor batch file subroutines.\n<\/p>\n<p>\nLet's take it for a spin:\n<\/p>\n<pre>\n@echo off\ncall :subroutine a b c\ncall :subroutine d e f\ngoto :eof\n:subroutine\necho My parameters are 1=%1, 2=%2, 3=%3\ngoto :eof\n<\/pre>\n<p>\nThat final <code>goto :eof<\/code> is redundant,\nbut it's probably a good habit to get into,\nlike putting a <code>break;<\/code> at the end of\nyour last <code>case<\/code>.\n<\/p>\n<p>\nThe subroutine technique is handy even if you don't\nreally care about the subroutine,\nbecause stashing the arguments into the <code>%n<\/code>\nparameters lets you use the\n<a HREF=\"http:\/\/www.microsoft.com\/resources\/documentation\/windows\/xp\/all\/proddocs\/en-us\/percent.mspx\">\ntilde operators<\/a>\nto process the inbound parameter.\n<\/p>\n<pre>\n@echo off\ncall :printfilesize \"C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe\"\ngoto :eof\n:printfilesize\necho The size of %1 is %~z1\ngoto :eof\n<\/pre>\n<p>\nOkay, this isn't actually much of a handy trick because you can also\ndo it without a subroutine:\n<\/p>\n<pre>\n@echo off\nfor %%i ^\nin (\"C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe\") ^\ndo echo The size of %%i is %%~zi\n<\/pre>\n<p>\nOn the other hand, the subroutine trick combines well with the\n<code>FOR<\/code> command,\nsince it lets you put complex content in the loop body\nwithout having to mess with\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2006\/08\/23\/714650.aspx\">\ndelayed expansion<\/a>:\n<\/p>\n<pre>\n@echo off\nsetlocal\nset DISKSIZE=1474560\nset CLUSTER=512\nset DISKS=1\nset TOTAL=0\nfor %%i in (*) do call :onefile \"%%i\"\nset \/a DISKS=DISKS+1\necho Total disks required: %DISKS%\nendlocal\ngoto :eof\n:onefile\nset \/a SIZE=((%~z1 + CLUSTER - 1) \/ CLUSTER) * CLUSTER\nif %SIZE% GEQ %DISKSIZE% (\n    echo File %1 does not fit on a floppy - skipped\n    goto :eof\n)\nset \/a TOTAL=TOTAL+SIZE\nif %TOTAL% GEQ %DISKSIZE% (\n    echo ---- need another disk\n    set \/a DISKS=DISKS+1\n    set \/a TOTAL=SIZE\n)\necho copy %1\ngoto :eof\n<\/pre>\n<p>\nThis program calculates the number of floppy disks it would take\nto copy the contents of the current directory without compression.\n<\/p>\n<p>\nThe <code>setlocal<\/code> command takes a snapshot of the\nenvironment for restoration when we perform the <code>endlocal<\/code>\nat the end.\nThat will clean up our temporary variables when we're done.\n<\/p>\n<p>\nThe first two variables are parameters for the calculation,\nnamely the disk capacity and the cluster size.\n(We're assuming that the root directory can hold all the files\nwe may ultimately copy.\nHey, this is just a demonstration, not a real program.)\n<\/p>\n<p>\nThe next two variables are our running total of the number of\ndisks we've used so far,\nand how many bytes we've used on the last disk.\n<\/p>\n<p>\nThe <code>for<\/code> command iterates over all the files in the\ncurrent directory.\nFor each one, we call <code>:onefile<\/code> with the file name.\n<\/p>\n<p>\nThe <code>:onefile<\/code> subroutine does all the real work.\nFirst, it takes the file size <code>%~z1<\/code> and rounds it up\nto the nearest cluster.\nIt then sees if that size is larger than a floppy disk;\nif so, then we're doomed, so we just skip the file.\nOtherwise, we add the file to the current disk and see if it fits.\nIf not, then we declare the disk full and put the file on a brand\nnew disk.\n<\/p>\n<p>\nAfter the loop is complete,\nwe print the number of floppy disks we calculated.\n<\/p>\n<p>\n(This algorithm erroneously reports that no files require one disk.\nFixing that is left as an exercise.)\n<\/p>\n<p>\nThere's your quick introduction to the secret\n<code>:eof<\/code> label and batch file subroutines.\n<\/p>\n<p>\n[Raymond is currently away; this message was pre-recorded.]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Prepare your party hats: Batch File Week is almost over. In your batch file, you may want to exit batch file processing (say, you encountered an error and want to give up), but if you use the exit command, that will exit the entire command processor. Which is probably not what you intended. Batch file [&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-6973","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Prepare your party hats: Batch File Week is almost over. In your batch file, you may want to exit batch file processing (say, you encountered an error and want to give up), but if you use the exit command, that will exit the entire command processor. Which is probably not what you intended. Batch file [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/6973","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=6973"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/6973\/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=6973"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=6973"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=6973"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}