{"id":105433,"date":"2021-07-13T07:00:00","date_gmt":"2021-07-13T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105433"},"modified":"2021-07-14T07:07:11","modified_gmt":"2021-07-14T14:07:11","slug":"20210713-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210713-00\/?p=105433","title":{"rendered":"How did copying and renaming with wildcards work in MS-DOS?"},"content":{"rendered":"<p>Some time ago, I described <a title=\"How did wildcards work in MS-DOS?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20071217-00\/?p=24143\"> how wildcards worked in MS-DOS<\/a>, specifically how wildcards participate in pattern matching. Today, I&#8217;ll look at how wildcards participate in copying and renaming.<\/p>\n<p>First, the source and destination patterns are expanded into the eleven-character FCB format by the algorithm I described in that earlier article.<\/p>\n<p>Next, the directory is searched for files that match the source pattern.<\/p>\n<p>Once such a match is found, the fun begins: Applying the rename pattern.<\/p>\n<p>The way it works is that the rename pattern is used to produce the resulting file name, except that if a question mark is encountered in the rename pattern, then the corresponding character from the source file name is copied.<\/p>\n<p>Here&#8217;s an example with renaming, although the same exercise also applies to copying:<\/p>\n<pre>REN ABC*.D?F GHIJ*.KL?\r\n<\/pre>\n<p>The source and destination patterns are<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Human-readable<\/th>\n<th colspan=\"11\">Parsed pattern<\/th>\n<\/tr>\n<tr>\n<td>Source<\/td>\n<td><tt>ABC*.D?F<\/tt><\/td>\n<td><tt>A<\/tt><\/td>\n<td><tt>B<\/tt><\/td>\n<td><tt>C<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>F<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Destination<\/td>\n<td><tt>GHIJ*.KL?<\/tt><\/td>\n<td><tt>G<\/tt><\/td>\n<td><tt>H<\/tt><\/td>\n<td><tt>I<\/tt><\/td>\n<td><tt>J<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>K<\/tt><\/td>\n<td><tt>L<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Suppose we have a file <tt>ABC12345.D6F<\/tt> that matches the source pattern. How do we transform it according to the destination pattern?<\/p>\n<p>Simple: Stack the original file name and the destination pattern on top of each other. For each character of the output, take the corresponding character from the destination pattern, unless it is a question mark, in which case you take the corresponding character from the original file name.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Human-readable<\/th>\n<th colspan=\"11\">Parsed pattern<\/th>\n<\/tr>\n<tr>\n<td>Actual<\/td>\n<td><tt>ABC12345.D6F<\/tt><\/td>\n<td><tt>A<\/tt><\/td>\n<td><tt>B<\/tt><\/td>\n<td><tt>C<\/tt><\/td>\n<td><tt>1<\/tt><\/td>\n<td bgcolor=\"#ffbbff\"><tt>2<\/tt><\/td>\n<td bgcolor=\"#ffbbff\"><tt>3<\/tt><\/td>\n<td bgcolor=\"#ffbbff\"><tt>4<\/tt><\/td>\n<td bgcolor=\"#ffbbff\"><tt>5<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>6<\/tt><\/td>\n<td bgcolor=\"#ffbbff\"><tt>F<\/tt><\/td>\n<\/tr>\n<tr style=\"border-bottom: solid 2px black;\">\n<td>Destination<\/td>\n<td><tt>GHIJ*.KL?<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>G<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>H<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>I<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>J<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>K<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>L<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Result<\/td>\n<td><tt>GHIJ2345.KLF<\/tt><\/td>\n<td><tt>G<\/tt><\/td>\n<td><tt>H<\/tt><\/td>\n<td><tt>I<\/tt><\/td>\n<td><tt>J<\/tt><\/td>\n<td><tt>2<\/tt><\/td>\n<td><tt>3<\/tt><\/td>\n<td><tt>4<\/tt><\/td>\n<td><tt>5<\/tt><\/td>\n<td><tt>K<\/tt><\/td>\n<td><tt>L<\/tt><\/td>\n<td><tt>F<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>One way of thinking of this is that you treat the destination pattern as a stencil, with holes punched out where the question marks are. You then overlay the stencil on top of the original file name. The characters from the original file name show through the holes, and what you see is the result.<\/p>\n<div style=\"padding: 1ex; background-color: #a5ff8a; display: inline-block;\">\n<table class=\"cp3\" style=\"border-collapse: collapse; padding: 1ex; background-color: #a5ff8a;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td bgcolor=\"#a5ff8a\"><tt>G<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>H<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>I<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>J<\/tt><\/td>\n<td style=\"border: solid 1px black;\" bgcolor=\"#ffbbff\"><tt>2<\/tt><\/td>\n<td style=\"border: solid 1px black;\" bgcolor=\"#ffbbff\"><tt>3<\/tt><\/td>\n<td style=\"border: solid 1px black;\" bgcolor=\"#ffbbff\"><tt>4<\/tt><\/td>\n<td style=\"border: solid 1px black;\" bgcolor=\"#ffbbff\"><tt>5<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>K<\/tt><\/td>\n<td bgcolor=\"#a5ff8a\"><tt>L<\/tt><\/td>\n<td style=\"border: solid 1px black;\" bgcolor=\"#ffbbff\"><tt>F<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>This algorithm enabled some simple rename patterns, like changing a file extension:<\/p>\n<pre>REN FRED*.TXT *.DOC\r\n<\/pre>\n<p>Suppose there is a file called <tt>FRED123.TXT<\/tt>. Let&#8217;s see what happens:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Human-readable<\/th>\n<th colspan=\"11\">Parsed pattern<\/th>\n<\/tr>\n<tr>\n<td>Source<\/td>\n<td><tt>FRED*.*<\/tt><\/td>\n<td><tt>F<\/tt><\/td>\n<td><tt>R<\/tt><\/td>\n<td><tt>E<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Match<\/td>\n<td><tt>FRED123.TXT<\/tt><\/td>\n<td><tt>F<\/tt><\/td>\n<td><tt>R<\/tt><\/td>\n<td><tt>E<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>1<\/tt><\/td>\n<td><tt>2<\/tt><\/td>\n<td><tt>3<\/tt><\/td>\n<td><tt>\u00b7<\/tt><\/td>\n<td><tt>T<\/tt><\/td>\n<td><tt>X<\/tt><\/td>\n<td><tt>T<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Destination<\/td>\n<td><tt>*.DOC<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>O<\/tt><\/td>\n<td><tt>C<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Result<\/td>\n<td><tt>FRED123.DOC<\/tt><\/td>\n<td><tt>F<\/tt><\/td>\n<td><tt>R<\/tt><\/td>\n<td><tt>E<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>1<\/tt><\/td>\n<td><tt>2<\/tt><\/td>\n<td><tt>3<\/tt><\/td>\n<td><tt>\u00b7<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>O<\/tt><\/td>\n<td><tt>C<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Observe that we didn&#8217;t have to repeat <tt>FRED<\/tt> in the replacement pattern. The asterisk (which parses into question marks) just copies the existing file name, which includes the <tt>FRED<\/tt>.<\/p>\n<p>Does this make sense? Because it does carry its own consequences.<\/p>\n<p>If you&#8217;re not expecting the &#8220;copies the existing file name&#8221; behavior, and the question marks in the destination don&#8217;t match the question marks in the source, the results can be somewhat surprising:<\/p>\n<pre>ren FRED*.* WILMA*.*\r\n<\/pre>\n<p>Suppose there is a file called <tt>FRED123.TXT<\/tt>, and you were hoping to rename it to <tt>WILMA123.TXT<\/tt>. Let&#8217;s see what happens:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Human-readable<\/th>\n<th colspan=\"11\">Parsed pattern<\/th>\n<\/tr>\n<tr>\n<td>Source<\/td>\n<td><tt>FRED*.*<\/tt><\/td>\n<td><tt>F<\/tt><\/td>\n<td><tt>R<\/tt><\/td>\n<td><tt>E<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Match<\/td>\n<td><tt>FRED123.TXT<\/tt><\/td>\n<td><tt>F<\/tt><\/td>\n<td><tt>R<\/tt><\/td>\n<td><tt>E<\/tt><\/td>\n<td><tt>D<\/tt><\/td>\n<td><tt>1<\/tt><\/td>\n<td><tt>2<\/tt><\/td>\n<td><tt>3<\/tt><\/td>\n<td><tt>\u00b7<\/tt><\/td>\n<td><tt>T<\/tt><\/td>\n<td><tt>X<\/tt><\/td>\n<td><tt>T<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Destination<\/td>\n<td><tt>WILMA*.*<\/tt><\/td>\n<td><tt>W<\/tt><\/td>\n<td><tt>I<\/tt><\/td>\n<td><tt>L<\/tt><\/td>\n<td><tt>M<\/tt><\/td>\n<td><tt>A<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<td><tt>?<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Result<\/td>\n<td><tt>WILMA23.TXT<\/tt><\/td>\n<td><tt>W<\/tt><\/td>\n<td><tt>I<\/tt><\/td>\n<td><tt>L<\/tt><\/td>\n<td><tt>M<\/tt><\/td>\n<td><tt>A<\/tt><\/td>\n<td><tt>2<\/tt><\/td>\n<td><tt>3<\/tt><\/td>\n<td><tt>\u00b7<\/tt><\/td>\n<td><tt>T<\/tt><\/td>\n<td><tt>X<\/tt><\/td>\n<td><tt>T<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Since <tt>WILMA<\/tt> is one character longer than <tt>FRED<\/tt>, the question marks don&#8217;t line up. After copying <tt>WILMA<\/tt> to the result, we reach the first question mark in the destination, which lines up with the <i>second<\/i> question mark in the source, not the first. The character that is copied is the sixth character from the source, which is a <tt>2<\/tt>. The <tt>1<\/tt> from the source is not copied because it was overwritten by the <tt>A<\/tt> in <tt>WILMA<\/tt>.<\/p>\n<p>Wildcards are just question marks, and question marks match or copy a single corresponding character. They don&#8217;t &#8220;go looking around for their buddy question mark&#8221;. Computers weren&#8217;t that fancy back then.<\/p>\n<p>We had agreed that the <tt>REN FRED*.TXT *.DOC<\/tt> made sense in that it didn&#8217;t rename <tt>FRED123.TXT<\/tt> to <tt>123.DOC<\/tt>. But that rule that made sense then doesn&#8217;t seem to make sense in this more complicated case where we are doing what is more like a search\/replace in the filename.<\/p>\n<p>It&#8217;s important to understand the MS-DOS wildcard copy\/rename algorithm because Windows remains compatible with it, so as not to break existing batch files. We&#8217;ll look at this some more <a title=\"How to perform more complicated search and replace-style renaming in a batch file\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210714-00\/?p=105439\"> next time<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>By lining up two parallel arrays.<\/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":[2],"class_list":["post-105433","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>By lining up two parallel arrays.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105433","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=105433"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105433\/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=105433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}