{"id":105439,"date":"2021-07-14T07:00:00","date_gmt":"2021-07-14T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105439"},"modified":"2021-07-14T07:06:02","modified_gmt":"2021-07-14T14:06:02","slug":"20210714-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210714-00\/?p=105439","title":{"rendered":"How to perform more complicated search and replace-style renaming in a batch file"},"content":{"rendered":"<p>Last time, we looked at <a title=\"How did copying and renaming with wildcards work in MS-DOS?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210713-00\/?p=105433\"> how copying and renaming with wildcards worked in MS-DOS<\/a>, and how it doesn&#8217;t work well if you are trying to perform search-and-replace operations where the strings have different lengths.<\/p>\n<p>For example, if you have a list of files, say <tt>fred001.txt<\/tt> through <tt>fred999.txt<\/tt> and you want to rename them to <tt>wilma001.txt<\/tt> through <tt>wilma999.txt<\/tt>, the obvious command<\/p>\n<pre>ren fred*.txt wilma*.txt\r\n<\/pre>\n<p>will not produce the desired results because the <tt>a<\/tt> in <tt>wilma<\/tt> overwrites the first character that was matched by the wildcard in the source pattern, since <tt>fred<\/tt> is only four characters long.<\/p>\n<p>You can still get what you want; you just won&#8217;t be able to use the wildcard algorithm to do it.<\/p>\n<pre>setlocal enabledelayedexpansion\r\nfor %%i in (fred*.txt) do set \"_=%%i\" &amp; ren \"%%i\" \"!_:fred=wilma!\"\r\n<\/pre>\n<p>We write a little batch file to perform the bulk rename operation.<\/p>\n<p>The main loop is driven by the <code>FOR<\/code> command, which we ask to enumerate all the files that match the pattern <tt>fred*.txt<\/tt>. For each such file, we set the variable <tt>_<\/tt> to the file name. I like to use <tt>_<\/tt> as a scratch variable name in batch files because it&#8217;s unlikely to collide with a name that means something to any particular program.\u00b9<\/p>\n<p>We then perform a non-wildcard <code>ren<\/code> command. The source file name is the file name which the <code>FOR<\/code> command gave us. The destination file name is the result of a search-replace operation with the <tt>_<\/tt> variable, where we ask to search for <tt>fred<\/tt> and change it to <tt>wilma<\/tt>.<\/p>\n<p>This is a two-liner instead of a one-liner because we need to enable delayed expansion so that we can delay the search-replace operation until after the <tt>_<\/tt> variable is set.<\/p>\n<p>If I need to do some sort of fancy renaming, I don&#8217;t do any of this. I&#8217;ll do a <code>dir \/b<\/code> and dump the list of file names into a file. Then I&#8217;ll edit that file and use the editor&#8217;s fancy search-replace features to convert it into a list of <code>REN<\/code> commands. I&#8217;ll look over the results to verify that they are doing what I want, and possibly perform some editing to deal with special cases like &#8220;Don&#8217;t rename <tt>fred314.txt<\/tt>; that one stays unchanged.&#8221; Once I&#8217;m satisfied, I save the results as a batch file and run it.<\/p>\n<p>If the editing is particularly complicated, I&#8217;ll write a one-off program to generate the batch file. I prefer generating a batch file to having the one-off program perform the renames directly, because that lets me preview the operation. You don&#8217;t want to mess it up.<\/p>\n<p><b>Bonus chatter<\/b>: The reimagined <a href=\"https:\/\/github.com\/microsoft\/PowerToys\"> Windows PowerToys<\/a> includes an interactive bulk renaming tool called <a href=\"https:\/\/github.com\/microsoft\/PowerToys\/wiki\/PowerRename-Overview\"> PowerRename<\/a>.<\/p>\n<p>\u00b9 Sometimes people new to batch programming will have need for a temporary variable to hold a path, and they call it <tt>PATH<\/tt>. This tends to result in a lot of head-scratching, since they are unwittingly modifying the executable search path.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Try something other than wildcards.<\/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-105439","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Try something other than wildcards.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105439","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=105439"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105439\/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=105439"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105439"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105439"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}