How does the Windows RENAME command interpret wildcards?












71















How does the Windows RENAME (REN) command interpret wildcards?



The built in HELP facility is of no help - it doesn't address wildcards at all.



The Microsoft technet XP online help isn't much better. Here is all it has to say regarding wildcards:




"You can use wildcards (* and ?) in either file name parameter. If you use wildcards in filename2, the characters represented by the wildcards will be identical to the corresponding characters in filename1."




Not much help - there are many ways that statement can be interpretted.



I've managed to successfully use wildcards in the filename2 parameter on some occasions, but it has always been trial and error. I haven't been able to anticipate what works and what doesn't. Frequently I've had to resort to writing a small batch script with a FOR loop that parses each name so that I can build each new name as needed. Not very convenient.



If I knew the rules for how wildcards are processed then I figure I could use the RENAME command more effectively without having to resort to batch as often. Of course knowing the rules would also benefit batch development.



(Yes - this is a case where I am posting a paired question and answer. I got tired of not knowing the rules and decided to experiment on my own. I figure many others may be interested in what I discovered)










share|improve this question













migrated from stackoverflow.com Sep 16 '12 at 18:49


This question came from our site for professional and enthusiast programmers.



















  • There's heaps of good examples of how to rename with wildcards here: lagmonster.org/docs/DOS7/z-ren1.html

    – Matthew Lock
    Aug 20 '13 at 7:38






  • 4





    @MatthewLock - Interesting link, but those rules and examples are for MSDOS 7, not Windows. There are significant differences. For example, MSDOS does not allow appending additional chars after *, Windows does. That has huge consequences. I wish I had known about that site though; it might have made my investigation easier. The MSDOS7 rules are significantly different then the old DOS rules before long file names, and they are a step in the direction of how Windows handles it. I had found the pre long file name DOS rules, and they were worthless for my investigation.

    – dbenham
    Aug 20 '13 at 11:43











  • I did not know that ;)

    – Matthew Lock
    Aug 20 '13 at 13:14
















71















How does the Windows RENAME (REN) command interpret wildcards?



The built in HELP facility is of no help - it doesn't address wildcards at all.



The Microsoft technet XP online help isn't much better. Here is all it has to say regarding wildcards:




"You can use wildcards (* and ?) in either file name parameter. If you use wildcards in filename2, the characters represented by the wildcards will be identical to the corresponding characters in filename1."




Not much help - there are many ways that statement can be interpretted.



I've managed to successfully use wildcards in the filename2 parameter on some occasions, but it has always been trial and error. I haven't been able to anticipate what works and what doesn't. Frequently I've had to resort to writing a small batch script with a FOR loop that parses each name so that I can build each new name as needed. Not very convenient.



If I knew the rules for how wildcards are processed then I figure I could use the RENAME command more effectively without having to resort to batch as often. Of course knowing the rules would also benefit batch development.



(Yes - this is a case where I am posting a paired question and answer. I got tired of not knowing the rules and decided to experiment on my own. I figure many others may be interested in what I discovered)










share|improve this question













migrated from stackoverflow.com Sep 16 '12 at 18:49


This question came from our site for professional and enthusiast programmers.



















  • There's heaps of good examples of how to rename with wildcards here: lagmonster.org/docs/DOS7/z-ren1.html

    – Matthew Lock
    Aug 20 '13 at 7:38






  • 4





    @MatthewLock - Interesting link, but those rules and examples are for MSDOS 7, not Windows. There are significant differences. For example, MSDOS does not allow appending additional chars after *, Windows does. That has huge consequences. I wish I had known about that site though; it might have made my investigation easier. The MSDOS7 rules are significantly different then the old DOS rules before long file names, and they are a step in the direction of how Windows handles it. I had found the pre long file name DOS rules, and they were worthless for my investigation.

    – dbenham
    Aug 20 '13 at 11:43











  • I did not know that ;)

    – Matthew Lock
    Aug 20 '13 at 13:14














71












71








71


38






How does the Windows RENAME (REN) command interpret wildcards?



The built in HELP facility is of no help - it doesn't address wildcards at all.



The Microsoft technet XP online help isn't much better. Here is all it has to say regarding wildcards:




"You can use wildcards (* and ?) in either file name parameter. If you use wildcards in filename2, the characters represented by the wildcards will be identical to the corresponding characters in filename1."




Not much help - there are many ways that statement can be interpretted.



I've managed to successfully use wildcards in the filename2 parameter on some occasions, but it has always been trial and error. I haven't been able to anticipate what works and what doesn't. Frequently I've had to resort to writing a small batch script with a FOR loop that parses each name so that I can build each new name as needed. Not very convenient.



If I knew the rules for how wildcards are processed then I figure I could use the RENAME command more effectively without having to resort to batch as often. Of course knowing the rules would also benefit batch development.



(Yes - this is a case where I am posting a paired question and answer. I got tired of not knowing the rules and decided to experiment on my own. I figure many others may be interested in what I discovered)










share|improve this question














How does the Windows RENAME (REN) command interpret wildcards?



The built in HELP facility is of no help - it doesn't address wildcards at all.



The Microsoft technet XP online help isn't much better. Here is all it has to say regarding wildcards:




"You can use wildcards (* and ?) in either file name parameter. If you use wildcards in filename2, the characters represented by the wildcards will be identical to the corresponding characters in filename1."




Not much help - there are many ways that statement can be interpretted.



I've managed to successfully use wildcards in the filename2 parameter on some occasions, but it has always been trial and error. I haven't been able to anticipate what works and what doesn't. Frequently I've had to resort to writing a small batch script with a FOR loop that parses each name so that I can build each new name as needed. Not very convenient.



If I knew the rules for how wildcards are processed then I figure I could use the RENAME command more effectively without having to resort to batch as often. Of course knowing the rules would also benefit batch development.



(Yes - this is a case where I am posting a paired question and answer. I got tired of not knowing the rules and decided to experiment on my own. I figure many others may be interested in what I discovered)







batch batch-file command-line rename






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Sep 16 '12 at 13:59









dbenhamdbenham

7,80142029




7,80142029




migrated from stackoverflow.com Sep 16 '12 at 18:49


This question came from our site for professional and enthusiast programmers.









migrated from stackoverflow.com Sep 16 '12 at 18:49


This question came from our site for professional and enthusiast programmers.















  • There's heaps of good examples of how to rename with wildcards here: lagmonster.org/docs/DOS7/z-ren1.html

    – Matthew Lock
    Aug 20 '13 at 7:38






  • 4





    @MatthewLock - Interesting link, but those rules and examples are for MSDOS 7, not Windows. There are significant differences. For example, MSDOS does not allow appending additional chars after *, Windows does. That has huge consequences. I wish I had known about that site though; it might have made my investigation easier. The MSDOS7 rules are significantly different then the old DOS rules before long file names, and they are a step in the direction of how Windows handles it. I had found the pre long file name DOS rules, and they were worthless for my investigation.

    – dbenham
    Aug 20 '13 at 11:43











  • I did not know that ;)

    – Matthew Lock
    Aug 20 '13 at 13:14



















  • There's heaps of good examples of how to rename with wildcards here: lagmonster.org/docs/DOS7/z-ren1.html

    – Matthew Lock
    Aug 20 '13 at 7:38






  • 4





    @MatthewLock - Interesting link, but those rules and examples are for MSDOS 7, not Windows. There are significant differences. For example, MSDOS does not allow appending additional chars after *, Windows does. That has huge consequences. I wish I had known about that site though; it might have made my investigation easier. The MSDOS7 rules are significantly different then the old DOS rules before long file names, and they are a step in the direction of how Windows handles it. I had found the pre long file name DOS rules, and they were worthless for my investigation.

    – dbenham
    Aug 20 '13 at 11:43











  • I did not know that ;)

    – Matthew Lock
    Aug 20 '13 at 13:14

















There's heaps of good examples of how to rename with wildcards here: lagmonster.org/docs/DOS7/z-ren1.html

– Matthew Lock
Aug 20 '13 at 7:38





There's heaps of good examples of how to rename with wildcards here: lagmonster.org/docs/DOS7/z-ren1.html

– Matthew Lock
Aug 20 '13 at 7:38




4




4





@MatthewLock - Interesting link, but those rules and examples are for MSDOS 7, not Windows. There are significant differences. For example, MSDOS does not allow appending additional chars after *, Windows does. That has huge consequences. I wish I had known about that site though; it might have made my investigation easier. The MSDOS7 rules are significantly different then the old DOS rules before long file names, and they are a step in the direction of how Windows handles it. I had found the pre long file name DOS rules, and they were worthless for my investigation.

– dbenham
Aug 20 '13 at 11:43





@MatthewLock - Interesting link, but those rules and examples are for MSDOS 7, not Windows. There are significant differences. For example, MSDOS does not allow appending additional chars after *, Windows does. That has huge consequences. I wish I had known about that site though; it might have made my investigation easier. The MSDOS7 rules are significantly different then the old DOS rules before long file names, and they are a step in the direction of how Windows handles it. I had found the pre long file name DOS rules, and they were worthless for my investigation.

– dbenham
Aug 20 '13 at 11:43













I did not know that ;)

– Matthew Lock
Aug 20 '13 at 13:14





I did not know that ;)

– Matthew Lock
Aug 20 '13 at 13:14










4 Answers
4






active

oldest

votes


















108














These rules were discovered after extensive testing on a Vista machine. No tests were done with unicode in file names.



RENAME requires 2 parameters - a sourceMask, followed by a targetMask. Both the sourceMask and targetMask can contain * and/or ? wildcards. The behavior of the wildcards changes slightly between source and target masks.



Note - REN can be used to rename a folder, but wildcards are not allowed in either the sourceMask or targetMask when renaming a folder. If the sourceMask matches at least one file, then the file(s) will be renamed and folders will be ignored. If the sourceMask matches only folders and not files, then a syntax error is generated if wildcards appear in source or target. If the sourceMask does not match anything, then a "file not found" error results.



Also, when renaming files, wildcards are only allowed in the file name portion of the sourceMask. Wildcards are not allowed in the path leading up to the file name.



sourceMask



The sourceMask works as a filter to determine which files are renamed. The wildcards work here the same as with any other command that filters file names.




  • ? - Matches any 0 or 1 character except . This wildcard is greedy - it always consumes the next character if it is not a . However it will match nothing without failure if at name end or if the next character is a .


  • * - Matches any 0 or more characters including . (with one exception below). This wildcard is not greedy. It will match as little or as much as is needed to enable subsequent characters to match.



All non-wildcard characters must match themselves, with a few special case exceptions.




  • . - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with .)


  • {space} - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with {space})


  • *. at the end - Matches any 0 or more characters except . The terminating . can actually be any combination of . and {space} as long as the very last character in the mask is . This is the one and only exception where * does not simply match any set of characters.



The above rules are not that complex. But there is one more very important rule that makes the situation confusing: The sourceMask is compared against both the long name and the short 8.3 name (if it exists). This last rule can make interpretation of the results very tricky, because it is not always obvious when the mask is matching via the short name.



It is possible to use RegEdit to disable the generation of short 8.3 names on NTFS volumes, at which point interpretation of file mask results is much more straight forward. Any short names that were generated before disabling short names will remain.



targetMask



Note - I haven't done any rigorous testing, but it appears these same rules also work for the target name of the COPY commmand



The targetMask specifies the new name. It is always applied to the full long name; The targetMask is never applied to the short 8.3 name, even if the sourceMask matched the short 8.3 name.



The presence or absence of wildcards in the sourceMask has no impact on how wildcards are processed in the targetMask.



In the following discussion - c represents any character that is not *, ?, or .



The targetMask is processed against the source name strictly from left to right with no back-tracking.




  • c - Advances the position within the source name as long as the next character is not . and appends c to the target name. (Replaces the character that was in source with c, but never replaces .)


  • ? - Matches the next character from the source long name and appends it to the target name as long as the next character is not . If the next character is . or if at the end of the source name then no character is added to the result and the current position within the source name is unchanged.


  • * at end of targetMask - Appends all remaining characters from source to the target. If already at the end of source, then does nothing.


  • *c - Matches all source characters from current position through the last occurance of c (case sensitive greedy match) and appends the matched set of characters to the target name. If c is not found, then all remaining characters from source are appended, followed by c This is the only situation I am aware of where Windows file pattern matching is case sensitive.


  • *. - Matches all source characters from current position through the last occurance of . (greedy match) and appends the matched set of characters to the target name. If . is not found, then all remaining characters from source are appended, followed by .


  • *? - Appends all remaining characters from source to the target. If already at end of source then does nothing.


  • . without * in front - Advances the position in source through the first occurance of . without copying any characters, and appends . to the target name. If . is not found in the source, then advances to the end of source and appends . to the target name.



After the targetMask has been exhausted, any trailing . and {space} are trimmed off the end of the resulting target name because Windows file names cannot end with . or {space}



Some practical examples



Substitute a character in the 1st and 3rd positions prior to any extension (adds a 2nd or 3rd character if it doesn't exist yet)



ren  *  A?Z*
1 -> AZ
12 -> A2Z
1.txt -> AZ.txt
12.txt -> A2Z.txt
123 -> A2Z
123.txt -> A2Z.txt
1234 -> A2Z4
1234.txt -> A2Z4.txt


Change the (final) extension of every file



ren  *  *.txt
a -> a.txt
b.dat -> b.txt
c.x.y -> c.x.txt


Append an extension to every file



ren  *  *?.bak
a -> a.bak
b.dat -> b.dat.bak
c.x.y -> c.x.y.bak


Remove any extra extension after the initial extension. Note that adequate ? must be used to preserve the full existing name and initial extension.



ren  *  ?????.?????
a -> a
a.b -> a.b
a.b.c -> a.b
part1.part2.part3 -> part1.part2
123456.123456.123456 -> 12345.12345 (note truncated name and extension because not enough `?` were used)


Same as above, but filter out files with initial name and/or extension longer than 5 chars so that they are not truncated. (Obviously could add an additional ? on either end of targetMask to preserve names and extensions up to 6 chars long)



ren  ?????.?????.*  ?????.?????
a -> a
a.b -> a.b
a.b.c -> a.b
part1.part2.part3 -> part1.part2
123456.123456.123456 (Not renamed because doesn't match sourceMask)


Change characters after last _ in name and attempt to preserve extension. (Doesn't work properly if _ appears in extension)



ren  *_*  *_NEW.*
abcd_12345.txt -> abcd_NEW.txt
abc_newt_1.dat -> abc_newt_NEW.dat
abcdef.jpg (Not renamed because doesn't match sourceMask)
abcd_123.a_b -> abcd_123.a_NEW (not desired, but no simple RENAME form will work in this case)


Any name can be broken up into components that are delimited by . Characters may only be appended to or deleted from the end of each component. Characters cannot be deleted from or added to the beginning or middle of a component while preserving the remainder with wildcards. Substitutions are allowed anywhere.



ren  ??????.??????.??????  ?x.????999.*rForTheCourse
part1.part2 -> px.part999.rForTheCourse
part1.part2.part3 -> px.part999.parForTheCourse
part1.part2.part3.part4 (Not renamed because doesn't match sourceMask)
a.b.c -> ax.b999.crForTheCourse
a.b.CarPart3BEER -> ax.b999.CarParForTheCourse


If short names are enabled, then a sourceMask with at least 8 ? for the name and at least 3 ? for the extension will match all files because it will always match the short 8.3 name.



ren ????????.???  ?x.????999.*rForTheCourse
part1.part2.part3.part4 -> px.part999.part3.parForTheCourse





Useful quirk/bug? for deleting name prefixes



This SuperUser post describes how a set of forward slashes (/) can be used to delete leading characters from a file name. One slash is required for each character to be deleted. I've confirmed the behavior on a Windows 10 machine.



ren "abc-*.txt" "////*.txt"
abc-123.txt --> 123.txt
abc-HelloWorld.txt --> HelloWorld.txt


This technique only works if both the source and target masks are enclosed in double quotes. All of the following forms without the requisite quotes fail with this error: The syntax of the command is incorrect



REM - All of these forms fail with a syntax error.
ren abc-*.txt "////*.txt"
ren "abc-*.txt" ////*.txt
ren abc-*.txt ////*.txt


The / cannot be used to remove any characters in the middle or end of a file name. It can only remove leading (prefix) characters.



Technically the / is not functioning as a wildcard. Rather it is doing a simple character substitution, but then after the substitution, the REN command recognizes that / is not valid in a file name, and strips the leading / slashes from the name. REN gives a syntax error if it detects / in the middle of a target name.






Possible RENAME bug - a single command may rename the same file twice!



Starting in an empty test folder:



C:test>copy nul 123456789.123
1 file(s) copied.

C:test>dir /x
Volume in drive C is OS
Volume Serial Number is EE2C-5A11

Directory of C:test

09/15/2012 07:42 PM <DIR> .
09/15/2012 07:42 PM <DIR> ..
09/15/2012 07:42 PM 0 123456~1.123 123456789.123
1 File(s) 0 bytes
2 Dir(s) 327,237,562,368 bytes free

C:test>ren *1* 2*3.?x

C:test>dir /x
Volume in drive C is OS
Volume Serial Number is EE2C-5A11

Directory of C:test

09/15/2012 07:42 PM <DIR> .
09/15/2012 07:42 PM <DIR> ..
09/15/2012 07:42 PM 0 223456~1.XX 223456789.123.xx
1 File(s) 0 bytes
2 Dir(s) 327,237,562,368 bytes free

REM Expected result = 223456789.123.x


I believe the sourceMask *1* first matches the long file name, and the file is renamed to the expected result of 223456789.123.x. RENAME then continues to look for more files to process and finds the newly named file via the new short name of 223456~1.X. The file is then renamed again giving the final result of 223456789.123.xx.



If I disable 8.3 name generation then the RENAME gives the expected result.



I haven't fully worked out all of the trigger conditions that must exist to induce this odd behavior. I was concerned that it might be possible to create a never ending recursive RENAME, but I was never able to induce one.



I believe all of the following must be true to induce the bug. Every bugged case I saw had the following conditions, but not all cases that met the following conditions were bugged.




  • Short 8.3 names must be enabled

  • The sourceMask must match the original long name.

  • The initial rename must generate a short name that also matches the sourceMask

  • The initial renamed short name must sort later than the original short name (if it existed?)






share|improve this answer





















  • 6





    What a thorough answer.. +1.

    – meder omuraliev
    Oct 5 '12 at 19:48











  • Tremendously elaborate!

    – Andriy M
    Mar 6 '13 at 5:39






  • 13





    Based on this, Microsoft should just add "For usage, see superuser.com/a/475875 " in REN /?.

    – efotinis
    Jun 14 '13 at 21:27








  • 4





    @CAD - This answer is 100% original content that Simon included on his site upon my request. Look at the bottom of that SS64 page and you will see that Simon gives me credit for the work.

    – dbenham
    Mar 14 '17 at 3:46






  • 2





    @JacksOnF1re - New information/technique added to my answer. You can actually delete your Copy of prefix using an obscure forward slash technique: ren "Copy of *.txt" "////////*"

    – dbenham
    Jun 3 '17 at 19:33



















3














Similar to exebook, here's a C# implementation to get the target filename from a sourcefile.



I found 1 small error in dbenham's examples:



 ren  *_*  *_NEW.*
abc_newt_1.dat -> abc_newt_NEW.txt (should be: abd_newt_NEW.dat)


Here's the code:



    /// <summary>
/// Returns a filename based on the sourcefile and the targetMask, as used in the second argument in rename/copy operations.
/// targetMask may contain wildcards (* and ?).
///
/// This follows the rules of: http://superuser.com/questions/475874/how-does-the-windows-rename-command-interpret-wildcards
/// </summary>
/// <param name="sourcefile">filename to change to target without wildcards</param>
/// <param name="targetMask">mask with wildcards</param>
/// <returns>a valid target filename given sourcefile and targetMask</returns>
public static string GetTargetFileName(string sourcefile, string targetMask)
{
if (string.IsNullOrEmpty(sourcefile))
throw new ArgumentNullException("sourcefile");

if (string.IsNullOrEmpty(targetMask))
throw new ArgumentNullException("targetMask");

if (sourcefile.Contains('*') || sourcefile.Contains('?'))
throw new ArgumentException("sourcefile cannot contain wildcards");

// no wildcards: return complete mask as file
if (!targetMask.Contains('*') && !targetMask.Contains('?'))
return targetMask;

var maskReader = new StringReader(targetMask);
var sourceReader = new StringReader(sourcefile);
var targetBuilder = new StringBuilder();


while (maskReader.Peek() != -1)
{

int current = maskReader.Read();
int sourcePeek = sourceReader.Peek();
switch (current)
{
case '*':
int next = maskReader.Read();
switch (next)
{
case -1:
case '?':
// Append all remaining characters from sourcefile
targetBuilder.Append(sourceReader.ReadToEnd());
break;
default:
// Read source until the last occurrance of 'next'.
// We cannot seek in the StringReader, so we will create a new StringReader if needed
string sourceTail = sourceReader.ReadToEnd();
int lastIndexOf = sourceTail.LastIndexOf((char) next);
// If not found, append everything and the 'next' char
if (lastIndexOf == -1)
{
targetBuilder.Append(sourceTail);
targetBuilder.Append((char) next);

}
else
{
string toAppend = sourceTail.Substring(0, lastIndexOf + 1);
string rest = sourceTail.Substring(lastIndexOf + 1);
sourceReader.Dispose();
// go on with the rest...
sourceReader = new StringReader(rest);
targetBuilder.Append(toAppend);
}
break;
}

break;
case '?':
if (sourcePeek != -1 && sourcePeek != '.')
{
targetBuilder.Append((char)sourceReader.Read());
}
break;
case '.':
// eat all characters until the dot is found
while (sourcePeek != -1 && sourcePeek != '.')
{
sourceReader.Read();
sourcePeek = sourceReader.Peek();
}

targetBuilder.Append('.');
// need to eat the . when we peeked it
if (sourcePeek == '.')
sourceReader.Read();

break;
default:
if (sourcePeek != '.') sourceReader.Read(); // also consume the source's char if not .
targetBuilder.Append((char)current);
break;
}

}

sourceReader.Dispose();
maskReader.Dispose();
return targetBuilder.ToString().TrimEnd('.', ' ');
}


And here's an NUnit test method to test the examples:



    [Test]
public void TestGetTargetFileName()
{
string targetMask = "?????.?????";
Assert.AreEqual("a", FileUtil.GetTargetFileName("a", targetMask));
Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b", targetMask));
Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b.c", targetMask));
Assert.AreEqual("part1.part2", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
Assert.AreEqual("12345.12345", FileUtil.GetTargetFileName("123456.123456.123456", targetMask));

targetMask = "A?Z*";
Assert.AreEqual("AZ", FileUtil.GetTargetFileName("1", targetMask));
Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("12", targetMask));
Assert.AreEqual("AZ.txt", FileUtil.GetTargetFileName("1.txt", targetMask));
Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("12.txt", targetMask));
Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("123", targetMask));
Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("123.txt", targetMask));
Assert.AreEqual("A2Z4", FileUtil.GetTargetFileName("1234", targetMask));
Assert.AreEqual("A2Z4.txt", FileUtil.GetTargetFileName("1234.txt", targetMask));

targetMask = "*.txt";
Assert.AreEqual("a.txt", FileUtil.GetTargetFileName("a", targetMask));
Assert.AreEqual("b.txt", FileUtil.GetTargetFileName("b.dat", targetMask));
Assert.AreEqual("c.x.txt", FileUtil.GetTargetFileName("c.x.y", targetMask));

targetMask = "*?.bak";
Assert.AreEqual("a.bak", FileUtil.GetTargetFileName("a", targetMask));
Assert.AreEqual("b.dat.bak", FileUtil.GetTargetFileName("b.dat", targetMask));
Assert.AreEqual("c.x.y.bak", FileUtil.GetTargetFileName("c.x.y", targetMask));

targetMask = "*_NEW.*";
Assert.AreEqual("abcd_NEW.txt", FileUtil.GetTargetFileName("abcd_12345.txt", targetMask));
Assert.AreEqual("abc_newt_NEW.dat", FileUtil.GetTargetFileName("abc_newt_1.dat", targetMask));
Assert.AreEqual("abcd_123.a_NEW", FileUtil.GetTargetFileName("abcd_123.a_b", targetMask));

targetMask = "?x.????999.*rForTheCourse";

Assert.AreEqual("px.part999.rForTheCourse", FileUtil.GetTargetFileName("part1.part2", targetMask));
Assert.AreEqual("px.part999.parForTheCourse", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
Assert.AreEqual("ax.b999.crForTheCourse", FileUtil.GetTargetFileName("a.b.c", targetMask));
Assert.AreEqual("ax.b999.CarParForTheCourse", FileUtil.GetTargetFileName("a.b.CarPart3BEER", targetMask));

}





share|improve this answer
























  • Thanks for the head's up about the mistake in my example. I've edited my answer to fix it.

    – dbenham
    Dec 16 '14 at 12:16



















1














Maybe someone can find this useful. This JavaScript code is based on the answer by dbenham above.



I did not test sourceMask very much, but targetMask does match all examples given by dbenham.



function maskMatch(path, mask) {
mask = mask.replace(/./g, '\.')
mask = mask.replace(/?/g, '.')
mask = mask.replace(/*/g, '.+?')
var r = new RegExp('^'+mask+'$', '')
return path.match(r)
}

function maskNewName(path, mask) {
if (path == '') return
var x = 0, R = ''
for (var m = 0; m < mask.length; m++) {
var ch = mask[m], q = path[x], z = mask[m + 1]
if (ch != '.' && ch != '*' && ch != '?') {
if (q && q != '.') x++
R += ch
} else if (ch == '?') {
if (q && q != '.') R += q, x++
} else if (ch == '*' && m == mask.length - 1) {
while (x < path.length) R += path[x++]
} else if (ch == '*') {
if (z == '.') {
for (var i = path.length - 1; i >= 0; i--) if (path[i] == '.') break
if (i < 0) {
R += path.substr(x, path.length) + '.'
i = path.length
} else R += path.substr(x, i - x + 1)
x = i + 1, m++
} else if (z == '?') {
R += path.substr(x, path.length), m++, x = path.length
} else {
for (var i = path.length - 1; i >= 0; i--) if (path[i] == z) break
if (i < 0) R += path.substr(x, path.length) + z, x = path.length, m++
else R += path.substr(x, i - x), x = i + 1
}
} else if (ch == '.') {
while (x < path.length) if (path[x++] == '.') break
R += '.'
}
}
while (R[R.length - 1] == '.') R = R.substr(0, R.length - 1)
}





share|improve this answer

































    0














    I have managed to write this code in BASIC to mask wildcard filenames:



    REM inputs a filename and matches wildcards returning masked output filename.
    FUNCTION maskNewName$ (path$, mask$)
    IF path$ = "" THEN EXIT FUNCTION
    IF INSTR(path$, "?") OR INSTR(path$, "*") THEN EXIT FUNCTION
    x = 0
    R$ = ""
    FOR m = 0 TO LEN(mask$) - 1
    ch$ = MID$(mask$, m + 1, 1)
    q$ = MID$(path$, x + 1, 1)
    z$ = MID$(mask$, m + 2, 1)
    IF ch$ <> "." AND ch$ <> "*" AND ch$ <> "?" THEN
    IF LEN(q$) AND q$ <> "." THEN x = x + 1
    R$ = R$ + ch$
    ELSE
    IF ch$ = "?" THEN
    IF LEN(q$) AND q$ <> "." THEN R$ = R$ + q$: x = x + 1
    ELSE
    IF ch$ = "*" AND m = LEN(mask$) - 1 THEN
    WHILE x < LEN(path$)
    R$ = R$ + MID$(path$, x + 1, 1)
    x = x + 1
    WEND
    ELSE
    IF ch$ = "*" THEN
    IF z$ = "." THEN
    FOR i = LEN(path$) - 1 TO 0 STEP -1
    IF MID$(path$, i + 1, 1) = "." THEN EXIT FOR
    NEXT
    IF i < 0 THEN
    R$ = R$ + MID$(path$, x + 1) + "."
    i = LEN(path$)
    ELSE
    R$ = R$ + MID$(path$, x + 1, i - x + 1)
    END IF
    x = i + 1
    m = m + 1
    ELSE
    IF z$ = "?" THEN
    R$ = R$ + MID$(path$, x + 1, LEN(path$))
    m = m + 1
    x = LEN(path$)
    ELSE
    FOR i = LEN(path$) - 1 TO 0 STEP -1
    'IF MID$(path$, i + 1, 1) = z$ THEN EXIT FOR
    IF UCASE$(MID$(path$, i + 1, 1)) = UCASE$(z$) THEN EXIT FOR
    NEXT
    IF i < 0 THEN
    R$ = R$ + MID$(path$, x + 1, LEN(path$)) + z$
    x = LEN(path$)
    m = m + 1
    ELSE
    R$ = R$ + MID$(path$, x + 1, i - x)
    x = i + 1
    END IF
    END IF
    END IF
    ELSE
    IF ch$ = "." THEN
    DO WHILE x < LEN(path$)
    IF MID$(path$, x + 1, 1) = "." THEN
    x = x + 1
    EXIT DO
    END IF
    x = x + 1
    LOOP
    R$ = R$ + "."
    END IF
    END IF
    END IF
    END IF
    END IF
    NEXT
    DO WHILE RIGHT$(R$, 1) = "."
    R$ = LEFT$(R$, LEN(R$) - 1)
    LOOP
    R$ = RTRIM$(R$)
    maskNewName$ = R$
    END FUNCTION





    share|improve this answer





















    • 2





      Can you clarify how this answers what was asked in the question?

      – fixer1234
      Oct 13 '16 at 3:58











    • It replicates the function REN uses for wildcard matching such as processing REN *.TMP *.DOC depending on how the function is called before renaming the filenames.

      – eoredson
      Oct 13 '16 at 21:08











    Your Answer








    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "3"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsuperuser.com%2fquestions%2f475874%2fhow-does-the-windows-rename-command-interpret-wildcards%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    4 Answers
    4






    active

    oldest

    votes








    4 Answers
    4






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    108














    These rules were discovered after extensive testing on a Vista machine. No tests were done with unicode in file names.



    RENAME requires 2 parameters - a sourceMask, followed by a targetMask. Both the sourceMask and targetMask can contain * and/or ? wildcards. The behavior of the wildcards changes slightly between source and target masks.



    Note - REN can be used to rename a folder, but wildcards are not allowed in either the sourceMask or targetMask when renaming a folder. If the sourceMask matches at least one file, then the file(s) will be renamed and folders will be ignored. If the sourceMask matches only folders and not files, then a syntax error is generated if wildcards appear in source or target. If the sourceMask does not match anything, then a "file not found" error results.



    Also, when renaming files, wildcards are only allowed in the file name portion of the sourceMask. Wildcards are not allowed in the path leading up to the file name.



    sourceMask



    The sourceMask works as a filter to determine which files are renamed. The wildcards work here the same as with any other command that filters file names.




    • ? - Matches any 0 or 1 character except . This wildcard is greedy - it always consumes the next character if it is not a . However it will match nothing without failure if at name end or if the next character is a .


    • * - Matches any 0 or more characters including . (with one exception below). This wildcard is not greedy. It will match as little or as much as is needed to enable subsequent characters to match.



    All non-wildcard characters must match themselves, with a few special case exceptions.




    • . - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with .)


    • {space} - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with {space})


    • *. at the end - Matches any 0 or more characters except . The terminating . can actually be any combination of . and {space} as long as the very last character in the mask is . This is the one and only exception where * does not simply match any set of characters.



    The above rules are not that complex. But there is one more very important rule that makes the situation confusing: The sourceMask is compared against both the long name and the short 8.3 name (if it exists). This last rule can make interpretation of the results very tricky, because it is not always obvious when the mask is matching via the short name.



    It is possible to use RegEdit to disable the generation of short 8.3 names on NTFS volumes, at which point interpretation of file mask results is much more straight forward. Any short names that were generated before disabling short names will remain.



    targetMask



    Note - I haven't done any rigorous testing, but it appears these same rules also work for the target name of the COPY commmand



    The targetMask specifies the new name. It is always applied to the full long name; The targetMask is never applied to the short 8.3 name, even if the sourceMask matched the short 8.3 name.



    The presence or absence of wildcards in the sourceMask has no impact on how wildcards are processed in the targetMask.



    In the following discussion - c represents any character that is not *, ?, or .



    The targetMask is processed against the source name strictly from left to right with no back-tracking.




    • c - Advances the position within the source name as long as the next character is not . and appends c to the target name. (Replaces the character that was in source with c, but never replaces .)


    • ? - Matches the next character from the source long name and appends it to the target name as long as the next character is not . If the next character is . or if at the end of the source name then no character is added to the result and the current position within the source name is unchanged.


    • * at end of targetMask - Appends all remaining characters from source to the target. If already at the end of source, then does nothing.


    • *c - Matches all source characters from current position through the last occurance of c (case sensitive greedy match) and appends the matched set of characters to the target name. If c is not found, then all remaining characters from source are appended, followed by c This is the only situation I am aware of where Windows file pattern matching is case sensitive.


    • *. - Matches all source characters from current position through the last occurance of . (greedy match) and appends the matched set of characters to the target name. If . is not found, then all remaining characters from source are appended, followed by .


    • *? - Appends all remaining characters from source to the target. If already at end of source then does nothing.


    • . without * in front - Advances the position in source through the first occurance of . without copying any characters, and appends . to the target name. If . is not found in the source, then advances to the end of source and appends . to the target name.



    After the targetMask has been exhausted, any trailing . and {space} are trimmed off the end of the resulting target name because Windows file names cannot end with . or {space}



    Some practical examples



    Substitute a character in the 1st and 3rd positions prior to any extension (adds a 2nd or 3rd character if it doesn't exist yet)



    ren  *  A?Z*
    1 -> AZ
    12 -> A2Z
    1.txt -> AZ.txt
    12.txt -> A2Z.txt
    123 -> A2Z
    123.txt -> A2Z.txt
    1234 -> A2Z4
    1234.txt -> A2Z4.txt


    Change the (final) extension of every file



    ren  *  *.txt
    a -> a.txt
    b.dat -> b.txt
    c.x.y -> c.x.txt


    Append an extension to every file



    ren  *  *?.bak
    a -> a.bak
    b.dat -> b.dat.bak
    c.x.y -> c.x.y.bak


    Remove any extra extension after the initial extension. Note that adequate ? must be used to preserve the full existing name and initial extension.



    ren  *  ?????.?????
    a -> a
    a.b -> a.b
    a.b.c -> a.b
    part1.part2.part3 -> part1.part2
    123456.123456.123456 -> 12345.12345 (note truncated name and extension because not enough `?` were used)


    Same as above, but filter out files with initial name and/or extension longer than 5 chars so that they are not truncated. (Obviously could add an additional ? on either end of targetMask to preserve names and extensions up to 6 chars long)



    ren  ?????.?????.*  ?????.?????
    a -> a
    a.b -> a.b
    a.b.c -> a.b
    part1.part2.part3 -> part1.part2
    123456.123456.123456 (Not renamed because doesn't match sourceMask)


    Change characters after last _ in name and attempt to preserve extension. (Doesn't work properly if _ appears in extension)



    ren  *_*  *_NEW.*
    abcd_12345.txt -> abcd_NEW.txt
    abc_newt_1.dat -> abc_newt_NEW.dat
    abcdef.jpg (Not renamed because doesn't match sourceMask)
    abcd_123.a_b -> abcd_123.a_NEW (not desired, but no simple RENAME form will work in this case)


    Any name can be broken up into components that are delimited by . Characters may only be appended to or deleted from the end of each component. Characters cannot be deleted from or added to the beginning or middle of a component while preserving the remainder with wildcards. Substitutions are allowed anywhere.



    ren  ??????.??????.??????  ?x.????999.*rForTheCourse
    part1.part2 -> px.part999.rForTheCourse
    part1.part2.part3 -> px.part999.parForTheCourse
    part1.part2.part3.part4 (Not renamed because doesn't match sourceMask)
    a.b.c -> ax.b999.crForTheCourse
    a.b.CarPart3BEER -> ax.b999.CarParForTheCourse


    If short names are enabled, then a sourceMask with at least 8 ? for the name and at least 3 ? for the extension will match all files because it will always match the short 8.3 name.



    ren ????????.???  ?x.????999.*rForTheCourse
    part1.part2.part3.part4 -> px.part999.part3.parForTheCourse





    Useful quirk/bug? for deleting name prefixes



    This SuperUser post describes how a set of forward slashes (/) can be used to delete leading characters from a file name. One slash is required for each character to be deleted. I've confirmed the behavior on a Windows 10 machine.



    ren "abc-*.txt" "////*.txt"
    abc-123.txt --> 123.txt
    abc-HelloWorld.txt --> HelloWorld.txt


    This technique only works if both the source and target masks are enclosed in double quotes. All of the following forms without the requisite quotes fail with this error: The syntax of the command is incorrect



    REM - All of these forms fail with a syntax error.
    ren abc-*.txt "////*.txt"
    ren "abc-*.txt" ////*.txt
    ren abc-*.txt ////*.txt


    The / cannot be used to remove any characters in the middle or end of a file name. It can only remove leading (prefix) characters.



    Technically the / is not functioning as a wildcard. Rather it is doing a simple character substitution, but then after the substitution, the REN command recognizes that / is not valid in a file name, and strips the leading / slashes from the name. REN gives a syntax error if it detects / in the middle of a target name.






    Possible RENAME bug - a single command may rename the same file twice!



    Starting in an empty test folder:



    C:test>copy nul 123456789.123
    1 file(s) copied.

    C:test>dir /x
    Volume in drive C is OS
    Volume Serial Number is EE2C-5A11

    Directory of C:test

    09/15/2012 07:42 PM <DIR> .
    09/15/2012 07:42 PM <DIR> ..
    09/15/2012 07:42 PM 0 123456~1.123 123456789.123
    1 File(s) 0 bytes
    2 Dir(s) 327,237,562,368 bytes free

    C:test>ren *1* 2*3.?x

    C:test>dir /x
    Volume in drive C is OS
    Volume Serial Number is EE2C-5A11

    Directory of C:test

    09/15/2012 07:42 PM <DIR> .
    09/15/2012 07:42 PM <DIR> ..
    09/15/2012 07:42 PM 0 223456~1.XX 223456789.123.xx
    1 File(s) 0 bytes
    2 Dir(s) 327,237,562,368 bytes free

    REM Expected result = 223456789.123.x


    I believe the sourceMask *1* first matches the long file name, and the file is renamed to the expected result of 223456789.123.x. RENAME then continues to look for more files to process and finds the newly named file via the new short name of 223456~1.X. The file is then renamed again giving the final result of 223456789.123.xx.



    If I disable 8.3 name generation then the RENAME gives the expected result.



    I haven't fully worked out all of the trigger conditions that must exist to induce this odd behavior. I was concerned that it might be possible to create a never ending recursive RENAME, but I was never able to induce one.



    I believe all of the following must be true to induce the bug. Every bugged case I saw had the following conditions, but not all cases that met the following conditions were bugged.




    • Short 8.3 names must be enabled

    • The sourceMask must match the original long name.

    • The initial rename must generate a short name that also matches the sourceMask

    • The initial renamed short name must sort later than the original short name (if it existed?)






    share|improve this answer





















    • 6





      What a thorough answer.. +1.

      – meder omuraliev
      Oct 5 '12 at 19:48











    • Tremendously elaborate!

      – Andriy M
      Mar 6 '13 at 5:39






    • 13





      Based on this, Microsoft should just add "For usage, see superuser.com/a/475875 " in REN /?.

      – efotinis
      Jun 14 '13 at 21:27








    • 4





      @CAD - This answer is 100% original content that Simon included on his site upon my request. Look at the bottom of that SS64 page and you will see that Simon gives me credit for the work.

      – dbenham
      Mar 14 '17 at 3:46






    • 2





      @JacksOnF1re - New information/technique added to my answer. You can actually delete your Copy of prefix using an obscure forward slash technique: ren "Copy of *.txt" "////////*"

      – dbenham
      Jun 3 '17 at 19:33
















    108














    These rules were discovered after extensive testing on a Vista machine. No tests were done with unicode in file names.



    RENAME requires 2 parameters - a sourceMask, followed by a targetMask. Both the sourceMask and targetMask can contain * and/or ? wildcards. The behavior of the wildcards changes slightly between source and target masks.



    Note - REN can be used to rename a folder, but wildcards are not allowed in either the sourceMask or targetMask when renaming a folder. If the sourceMask matches at least one file, then the file(s) will be renamed and folders will be ignored. If the sourceMask matches only folders and not files, then a syntax error is generated if wildcards appear in source or target. If the sourceMask does not match anything, then a "file not found" error results.



    Also, when renaming files, wildcards are only allowed in the file name portion of the sourceMask. Wildcards are not allowed in the path leading up to the file name.



    sourceMask



    The sourceMask works as a filter to determine which files are renamed. The wildcards work here the same as with any other command that filters file names.




    • ? - Matches any 0 or 1 character except . This wildcard is greedy - it always consumes the next character if it is not a . However it will match nothing without failure if at name end or if the next character is a .


    • * - Matches any 0 or more characters including . (with one exception below). This wildcard is not greedy. It will match as little or as much as is needed to enable subsequent characters to match.



    All non-wildcard characters must match themselves, with a few special case exceptions.




    • . - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with .)


    • {space} - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with {space})


    • *. at the end - Matches any 0 or more characters except . The terminating . can actually be any combination of . and {space} as long as the very last character in the mask is . This is the one and only exception where * does not simply match any set of characters.



    The above rules are not that complex. But there is one more very important rule that makes the situation confusing: The sourceMask is compared against both the long name and the short 8.3 name (if it exists). This last rule can make interpretation of the results very tricky, because it is not always obvious when the mask is matching via the short name.



    It is possible to use RegEdit to disable the generation of short 8.3 names on NTFS volumes, at which point interpretation of file mask results is much more straight forward. Any short names that were generated before disabling short names will remain.



    targetMask



    Note - I haven't done any rigorous testing, but it appears these same rules also work for the target name of the COPY commmand



    The targetMask specifies the new name. It is always applied to the full long name; The targetMask is never applied to the short 8.3 name, even if the sourceMask matched the short 8.3 name.



    The presence or absence of wildcards in the sourceMask has no impact on how wildcards are processed in the targetMask.



    In the following discussion - c represents any character that is not *, ?, or .



    The targetMask is processed against the source name strictly from left to right with no back-tracking.




    • c - Advances the position within the source name as long as the next character is not . and appends c to the target name. (Replaces the character that was in source with c, but never replaces .)


    • ? - Matches the next character from the source long name and appends it to the target name as long as the next character is not . If the next character is . or if at the end of the source name then no character is added to the result and the current position within the source name is unchanged.


    • * at end of targetMask - Appends all remaining characters from source to the target. If already at the end of source, then does nothing.


    • *c - Matches all source characters from current position through the last occurance of c (case sensitive greedy match) and appends the matched set of characters to the target name. If c is not found, then all remaining characters from source are appended, followed by c This is the only situation I am aware of where Windows file pattern matching is case sensitive.


    • *. - Matches all source characters from current position through the last occurance of . (greedy match) and appends the matched set of characters to the target name. If . is not found, then all remaining characters from source are appended, followed by .


    • *? - Appends all remaining characters from source to the target. If already at end of source then does nothing.


    • . without * in front - Advances the position in source through the first occurance of . without copying any characters, and appends . to the target name. If . is not found in the source, then advances to the end of source and appends . to the target name.



    After the targetMask has been exhausted, any trailing . and {space} are trimmed off the end of the resulting target name because Windows file names cannot end with . or {space}



    Some practical examples



    Substitute a character in the 1st and 3rd positions prior to any extension (adds a 2nd or 3rd character if it doesn't exist yet)



    ren  *  A?Z*
    1 -> AZ
    12 -> A2Z
    1.txt -> AZ.txt
    12.txt -> A2Z.txt
    123 -> A2Z
    123.txt -> A2Z.txt
    1234 -> A2Z4
    1234.txt -> A2Z4.txt


    Change the (final) extension of every file



    ren  *  *.txt
    a -> a.txt
    b.dat -> b.txt
    c.x.y -> c.x.txt


    Append an extension to every file



    ren  *  *?.bak
    a -> a.bak
    b.dat -> b.dat.bak
    c.x.y -> c.x.y.bak


    Remove any extra extension after the initial extension. Note that adequate ? must be used to preserve the full existing name and initial extension.



    ren  *  ?????.?????
    a -> a
    a.b -> a.b
    a.b.c -> a.b
    part1.part2.part3 -> part1.part2
    123456.123456.123456 -> 12345.12345 (note truncated name and extension because not enough `?` were used)


    Same as above, but filter out files with initial name and/or extension longer than 5 chars so that they are not truncated. (Obviously could add an additional ? on either end of targetMask to preserve names and extensions up to 6 chars long)



    ren  ?????.?????.*  ?????.?????
    a -> a
    a.b -> a.b
    a.b.c -> a.b
    part1.part2.part3 -> part1.part2
    123456.123456.123456 (Not renamed because doesn't match sourceMask)


    Change characters after last _ in name and attempt to preserve extension. (Doesn't work properly if _ appears in extension)



    ren  *_*  *_NEW.*
    abcd_12345.txt -> abcd_NEW.txt
    abc_newt_1.dat -> abc_newt_NEW.dat
    abcdef.jpg (Not renamed because doesn't match sourceMask)
    abcd_123.a_b -> abcd_123.a_NEW (not desired, but no simple RENAME form will work in this case)


    Any name can be broken up into components that are delimited by . Characters may only be appended to or deleted from the end of each component. Characters cannot be deleted from or added to the beginning or middle of a component while preserving the remainder with wildcards. Substitutions are allowed anywhere.



    ren  ??????.??????.??????  ?x.????999.*rForTheCourse
    part1.part2 -> px.part999.rForTheCourse
    part1.part2.part3 -> px.part999.parForTheCourse
    part1.part2.part3.part4 (Not renamed because doesn't match sourceMask)
    a.b.c -> ax.b999.crForTheCourse
    a.b.CarPart3BEER -> ax.b999.CarParForTheCourse


    If short names are enabled, then a sourceMask with at least 8 ? for the name and at least 3 ? for the extension will match all files because it will always match the short 8.3 name.



    ren ????????.???  ?x.????999.*rForTheCourse
    part1.part2.part3.part4 -> px.part999.part3.parForTheCourse





    Useful quirk/bug? for deleting name prefixes



    This SuperUser post describes how a set of forward slashes (/) can be used to delete leading characters from a file name. One slash is required for each character to be deleted. I've confirmed the behavior on a Windows 10 machine.



    ren "abc-*.txt" "////*.txt"
    abc-123.txt --> 123.txt
    abc-HelloWorld.txt --> HelloWorld.txt


    This technique only works if both the source and target masks are enclosed in double quotes. All of the following forms without the requisite quotes fail with this error: The syntax of the command is incorrect



    REM - All of these forms fail with a syntax error.
    ren abc-*.txt "////*.txt"
    ren "abc-*.txt" ////*.txt
    ren abc-*.txt ////*.txt


    The / cannot be used to remove any characters in the middle or end of a file name. It can only remove leading (prefix) characters.



    Technically the / is not functioning as a wildcard. Rather it is doing a simple character substitution, but then after the substitution, the REN command recognizes that / is not valid in a file name, and strips the leading / slashes from the name. REN gives a syntax error if it detects / in the middle of a target name.






    Possible RENAME bug - a single command may rename the same file twice!



    Starting in an empty test folder:



    C:test>copy nul 123456789.123
    1 file(s) copied.

    C:test>dir /x
    Volume in drive C is OS
    Volume Serial Number is EE2C-5A11

    Directory of C:test

    09/15/2012 07:42 PM <DIR> .
    09/15/2012 07:42 PM <DIR> ..
    09/15/2012 07:42 PM 0 123456~1.123 123456789.123
    1 File(s) 0 bytes
    2 Dir(s) 327,237,562,368 bytes free

    C:test>ren *1* 2*3.?x

    C:test>dir /x
    Volume in drive C is OS
    Volume Serial Number is EE2C-5A11

    Directory of C:test

    09/15/2012 07:42 PM <DIR> .
    09/15/2012 07:42 PM <DIR> ..
    09/15/2012 07:42 PM 0 223456~1.XX 223456789.123.xx
    1 File(s) 0 bytes
    2 Dir(s) 327,237,562,368 bytes free

    REM Expected result = 223456789.123.x


    I believe the sourceMask *1* first matches the long file name, and the file is renamed to the expected result of 223456789.123.x. RENAME then continues to look for more files to process and finds the newly named file via the new short name of 223456~1.X. The file is then renamed again giving the final result of 223456789.123.xx.



    If I disable 8.3 name generation then the RENAME gives the expected result.



    I haven't fully worked out all of the trigger conditions that must exist to induce this odd behavior. I was concerned that it might be possible to create a never ending recursive RENAME, but I was never able to induce one.



    I believe all of the following must be true to induce the bug. Every bugged case I saw had the following conditions, but not all cases that met the following conditions were bugged.




    • Short 8.3 names must be enabled

    • The sourceMask must match the original long name.

    • The initial rename must generate a short name that also matches the sourceMask

    • The initial renamed short name must sort later than the original short name (if it existed?)






    share|improve this answer





















    • 6





      What a thorough answer.. +1.

      – meder omuraliev
      Oct 5 '12 at 19:48











    • Tremendously elaborate!

      – Andriy M
      Mar 6 '13 at 5:39






    • 13





      Based on this, Microsoft should just add "For usage, see superuser.com/a/475875 " in REN /?.

      – efotinis
      Jun 14 '13 at 21:27








    • 4





      @CAD - This answer is 100% original content that Simon included on his site upon my request. Look at the bottom of that SS64 page and you will see that Simon gives me credit for the work.

      – dbenham
      Mar 14 '17 at 3:46






    • 2





      @JacksOnF1re - New information/technique added to my answer. You can actually delete your Copy of prefix using an obscure forward slash technique: ren "Copy of *.txt" "////////*"

      – dbenham
      Jun 3 '17 at 19:33














    108












    108








    108







    These rules were discovered after extensive testing on a Vista machine. No tests were done with unicode in file names.



    RENAME requires 2 parameters - a sourceMask, followed by a targetMask. Both the sourceMask and targetMask can contain * and/or ? wildcards. The behavior of the wildcards changes slightly between source and target masks.



    Note - REN can be used to rename a folder, but wildcards are not allowed in either the sourceMask or targetMask when renaming a folder. If the sourceMask matches at least one file, then the file(s) will be renamed and folders will be ignored. If the sourceMask matches only folders and not files, then a syntax error is generated if wildcards appear in source or target. If the sourceMask does not match anything, then a "file not found" error results.



    Also, when renaming files, wildcards are only allowed in the file name portion of the sourceMask. Wildcards are not allowed in the path leading up to the file name.



    sourceMask



    The sourceMask works as a filter to determine which files are renamed. The wildcards work here the same as with any other command that filters file names.




    • ? - Matches any 0 or 1 character except . This wildcard is greedy - it always consumes the next character if it is not a . However it will match nothing without failure if at name end or if the next character is a .


    • * - Matches any 0 or more characters including . (with one exception below). This wildcard is not greedy. It will match as little or as much as is needed to enable subsequent characters to match.



    All non-wildcard characters must match themselves, with a few special case exceptions.




    • . - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with .)


    • {space} - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with {space})


    • *. at the end - Matches any 0 or more characters except . The terminating . can actually be any combination of . and {space} as long as the very last character in the mask is . This is the one and only exception where * does not simply match any set of characters.



    The above rules are not that complex. But there is one more very important rule that makes the situation confusing: The sourceMask is compared against both the long name and the short 8.3 name (if it exists). This last rule can make interpretation of the results very tricky, because it is not always obvious when the mask is matching via the short name.



    It is possible to use RegEdit to disable the generation of short 8.3 names on NTFS volumes, at which point interpretation of file mask results is much more straight forward. Any short names that were generated before disabling short names will remain.



    targetMask



    Note - I haven't done any rigorous testing, but it appears these same rules also work for the target name of the COPY commmand



    The targetMask specifies the new name. It is always applied to the full long name; The targetMask is never applied to the short 8.3 name, even if the sourceMask matched the short 8.3 name.



    The presence or absence of wildcards in the sourceMask has no impact on how wildcards are processed in the targetMask.



    In the following discussion - c represents any character that is not *, ?, or .



    The targetMask is processed against the source name strictly from left to right with no back-tracking.




    • c - Advances the position within the source name as long as the next character is not . and appends c to the target name. (Replaces the character that was in source with c, but never replaces .)


    • ? - Matches the next character from the source long name and appends it to the target name as long as the next character is not . If the next character is . or if at the end of the source name then no character is added to the result and the current position within the source name is unchanged.


    • * at end of targetMask - Appends all remaining characters from source to the target. If already at the end of source, then does nothing.


    • *c - Matches all source characters from current position through the last occurance of c (case sensitive greedy match) and appends the matched set of characters to the target name. If c is not found, then all remaining characters from source are appended, followed by c This is the only situation I am aware of where Windows file pattern matching is case sensitive.


    • *. - Matches all source characters from current position through the last occurance of . (greedy match) and appends the matched set of characters to the target name. If . is not found, then all remaining characters from source are appended, followed by .


    • *? - Appends all remaining characters from source to the target. If already at end of source then does nothing.


    • . without * in front - Advances the position in source through the first occurance of . without copying any characters, and appends . to the target name. If . is not found in the source, then advances to the end of source and appends . to the target name.



    After the targetMask has been exhausted, any trailing . and {space} are trimmed off the end of the resulting target name because Windows file names cannot end with . or {space}



    Some practical examples



    Substitute a character in the 1st and 3rd positions prior to any extension (adds a 2nd or 3rd character if it doesn't exist yet)



    ren  *  A?Z*
    1 -> AZ
    12 -> A2Z
    1.txt -> AZ.txt
    12.txt -> A2Z.txt
    123 -> A2Z
    123.txt -> A2Z.txt
    1234 -> A2Z4
    1234.txt -> A2Z4.txt


    Change the (final) extension of every file



    ren  *  *.txt
    a -> a.txt
    b.dat -> b.txt
    c.x.y -> c.x.txt


    Append an extension to every file



    ren  *  *?.bak
    a -> a.bak
    b.dat -> b.dat.bak
    c.x.y -> c.x.y.bak


    Remove any extra extension after the initial extension. Note that adequate ? must be used to preserve the full existing name and initial extension.



    ren  *  ?????.?????
    a -> a
    a.b -> a.b
    a.b.c -> a.b
    part1.part2.part3 -> part1.part2
    123456.123456.123456 -> 12345.12345 (note truncated name and extension because not enough `?` were used)


    Same as above, but filter out files with initial name and/or extension longer than 5 chars so that they are not truncated. (Obviously could add an additional ? on either end of targetMask to preserve names and extensions up to 6 chars long)



    ren  ?????.?????.*  ?????.?????
    a -> a
    a.b -> a.b
    a.b.c -> a.b
    part1.part2.part3 -> part1.part2
    123456.123456.123456 (Not renamed because doesn't match sourceMask)


    Change characters after last _ in name and attempt to preserve extension. (Doesn't work properly if _ appears in extension)



    ren  *_*  *_NEW.*
    abcd_12345.txt -> abcd_NEW.txt
    abc_newt_1.dat -> abc_newt_NEW.dat
    abcdef.jpg (Not renamed because doesn't match sourceMask)
    abcd_123.a_b -> abcd_123.a_NEW (not desired, but no simple RENAME form will work in this case)


    Any name can be broken up into components that are delimited by . Characters may only be appended to or deleted from the end of each component. Characters cannot be deleted from or added to the beginning or middle of a component while preserving the remainder with wildcards. Substitutions are allowed anywhere.



    ren  ??????.??????.??????  ?x.????999.*rForTheCourse
    part1.part2 -> px.part999.rForTheCourse
    part1.part2.part3 -> px.part999.parForTheCourse
    part1.part2.part3.part4 (Not renamed because doesn't match sourceMask)
    a.b.c -> ax.b999.crForTheCourse
    a.b.CarPart3BEER -> ax.b999.CarParForTheCourse


    If short names are enabled, then a sourceMask with at least 8 ? for the name and at least 3 ? for the extension will match all files because it will always match the short 8.3 name.



    ren ????????.???  ?x.????999.*rForTheCourse
    part1.part2.part3.part4 -> px.part999.part3.parForTheCourse





    Useful quirk/bug? for deleting name prefixes



    This SuperUser post describes how a set of forward slashes (/) can be used to delete leading characters from a file name. One slash is required for each character to be deleted. I've confirmed the behavior on a Windows 10 machine.



    ren "abc-*.txt" "////*.txt"
    abc-123.txt --> 123.txt
    abc-HelloWorld.txt --> HelloWorld.txt


    This technique only works if both the source and target masks are enclosed in double quotes. All of the following forms without the requisite quotes fail with this error: The syntax of the command is incorrect



    REM - All of these forms fail with a syntax error.
    ren abc-*.txt "////*.txt"
    ren "abc-*.txt" ////*.txt
    ren abc-*.txt ////*.txt


    The / cannot be used to remove any characters in the middle or end of a file name. It can only remove leading (prefix) characters.



    Technically the / is not functioning as a wildcard. Rather it is doing a simple character substitution, but then after the substitution, the REN command recognizes that / is not valid in a file name, and strips the leading / slashes from the name. REN gives a syntax error if it detects / in the middle of a target name.






    Possible RENAME bug - a single command may rename the same file twice!



    Starting in an empty test folder:



    C:test>copy nul 123456789.123
    1 file(s) copied.

    C:test>dir /x
    Volume in drive C is OS
    Volume Serial Number is EE2C-5A11

    Directory of C:test

    09/15/2012 07:42 PM <DIR> .
    09/15/2012 07:42 PM <DIR> ..
    09/15/2012 07:42 PM 0 123456~1.123 123456789.123
    1 File(s) 0 bytes
    2 Dir(s) 327,237,562,368 bytes free

    C:test>ren *1* 2*3.?x

    C:test>dir /x
    Volume in drive C is OS
    Volume Serial Number is EE2C-5A11

    Directory of C:test

    09/15/2012 07:42 PM <DIR> .
    09/15/2012 07:42 PM <DIR> ..
    09/15/2012 07:42 PM 0 223456~1.XX 223456789.123.xx
    1 File(s) 0 bytes
    2 Dir(s) 327,237,562,368 bytes free

    REM Expected result = 223456789.123.x


    I believe the sourceMask *1* first matches the long file name, and the file is renamed to the expected result of 223456789.123.x. RENAME then continues to look for more files to process and finds the newly named file via the new short name of 223456~1.X. The file is then renamed again giving the final result of 223456789.123.xx.



    If I disable 8.3 name generation then the RENAME gives the expected result.



    I haven't fully worked out all of the trigger conditions that must exist to induce this odd behavior. I was concerned that it might be possible to create a never ending recursive RENAME, but I was never able to induce one.



    I believe all of the following must be true to induce the bug. Every bugged case I saw had the following conditions, but not all cases that met the following conditions were bugged.




    • Short 8.3 names must be enabled

    • The sourceMask must match the original long name.

    • The initial rename must generate a short name that also matches the sourceMask

    • The initial renamed short name must sort later than the original short name (if it existed?)






    share|improve this answer















    These rules were discovered after extensive testing on a Vista machine. No tests were done with unicode in file names.



    RENAME requires 2 parameters - a sourceMask, followed by a targetMask. Both the sourceMask and targetMask can contain * and/or ? wildcards. The behavior of the wildcards changes slightly between source and target masks.



    Note - REN can be used to rename a folder, but wildcards are not allowed in either the sourceMask or targetMask when renaming a folder. If the sourceMask matches at least one file, then the file(s) will be renamed and folders will be ignored. If the sourceMask matches only folders and not files, then a syntax error is generated if wildcards appear in source or target. If the sourceMask does not match anything, then a "file not found" error results.



    Also, when renaming files, wildcards are only allowed in the file name portion of the sourceMask. Wildcards are not allowed in the path leading up to the file name.



    sourceMask



    The sourceMask works as a filter to determine which files are renamed. The wildcards work here the same as with any other command that filters file names.




    • ? - Matches any 0 or 1 character except . This wildcard is greedy - it always consumes the next character if it is not a . However it will match nothing without failure if at name end or if the next character is a .


    • * - Matches any 0 or more characters including . (with one exception below). This wildcard is not greedy. It will match as little or as much as is needed to enable subsequent characters to match.



    All non-wildcard characters must match themselves, with a few special case exceptions.




    • . - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with .)


    • {space} - Matches itself or it can match the end of name (nothing) if no more characters remain. (Note - a valid Windows name cannot end with {space})


    • *. at the end - Matches any 0 or more characters except . The terminating . can actually be any combination of . and {space} as long as the very last character in the mask is . This is the one and only exception where * does not simply match any set of characters.



    The above rules are not that complex. But there is one more very important rule that makes the situation confusing: The sourceMask is compared against both the long name and the short 8.3 name (if it exists). This last rule can make interpretation of the results very tricky, because it is not always obvious when the mask is matching via the short name.



    It is possible to use RegEdit to disable the generation of short 8.3 names on NTFS volumes, at which point interpretation of file mask results is much more straight forward. Any short names that were generated before disabling short names will remain.



    targetMask



    Note - I haven't done any rigorous testing, but it appears these same rules also work for the target name of the COPY commmand



    The targetMask specifies the new name. It is always applied to the full long name; The targetMask is never applied to the short 8.3 name, even if the sourceMask matched the short 8.3 name.



    The presence or absence of wildcards in the sourceMask has no impact on how wildcards are processed in the targetMask.



    In the following discussion - c represents any character that is not *, ?, or .



    The targetMask is processed against the source name strictly from left to right with no back-tracking.




    • c - Advances the position within the source name as long as the next character is not . and appends c to the target name. (Replaces the character that was in source with c, but never replaces .)


    • ? - Matches the next character from the source long name and appends it to the target name as long as the next character is not . If the next character is . or if at the end of the source name then no character is added to the result and the current position within the source name is unchanged.


    • * at end of targetMask - Appends all remaining characters from source to the target. If already at the end of source, then does nothing.


    • *c - Matches all source characters from current position through the last occurance of c (case sensitive greedy match) and appends the matched set of characters to the target name. If c is not found, then all remaining characters from source are appended, followed by c This is the only situation I am aware of where Windows file pattern matching is case sensitive.


    • *. - Matches all source characters from current position through the last occurance of . (greedy match) and appends the matched set of characters to the target name. If . is not found, then all remaining characters from source are appended, followed by .


    • *? - Appends all remaining characters from source to the target. If already at end of source then does nothing.


    • . without * in front - Advances the position in source through the first occurance of . without copying any characters, and appends . to the target name. If . is not found in the source, then advances to the end of source and appends . to the target name.



    After the targetMask has been exhausted, any trailing . and {space} are trimmed off the end of the resulting target name because Windows file names cannot end with . or {space}



    Some practical examples



    Substitute a character in the 1st and 3rd positions prior to any extension (adds a 2nd or 3rd character if it doesn't exist yet)



    ren  *  A?Z*
    1 -> AZ
    12 -> A2Z
    1.txt -> AZ.txt
    12.txt -> A2Z.txt
    123 -> A2Z
    123.txt -> A2Z.txt
    1234 -> A2Z4
    1234.txt -> A2Z4.txt


    Change the (final) extension of every file



    ren  *  *.txt
    a -> a.txt
    b.dat -> b.txt
    c.x.y -> c.x.txt


    Append an extension to every file



    ren  *  *?.bak
    a -> a.bak
    b.dat -> b.dat.bak
    c.x.y -> c.x.y.bak


    Remove any extra extension after the initial extension. Note that adequate ? must be used to preserve the full existing name and initial extension.



    ren  *  ?????.?????
    a -> a
    a.b -> a.b
    a.b.c -> a.b
    part1.part2.part3 -> part1.part2
    123456.123456.123456 -> 12345.12345 (note truncated name and extension because not enough `?` were used)


    Same as above, but filter out files with initial name and/or extension longer than 5 chars so that they are not truncated. (Obviously could add an additional ? on either end of targetMask to preserve names and extensions up to 6 chars long)



    ren  ?????.?????.*  ?????.?????
    a -> a
    a.b -> a.b
    a.b.c -> a.b
    part1.part2.part3 -> part1.part2
    123456.123456.123456 (Not renamed because doesn't match sourceMask)


    Change characters after last _ in name and attempt to preserve extension. (Doesn't work properly if _ appears in extension)



    ren  *_*  *_NEW.*
    abcd_12345.txt -> abcd_NEW.txt
    abc_newt_1.dat -> abc_newt_NEW.dat
    abcdef.jpg (Not renamed because doesn't match sourceMask)
    abcd_123.a_b -> abcd_123.a_NEW (not desired, but no simple RENAME form will work in this case)


    Any name can be broken up into components that are delimited by . Characters may only be appended to or deleted from the end of each component. Characters cannot be deleted from or added to the beginning or middle of a component while preserving the remainder with wildcards. Substitutions are allowed anywhere.



    ren  ??????.??????.??????  ?x.????999.*rForTheCourse
    part1.part2 -> px.part999.rForTheCourse
    part1.part2.part3 -> px.part999.parForTheCourse
    part1.part2.part3.part4 (Not renamed because doesn't match sourceMask)
    a.b.c -> ax.b999.crForTheCourse
    a.b.CarPart3BEER -> ax.b999.CarParForTheCourse


    If short names are enabled, then a sourceMask with at least 8 ? for the name and at least 3 ? for the extension will match all files because it will always match the short 8.3 name.



    ren ????????.???  ?x.????999.*rForTheCourse
    part1.part2.part3.part4 -> px.part999.part3.parForTheCourse





    Useful quirk/bug? for deleting name prefixes



    This SuperUser post describes how a set of forward slashes (/) can be used to delete leading characters from a file name. One slash is required for each character to be deleted. I've confirmed the behavior on a Windows 10 machine.



    ren "abc-*.txt" "////*.txt"
    abc-123.txt --> 123.txt
    abc-HelloWorld.txt --> HelloWorld.txt


    This technique only works if both the source and target masks are enclosed in double quotes. All of the following forms without the requisite quotes fail with this error: The syntax of the command is incorrect



    REM - All of these forms fail with a syntax error.
    ren abc-*.txt "////*.txt"
    ren "abc-*.txt" ////*.txt
    ren abc-*.txt ////*.txt


    The / cannot be used to remove any characters in the middle or end of a file name. It can only remove leading (prefix) characters.



    Technically the / is not functioning as a wildcard. Rather it is doing a simple character substitution, but then after the substitution, the REN command recognizes that / is not valid in a file name, and strips the leading / slashes from the name. REN gives a syntax error if it detects / in the middle of a target name.






    Possible RENAME bug - a single command may rename the same file twice!



    Starting in an empty test folder:



    C:test>copy nul 123456789.123
    1 file(s) copied.

    C:test>dir /x
    Volume in drive C is OS
    Volume Serial Number is EE2C-5A11

    Directory of C:test

    09/15/2012 07:42 PM <DIR> .
    09/15/2012 07:42 PM <DIR> ..
    09/15/2012 07:42 PM 0 123456~1.123 123456789.123
    1 File(s) 0 bytes
    2 Dir(s) 327,237,562,368 bytes free

    C:test>ren *1* 2*3.?x

    C:test>dir /x
    Volume in drive C is OS
    Volume Serial Number is EE2C-5A11

    Directory of C:test

    09/15/2012 07:42 PM <DIR> .
    09/15/2012 07:42 PM <DIR> ..
    09/15/2012 07:42 PM 0 223456~1.XX 223456789.123.xx
    1 File(s) 0 bytes
    2 Dir(s) 327,237,562,368 bytes free

    REM Expected result = 223456789.123.x


    I believe the sourceMask *1* first matches the long file name, and the file is renamed to the expected result of 223456789.123.x. RENAME then continues to look for more files to process and finds the newly named file via the new short name of 223456~1.X. The file is then renamed again giving the final result of 223456789.123.xx.



    If I disable 8.3 name generation then the RENAME gives the expected result.



    I haven't fully worked out all of the trigger conditions that must exist to induce this odd behavior. I was concerned that it might be possible to create a never ending recursive RENAME, but I was never able to induce one.



    I believe all of the following must be true to induce the bug. Every bugged case I saw had the following conditions, but not all cases that met the following conditions were bugged.




    • Short 8.3 names must be enabled

    • The sourceMask must match the original long name.

    • The initial rename must generate a short name that also matches the sourceMask

    • The initial renamed short name must sort later than the original short name (if it existed?)







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Dec 28 '18 at 15:14

























    answered Sep 16 '12 at 14:00









    dbenhamdbenham

    7,80142029




    7,80142029








    • 6





      What a thorough answer.. +1.

      – meder omuraliev
      Oct 5 '12 at 19:48











    • Tremendously elaborate!

      – Andriy M
      Mar 6 '13 at 5:39






    • 13





      Based on this, Microsoft should just add "For usage, see superuser.com/a/475875 " in REN /?.

      – efotinis
      Jun 14 '13 at 21:27








    • 4





      @CAD - This answer is 100% original content that Simon included on his site upon my request. Look at the bottom of that SS64 page and you will see that Simon gives me credit for the work.

      – dbenham
      Mar 14 '17 at 3:46






    • 2





      @JacksOnF1re - New information/technique added to my answer. You can actually delete your Copy of prefix using an obscure forward slash technique: ren "Copy of *.txt" "////////*"

      – dbenham
      Jun 3 '17 at 19:33














    • 6





      What a thorough answer.. +1.

      – meder omuraliev
      Oct 5 '12 at 19:48











    • Tremendously elaborate!

      – Andriy M
      Mar 6 '13 at 5:39






    • 13





      Based on this, Microsoft should just add "For usage, see superuser.com/a/475875 " in REN /?.

      – efotinis
      Jun 14 '13 at 21:27








    • 4





      @CAD - This answer is 100% original content that Simon included on his site upon my request. Look at the bottom of that SS64 page and you will see that Simon gives me credit for the work.

      – dbenham
      Mar 14 '17 at 3:46






    • 2





      @JacksOnF1re - New information/technique added to my answer. You can actually delete your Copy of prefix using an obscure forward slash technique: ren "Copy of *.txt" "////////*"

      – dbenham
      Jun 3 '17 at 19:33








    6




    6





    What a thorough answer.. +1.

    – meder omuraliev
    Oct 5 '12 at 19:48





    What a thorough answer.. +1.

    – meder omuraliev
    Oct 5 '12 at 19:48













    Tremendously elaborate!

    – Andriy M
    Mar 6 '13 at 5:39





    Tremendously elaborate!

    – Andriy M
    Mar 6 '13 at 5:39




    13




    13





    Based on this, Microsoft should just add "For usage, see superuser.com/a/475875 " in REN /?.

    – efotinis
    Jun 14 '13 at 21:27







    Based on this, Microsoft should just add "For usage, see superuser.com/a/475875 " in REN /?.

    – efotinis
    Jun 14 '13 at 21:27






    4




    4





    @CAD - This answer is 100% original content that Simon included on his site upon my request. Look at the bottom of that SS64 page and you will see that Simon gives me credit for the work.

    – dbenham
    Mar 14 '17 at 3:46





    @CAD - This answer is 100% original content that Simon included on his site upon my request. Look at the bottom of that SS64 page and you will see that Simon gives me credit for the work.

    – dbenham
    Mar 14 '17 at 3:46




    2




    2





    @JacksOnF1re - New information/technique added to my answer. You can actually delete your Copy of prefix using an obscure forward slash technique: ren "Copy of *.txt" "////////*"

    – dbenham
    Jun 3 '17 at 19:33





    @JacksOnF1re - New information/technique added to my answer. You can actually delete your Copy of prefix using an obscure forward slash technique: ren "Copy of *.txt" "////////*"

    – dbenham
    Jun 3 '17 at 19:33













    3














    Similar to exebook, here's a C# implementation to get the target filename from a sourcefile.



    I found 1 small error in dbenham's examples:



     ren  *_*  *_NEW.*
    abc_newt_1.dat -> abc_newt_NEW.txt (should be: abd_newt_NEW.dat)


    Here's the code:



        /// <summary>
    /// Returns a filename based on the sourcefile and the targetMask, as used in the second argument in rename/copy operations.
    /// targetMask may contain wildcards (* and ?).
    ///
    /// This follows the rules of: http://superuser.com/questions/475874/how-does-the-windows-rename-command-interpret-wildcards
    /// </summary>
    /// <param name="sourcefile">filename to change to target without wildcards</param>
    /// <param name="targetMask">mask with wildcards</param>
    /// <returns>a valid target filename given sourcefile and targetMask</returns>
    public static string GetTargetFileName(string sourcefile, string targetMask)
    {
    if (string.IsNullOrEmpty(sourcefile))
    throw new ArgumentNullException("sourcefile");

    if (string.IsNullOrEmpty(targetMask))
    throw new ArgumentNullException("targetMask");

    if (sourcefile.Contains('*') || sourcefile.Contains('?'))
    throw new ArgumentException("sourcefile cannot contain wildcards");

    // no wildcards: return complete mask as file
    if (!targetMask.Contains('*') && !targetMask.Contains('?'))
    return targetMask;

    var maskReader = new StringReader(targetMask);
    var sourceReader = new StringReader(sourcefile);
    var targetBuilder = new StringBuilder();


    while (maskReader.Peek() != -1)
    {

    int current = maskReader.Read();
    int sourcePeek = sourceReader.Peek();
    switch (current)
    {
    case '*':
    int next = maskReader.Read();
    switch (next)
    {
    case -1:
    case '?':
    // Append all remaining characters from sourcefile
    targetBuilder.Append(sourceReader.ReadToEnd());
    break;
    default:
    // Read source until the last occurrance of 'next'.
    // We cannot seek in the StringReader, so we will create a new StringReader if needed
    string sourceTail = sourceReader.ReadToEnd();
    int lastIndexOf = sourceTail.LastIndexOf((char) next);
    // If not found, append everything and the 'next' char
    if (lastIndexOf == -1)
    {
    targetBuilder.Append(sourceTail);
    targetBuilder.Append((char) next);

    }
    else
    {
    string toAppend = sourceTail.Substring(0, lastIndexOf + 1);
    string rest = sourceTail.Substring(lastIndexOf + 1);
    sourceReader.Dispose();
    // go on with the rest...
    sourceReader = new StringReader(rest);
    targetBuilder.Append(toAppend);
    }
    break;
    }

    break;
    case '?':
    if (sourcePeek != -1 && sourcePeek != '.')
    {
    targetBuilder.Append((char)sourceReader.Read());
    }
    break;
    case '.':
    // eat all characters until the dot is found
    while (sourcePeek != -1 && sourcePeek != '.')
    {
    sourceReader.Read();
    sourcePeek = sourceReader.Peek();
    }

    targetBuilder.Append('.');
    // need to eat the . when we peeked it
    if (sourcePeek == '.')
    sourceReader.Read();

    break;
    default:
    if (sourcePeek != '.') sourceReader.Read(); // also consume the source's char if not .
    targetBuilder.Append((char)current);
    break;
    }

    }

    sourceReader.Dispose();
    maskReader.Dispose();
    return targetBuilder.ToString().TrimEnd('.', ' ');
    }


    And here's an NUnit test method to test the examples:



        [Test]
    public void TestGetTargetFileName()
    {
    string targetMask = "?????.?????";
    Assert.AreEqual("a", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b", targetMask));
    Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b.c", targetMask));
    Assert.AreEqual("part1.part2", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
    Assert.AreEqual("12345.12345", FileUtil.GetTargetFileName("123456.123456.123456", targetMask));

    targetMask = "A?Z*";
    Assert.AreEqual("AZ", FileUtil.GetTargetFileName("1", targetMask));
    Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("12", targetMask));
    Assert.AreEqual("AZ.txt", FileUtil.GetTargetFileName("1.txt", targetMask));
    Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("12.txt", targetMask));
    Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("123", targetMask));
    Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("123.txt", targetMask));
    Assert.AreEqual("A2Z4", FileUtil.GetTargetFileName("1234", targetMask));
    Assert.AreEqual("A2Z4.txt", FileUtil.GetTargetFileName("1234.txt", targetMask));

    targetMask = "*.txt";
    Assert.AreEqual("a.txt", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("b.txt", FileUtil.GetTargetFileName("b.dat", targetMask));
    Assert.AreEqual("c.x.txt", FileUtil.GetTargetFileName("c.x.y", targetMask));

    targetMask = "*?.bak";
    Assert.AreEqual("a.bak", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("b.dat.bak", FileUtil.GetTargetFileName("b.dat", targetMask));
    Assert.AreEqual("c.x.y.bak", FileUtil.GetTargetFileName("c.x.y", targetMask));

    targetMask = "*_NEW.*";
    Assert.AreEqual("abcd_NEW.txt", FileUtil.GetTargetFileName("abcd_12345.txt", targetMask));
    Assert.AreEqual("abc_newt_NEW.dat", FileUtil.GetTargetFileName("abc_newt_1.dat", targetMask));
    Assert.AreEqual("abcd_123.a_NEW", FileUtil.GetTargetFileName("abcd_123.a_b", targetMask));

    targetMask = "?x.????999.*rForTheCourse";

    Assert.AreEqual("px.part999.rForTheCourse", FileUtil.GetTargetFileName("part1.part2", targetMask));
    Assert.AreEqual("px.part999.parForTheCourse", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
    Assert.AreEqual("ax.b999.crForTheCourse", FileUtil.GetTargetFileName("a.b.c", targetMask));
    Assert.AreEqual("ax.b999.CarParForTheCourse", FileUtil.GetTargetFileName("a.b.CarPart3BEER", targetMask));

    }





    share|improve this answer
























    • Thanks for the head's up about the mistake in my example. I've edited my answer to fix it.

      – dbenham
      Dec 16 '14 at 12:16
















    3














    Similar to exebook, here's a C# implementation to get the target filename from a sourcefile.



    I found 1 small error in dbenham's examples:



     ren  *_*  *_NEW.*
    abc_newt_1.dat -> abc_newt_NEW.txt (should be: abd_newt_NEW.dat)


    Here's the code:



        /// <summary>
    /// Returns a filename based on the sourcefile and the targetMask, as used in the second argument in rename/copy operations.
    /// targetMask may contain wildcards (* and ?).
    ///
    /// This follows the rules of: http://superuser.com/questions/475874/how-does-the-windows-rename-command-interpret-wildcards
    /// </summary>
    /// <param name="sourcefile">filename to change to target without wildcards</param>
    /// <param name="targetMask">mask with wildcards</param>
    /// <returns>a valid target filename given sourcefile and targetMask</returns>
    public static string GetTargetFileName(string sourcefile, string targetMask)
    {
    if (string.IsNullOrEmpty(sourcefile))
    throw new ArgumentNullException("sourcefile");

    if (string.IsNullOrEmpty(targetMask))
    throw new ArgumentNullException("targetMask");

    if (sourcefile.Contains('*') || sourcefile.Contains('?'))
    throw new ArgumentException("sourcefile cannot contain wildcards");

    // no wildcards: return complete mask as file
    if (!targetMask.Contains('*') && !targetMask.Contains('?'))
    return targetMask;

    var maskReader = new StringReader(targetMask);
    var sourceReader = new StringReader(sourcefile);
    var targetBuilder = new StringBuilder();


    while (maskReader.Peek() != -1)
    {

    int current = maskReader.Read();
    int sourcePeek = sourceReader.Peek();
    switch (current)
    {
    case '*':
    int next = maskReader.Read();
    switch (next)
    {
    case -1:
    case '?':
    // Append all remaining characters from sourcefile
    targetBuilder.Append(sourceReader.ReadToEnd());
    break;
    default:
    // Read source until the last occurrance of 'next'.
    // We cannot seek in the StringReader, so we will create a new StringReader if needed
    string sourceTail = sourceReader.ReadToEnd();
    int lastIndexOf = sourceTail.LastIndexOf((char) next);
    // If not found, append everything and the 'next' char
    if (lastIndexOf == -1)
    {
    targetBuilder.Append(sourceTail);
    targetBuilder.Append((char) next);

    }
    else
    {
    string toAppend = sourceTail.Substring(0, lastIndexOf + 1);
    string rest = sourceTail.Substring(lastIndexOf + 1);
    sourceReader.Dispose();
    // go on with the rest...
    sourceReader = new StringReader(rest);
    targetBuilder.Append(toAppend);
    }
    break;
    }

    break;
    case '?':
    if (sourcePeek != -1 && sourcePeek != '.')
    {
    targetBuilder.Append((char)sourceReader.Read());
    }
    break;
    case '.':
    // eat all characters until the dot is found
    while (sourcePeek != -1 && sourcePeek != '.')
    {
    sourceReader.Read();
    sourcePeek = sourceReader.Peek();
    }

    targetBuilder.Append('.');
    // need to eat the . when we peeked it
    if (sourcePeek == '.')
    sourceReader.Read();

    break;
    default:
    if (sourcePeek != '.') sourceReader.Read(); // also consume the source's char if not .
    targetBuilder.Append((char)current);
    break;
    }

    }

    sourceReader.Dispose();
    maskReader.Dispose();
    return targetBuilder.ToString().TrimEnd('.', ' ');
    }


    And here's an NUnit test method to test the examples:



        [Test]
    public void TestGetTargetFileName()
    {
    string targetMask = "?????.?????";
    Assert.AreEqual("a", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b", targetMask));
    Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b.c", targetMask));
    Assert.AreEqual("part1.part2", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
    Assert.AreEqual("12345.12345", FileUtil.GetTargetFileName("123456.123456.123456", targetMask));

    targetMask = "A?Z*";
    Assert.AreEqual("AZ", FileUtil.GetTargetFileName("1", targetMask));
    Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("12", targetMask));
    Assert.AreEqual("AZ.txt", FileUtil.GetTargetFileName("1.txt", targetMask));
    Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("12.txt", targetMask));
    Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("123", targetMask));
    Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("123.txt", targetMask));
    Assert.AreEqual("A2Z4", FileUtil.GetTargetFileName("1234", targetMask));
    Assert.AreEqual("A2Z4.txt", FileUtil.GetTargetFileName("1234.txt", targetMask));

    targetMask = "*.txt";
    Assert.AreEqual("a.txt", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("b.txt", FileUtil.GetTargetFileName("b.dat", targetMask));
    Assert.AreEqual("c.x.txt", FileUtil.GetTargetFileName("c.x.y", targetMask));

    targetMask = "*?.bak";
    Assert.AreEqual("a.bak", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("b.dat.bak", FileUtil.GetTargetFileName("b.dat", targetMask));
    Assert.AreEqual("c.x.y.bak", FileUtil.GetTargetFileName("c.x.y", targetMask));

    targetMask = "*_NEW.*";
    Assert.AreEqual("abcd_NEW.txt", FileUtil.GetTargetFileName("abcd_12345.txt", targetMask));
    Assert.AreEqual("abc_newt_NEW.dat", FileUtil.GetTargetFileName("abc_newt_1.dat", targetMask));
    Assert.AreEqual("abcd_123.a_NEW", FileUtil.GetTargetFileName("abcd_123.a_b", targetMask));

    targetMask = "?x.????999.*rForTheCourse";

    Assert.AreEqual("px.part999.rForTheCourse", FileUtil.GetTargetFileName("part1.part2", targetMask));
    Assert.AreEqual("px.part999.parForTheCourse", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
    Assert.AreEqual("ax.b999.crForTheCourse", FileUtil.GetTargetFileName("a.b.c", targetMask));
    Assert.AreEqual("ax.b999.CarParForTheCourse", FileUtil.GetTargetFileName("a.b.CarPart3BEER", targetMask));

    }





    share|improve this answer
























    • Thanks for the head's up about the mistake in my example. I've edited my answer to fix it.

      – dbenham
      Dec 16 '14 at 12:16














    3












    3








    3







    Similar to exebook, here's a C# implementation to get the target filename from a sourcefile.



    I found 1 small error in dbenham's examples:



     ren  *_*  *_NEW.*
    abc_newt_1.dat -> abc_newt_NEW.txt (should be: abd_newt_NEW.dat)


    Here's the code:



        /// <summary>
    /// Returns a filename based on the sourcefile and the targetMask, as used in the second argument in rename/copy operations.
    /// targetMask may contain wildcards (* and ?).
    ///
    /// This follows the rules of: http://superuser.com/questions/475874/how-does-the-windows-rename-command-interpret-wildcards
    /// </summary>
    /// <param name="sourcefile">filename to change to target without wildcards</param>
    /// <param name="targetMask">mask with wildcards</param>
    /// <returns>a valid target filename given sourcefile and targetMask</returns>
    public static string GetTargetFileName(string sourcefile, string targetMask)
    {
    if (string.IsNullOrEmpty(sourcefile))
    throw new ArgumentNullException("sourcefile");

    if (string.IsNullOrEmpty(targetMask))
    throw new ArgumentNullException("targetMask");

    if (sourcefile.Contains('*') || sourcefile.Contains('?'))
    throw new ArgumentException("sourcefile cannot contain wildcards");

    // no wildcards: return complete mask as file
    if (!targetMask.Contains('*') && !targetMask.Contains('?'))
    return targetMask;

    var maskReader = new StringReader(targetMask);
    var sourceReader = new StringReader(sourcefile);
    var targetBuilder = new StringBuilder();


    while (maskReader.Peek() != -1)
    {

    int current = maskReader.Read();
    int sourcePeek = sourceReader.Peek();
    switch (current)
    {
    case '*':
    int next = maskReader.Read();
    switch (next)
    {
    case -1:
    case '?':
    // Append all remaining characters from sourcefile
    targetBuilder.Append(sourceReader.ReadToEnd());
    break;
    default:
    // Read source until the last occurrance of 'next'.
    // We cannot seek in the StringReader, so we will create a new StringReader if needed
    string sourceTail = sourceReader.ReadToEnd();
    int lastIndexOf = sourceTail.LastIndexOf((char) next);
    // If not found, append everything and the 'next' char
    if (lastIndexOf == -1)
    {
    targetBuilder.Append(sourceTail);
    targetBuilder.Append((char) next);

    }
    else
    {
    string toAppend = sourceTail.Substring(0, lastIndexOf + 1);
    string rest = sourceTail.Substring(lastIndexOf + 1);
    sourceReader.Dispose();
    // go on with the rest...
    sourceReader = new StringReader(rest);
    targetBuilder.Append(toAppend);
    }
    break;
    }

    break;
    case '?':
    if (sourcePeek != -1 && sourcePeek != '.')
    {
    targetBuilder.Append((char)sourceReader.Read());
    }
    break;
    case '.':
    // eat all characters until the dot is found
    while (sourcePeek != -1 && sourcePeek != '.')
    {
    sourceReader.Read();
    sourcePeek = sourceReader.Peek();
    }

    targetBuilder.Append('.');
    // need to eat the . when we peeked it
    if (sourcePeek == '.')
    sourceReader.Read();

    break;
    default:
    if (sourcePeek != '.') sourceReader.Read(); // also consume the source's char if not .
    targetBuilder.Append((char)current);
    break;
    }

    }

    sourceReader.Dispose();
    maskReader.Dispose();
    return targetBuilder.ToString().TrimEnd('.', ' ');
    }


    And here's an NUnit test method to test the examples:



        [Test]
    public void TestGetTargetFileName()
    {
    string targetMask = "?????.?????";
    Assert.AreEqual("a", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b", targetMask));
    Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b.c", targetMask));
    Assert.AreEqual("part1.part2", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
    Assert.AreEqual("12345.12345", FileUtil.GetTargetFileName("123456.123456.123456", targetMask));

    targetMask = "A?Z*";
    Assert.AreEqual("AZ", FileUtil.GetTargetFileName("1", targetMask));
    Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("12", targetMask));
    Assert.AreEqual("AZ.txt", FileUtil.GetTargetFileName("1.txt", targetMask));
    Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("12.txt", targetMask));
    Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("123", targetMask));
    Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("123.txt", targetMask));
    Assert.AreEqual("A2Z4", FileUtil.GetTargetFileName("1234", targetMask));
    Assert.AreEqual("A2Z4.txt", FileUtil.GetTargetFileName("1234.txt", targetMask));

    targetMask = "*.txt";
    Assert.AreEqual("a.txt", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("b.txt", FileUtil.GetTargetFileName("b.dat", targetMask));
    Assert.AreEqual("c.x.txt", FileUtil.GetTargetFileName("c.x.y", targetMask));

    targetMask = "*?.bak";
    Assert.AreEqual("a.bak", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("b.dat.bak", FileUtil.GetTargetFileName("b.dat", targetMask));
    Assert.AreEqual("c.x.y.bak", FileUtil.GetTargetFileName("c.x.y", targetMask));

    targetMask = "*_NEW.*";
    Assert.AreEqual("abcd_NEW.txt", FileUtil.GetTargetFileName("abcd_12345.txt", targetMask));
    Assert.AreEqual("abc_newt_NEW.dat", FileUtil.GetTargetFileName("abc_newt_1.dat", targetMask));
    Assert.AreEqual("abcd_123.a_NEW", FileUtil.GetTargetFileName("abcd_123.a_b", targetMask));

    targetMask = "?x.????999.*rForTheCourse";

    Assert.AreEqual("px.part999.rForTheCourse", FileUtil.GetTargetFileName("part1.part2", targetMask));
    Assert.AreEqual("px.part999.parForTheCourse", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
    Assert.AreEqual("ax.b999.crForTheCourse", FileUtil.GetTargetFileName("a.b.c", targetMask));
    Assert.AreEqual("ax.b999.CarParForTheCourse", FileUtil.GetTargetFileName("a.b.CarPart3BEER", targetMask));

    }





    share|improve this answer













    Similar to exebook, here's a C# implementation to get the target filename from a sourcefile.



    I found 1 small error in dbenham's examples:



     ren  *_*  *_NEW.*
    abc_newt_1.dat -> abc_newt_NEW.txt (should be: abd_newt_NEW.dat)


    Here's the code:



        /// <summary>
    /// Returns a filename based on the sourcefile and the targetMask, as used in the second argument in rename/copy operations.
    /// targetMask may contain wildcards (* and ?).
    ///
    /// This follows the rules of: http://superuser.com/questions/475874/how-does-the-windows-rename-command-interpret-wildcards
    /// </summary>
    /// <param name="sourcefile">filename to change to target without wildcards</param>
    /// <param name="targetMask">mask with wildcards</param>
    /// <returns>a valid target filename given sourcefile and targetMask</returns>
    public static string GetTargetFileName(string sourcefile, string targetMask)
    {
    if (string.IsNullOrEmpty(sourcefile))
    throw new ArgumentNullException("sourcefile");

    if (string.IsNullOrEmpty(targetMask))
    throw new ArgumentNullException("targetMask");

    if (sourcefile.Contains('*') || sourcefile.Contains('?'))
    throw new ArgumentException("sourcefile cannot contain wildcards");

    // no wildcards: return complete mask as file
    if (!targetMask.Contains('*') && !targetMask.Contains('?'))
    return targetMask;

    var maskReader = new StringReader(targetMask);
    var sourceReader = new StringReader(sourcefile);
    var targetBuilder = new StringBuilder();


    while (maskReader.Peek() != -1)
    {

    int current = maskReader.Read();
    int sourcePeek = sourceReader.Peek();
    switch (current)
    {
    case '*':
    int next = maskReader.Read();
    switch (next)
    {
    case -1:
    case '?':
    // Append all remaining characters from sourcefile
    targetBuilder.Append(sourceReader.ReadToEnd());
    break;
    default:
    // Read source until the last occurrance of 'next'.
    // We cannot seek in the StringReader, so we will create a new StringReader if needed
    string sourceTail = sourceReader.ReadToEnd();
    int lastIndexOf = sourceTail.LastIndexOf((char) next);
    // If not found, append everything and the 'next' char
    if (lastIndexOf == -1)
    {
    targetBuilder.Append(sourceTail);
    targetBuilder.Append((char) next);

    }
    else
    {
    string toAppend = sourceTail.Substring(0, lastIndexOf + 1);
    string rest = sourceTail.Substring(lastIndexOf + 1);
    sourceReader.Dispose();
    // go on with the rest...
    sourceReader = new StringReader(rest);
    targetBuilder.Append(toAppend);
    }
    break;
    }

    break;
    case '?':
    if (sourcePeek != -1 && sourcePeek != '.')
    {
    targetBuilder.Append((char)sourceReader.Read());
    }
    break;
    case '.':
    // eat all characters until the dot is found
    while (sourcePeek != -1 && sourcePeek != '.')
    {
    sourceReader.Read();
    sourcePeek = sourceReader.Peek();
    }

    targetBuilder.Append('.');
    // need to eat the . when we peeked it
    if (sourcePeek == '.')
    sourceReader.Read();

    break;
    default:
    if (sourcePeek != '.') sourceReader.Read(); // also consume the source's char if not .
    targetBuilder.Append((char)current);
    break;
    }

    }

    sourceReader.Dispose();
    maskReader.Dispose();
    return targetBuilder.ToString().TrimEnd('.', ' ');
    }


    And here's an NUnit test method to test the examples:



        [Test]
    public void TestGetTargetFileName()
    {
    string targetMask = "?????.?????";
    Assert.AreEqual("a", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b", targetMask));
    Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b.c", targetMask));
    Assert.AreEqual("part1.part2", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
    Assert.AreEqual("12345.12345", FileUtil.GetTargetFileName("123456.123456.123456", targetMask));

    targetMask = "A?Z*";
    Assert.AreEqual("AZ", FileUtil.GetTargetFileName("1", targetMask));
    Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("12", targetMask));
    Assert.AreEqual("AZ.txt", FileUtil.GetTargetFileName("1.txt", targetMask));
    Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("12.txt", targetMask));
    Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("123", targetMask));
    Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("123.txt", targetMask));
    Assert.AreEqual("A2Z4", FileUtil.GetTargetFileName("1234", targetMask));
    Assert.AreEqual("A2Z4.txt", FileUtil.GetTargetFileName("1234.txt", targetMask));

    targetMask = "*.txt";
    Assert.AreEqual("a.txt", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("b.txt", FileUtil.GetTargetFileName("b.dat", targetMask));
    Assert.AreEqual("c.x.txt", FileUtil.GetTargetFileName("c.x.y", targetMask));

    targetMask = "*?.bak";
    Assert.AreEqual("a.bak", FileUtil.GetTargetFileName("a", targetMask));
    Assert.AreEqual("b.dat.bak", FileUtil.GetTargetFileName("b.dat", targetMask));
    Assert.AreEqual("c.x.y.bak", FileUtil.GetTargetFileName("c.x.y", targetMask));

    targetMask = "*_NEW.*";
    Assert.AreEqual("abcd_NEW.txt", FileUtil.GetTargetFileName("abcd_12345.txt", targetMask));
    Assert.AreEqual("abc_newt_NEW.dat", FileUtil.GetTargetFileName("abc_newt_1.dat", targetMask));
    Assert.AreEqual("abcd_123.a_NEW", FileUtil.GetTargetFileName("abcd_123.a_b", targetMask));

    targetMask = "?x.????999.*rForTheCourse";

    Assert.AreEqual("px.part999.rForTheCourse", FileUtil.GetTargetFileName("part1.part2", targetMask));
    Assert.AreEqual("px.part999.parForTheCourse", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
    Assert.AreEqual("ax.b999.crForTheCourse", FileUtil.GetTargetFileName("a.b.c", targetMask));
    Assert.AreEqual("ax.b999.CarParForTheCourse", FileUtil.GetTargetFileName("a.b.CarPart3BEER", targetMask));

    }






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Dec 16 '14 at 10:13









    amrunningamrunning

    312




    312













    • Thanks for the head's up about the mistake in my example. I've edited my answer to fix it.

      – dbenham
      Dec 16 '14 at 12:16



















    • Thanks for the head's up about the mistake in my example. I've edited my answer to fix it.

      – dbenham
      Dec 16 '14 at 12:16

















    Thanks for the head's up about the mistake in my example. I've edited my answer to fix it.

    – dbenham
    Dec 16 '14 at 12:16





    Thanks for the head's up about the mistake in my example. I've edited my answer to fix it.

    – dbenham
    Dec 16 '14 at 12:16











    1














    Maybe someone can find this useful. This JavaScript code is based on the answer by dbenham above.



    I did not test sourceMask very much, but targetMask does match all examples given by dbenham.



    function maskMatch(path, mask) {
    mask = mask.replace(/./g, '\.')
    mask = mask.replace(/?/g, '.')
    mask = mask.replace(/*/g, '.+?')
    var r = new RegExp('^'+mask+'$', '')
    return path.match(r)
    }

    function maskNewName(path, mask) {
    if (path == '') return
    var x = 0, R = ''
    for (var m = 0; m < mask.length; m++) {
    var ch = mask[m], q = path[x], z = mask[m + 1]
    if (ch != '.' && ch != '*' && ch != '?') {
    if (q && q != '.') x++
    R += ch
    } else if (ch == '?') {
    if (q && q != '.') R += q, x++
    } else if (ch == '*' && m == mask.length - 1) {
    while (x < path.length) R += path[x++]
    } else if (ch == '*') {
    if (z == '.') {
    for (var i = path.length - 1; i >= 0; i--) if (path[i] == '.') break
    if (i < 0) {
    R += path.substr(x, path.length) + '.'
    i = path.length
    } else R += path.substr(x, i - x + 1)
    x = i + 1, m++
    } else if (z == '?') {
    R += path.substr(x, path.length), m++, x = path.length
    } else {
    for (var i = path.length - 1; i >= 0; i--) if (path[i] == z) break
    if (i < 0) R += path.substr(x, path.length) + z, x = path.length, m++
    else R += path.substr(x, i - x), x = i + 1
    }
    } else if (ch == '.') {
    while (x < path.length) if (path[x++] == '.') break
    R += '.'
    }
    }
    while (R[R.length - 1] == '.') R = R.substr(0, R.length - 1)
    }





    share|improve this answer






























      1














      Maybe someone can find this useful. This JavaScript code is based on the answer by dbenham above.



      I did not test sourceMask very much, but targetMask does match all examples given by dbenham.



      function maskMatch(path, mask) {
      mask = mask.replace(/./g, '\.')
      mask = mask.replace(/?/g, '.')
      mask = mask.replace(/*/g, '.+?')
      var r = new RegExp('^'+mask+'$', '')
      return path.match(r)
      }

      function maskNewName(path, mask) {
      if (path == '') return
      var x = 0, R = ''
      for (var m = 0; m < mask.length; m++) {
      var ch = mask[m], q = path[x], z = mask[m + 1]
      if (ch != '.' && ch != '*' && ch != '?') {
      if (q && q != '.') x++
      R += ch
      } else if (ch == '?') {
      if (q && q != '.') R += q, x++
      } else if (ch == '*' && m == mask.length - 1) {
      while (x < path.length) R += path[x++]
      } else if (ch == '*') {
      if (z == '.') {
      for (var i = path.length - 1; i >= 0; i--) if (path[i] == '.') break
      if (i < 0) {
      R += path.substr(x, path.length) + '.'
      i = path.length
      } else R += path.substr(x, i - x + 1)
      x = i + 1, m++
      } else if (z == '?') {
      R += path.substr(x, path.length), m++, x = path.length
      } else {
      for (var i = path.length - 1; i >= 0; i--) if (path[i] == z) break
      if (i < 0) R += path.substr(x, path.length) + z, x = path.length, m++
      else R += path.substr(x, i - x), x = i + 1
      }
      } else if (ch == '.') {
      while (x < path.length) if (path[x++] == '.') break
      R += '.'
      }
      }
      while (R[R.length - 1] == '.') R = R.substr(0, R.length - 1)
      }





      share|improve this answer




























        1












        1








        1







        Maybe someone can find this useful. This JavaScript code is based on the answer by dbenham above.



        I did not test sourceMask very much, but targetMask does match all examples given by dbenham.



        function maskMatch(path, mask) {
        mask = mask.replace(/./g, '\.')
        mask = mask.replace(/?/g, '.')
        mask = mask.replace(/*/g, '.+?')
        var r = new RegExp('^'+mask+'$', '')
        return path.match(r)
        }

        function maskNewName(path, mask) {
        if (path == '') return
        var x = 0, R = ''
        for (var m = 0; m < mask.length; m++) {
        var ch = mask[m], q = path[x], z = mask[m + 1]
        if (ch != '.' && ch != '*' && ch != '?') {
        if (q && q != '.') x++
        R += ch
        } else if (ch == '?') {
        if (q && q != '.') R += q, x++
        } else if (ch == '*' && m == mask.length - 1) {
        while (x < path.length) R += path[x++]
        } else if (ch == '*') {
        if (z == '.') {
        for (var i = path.length - 1; i >= 0; i--) if (path[i] == '.') break
        if (i < 0) {
        R += path.substr(x, path.length) + '.'
        i = path.length
        } else R += path.substr(x, i - x + 1)
        x = i + 1, m++
        } else if (z == '?') {
        R += path.substr(x, path.length), m++, x = path.length
        } else {
        for (var i = path.length - 1; i >= 0; i--) if (path[i] == z) break
        if (i < 0) R += path.substr(x, path.length) + z, x = path.length, m++
        else R += path.substr(x, i - x), x = i + 1
        }
        } else if (ch == '.') {
        while (x < path.length) if (path[x++] == '.') break
        R += '.'
        }
        }
        while (R[R.length - 1] == '.') R = R.substr(0, R.length - 1)
        }





        share|improve this answer















        Maybe someone can find this useful. This JavaScript code is based on the answer by dbenham above.



        I did not test sourceMask very much, but targetMask does match all examples given by dbenham.



        function maskMatch(path, mask) {
        mask = mask.replace(/./g, '\.')
        mask = mask.replace(/?/g, '.')
        mask = mask.replace(/*/g, '.+?')
        var r = new RegExp('^'+mask+'$', '')
        return path.match(r)
        }

        function maskNewName(path, mask) {
        if (path == '') return
        var x = 0, R = ''
        for (var m = 0; m < mask.length; m++) {
        var ch = mask[m], q = path[x], z = mask[m + 1]
        if (ch != '.' && ch != '*' && ch != '?') {
        if (q && q != '.') x++
        R += ch
        } else if (ch == '?') {
        if (q && q != '.') R += q, x++
        } else if (ch == '*' && m == mask.length - 1) {
        while (x < path.length) R += path[x++]
        } else if (ch == '*') {
        if (z == '.') {
        for (var i = path.length - 1; i >= 0; i--) if (path[i] == '.') break
        if (i < 0) {
        R += path.substr(x, path.length) + '.'
        i = path.length
        } else R += path.substr(x, i - x + 1)
        x = i + 1, m++
        } else if (z == '?') {
        R += path.substr(x, path.length), m++, x = path.length
        } else {
        for (var i = path.length - 1; i >= 0; i--) if (path[i] == z) break
        if (i < 0) R += path.substr(x, path.length) + z, x = path.length, m++
        else R += path.substr(x, i - x), x = i + 1
        }
        } else if (ch == '.') {
        while (x < path.length) if (path[x++] == '.') break
        R += '.'
        }
        }
        while (R[R.length - 1] == '.') R = R.substr(0, R.length - 1)
        }






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jun 7 '18 at 9:50









        phuclv

        9,06463889




        9,06463889










        answered Apr 9 '14 at 17:07









        exebookexebook

        1741210




        1741210























            0














            I have managed to write this code in BASIC to mask wildcard filenames:



            REM inputs a filename and matches wildcards returning masked output filename.
            FUNCTION maskNewName$ (path$, mask$)
            IF path$ = "" THEN EXIT FUNCTION
            IF INSTR(path$, "?") OR INSTR(path$, "*") THEN EXIT FUNCTION
            x = 0
            R$ = ""
            FOR m = 0 TO LEN(mask$) - 1
            ch$ = MID$(mask$, m + 1, 1)
            q$ = MID$(path$, x + 1, 1)
            z$ = MID$(mask$, m + 2, 1)
            IF ch$ <> "." AND ch$ <> "*" AND ch$ <> "?" THEN
            IF LEN(q$) AND q$ <> "." THEN x = x + 1
            R$ = R$ + ch$
            ELSE
            IF ch$ = "?" THEN
            IF LEN(q$) AND q$ <> "." THEN R$ = R$ + q$: x = x + 1
            ELSE
            IF ch$ = "*" AND m = LEN(mask$) - 1 THEN
            WHILE x < LEN(path$)
            R$ = R$ + MID$(path$, x + 1, 1)
            x = x + 1
            WEND
            ELSE
            IF ch$ = "*" THEN
            IF z$ = "." THEN
            FOR i = LEN(path$) - 1 TO 0 STEP -1
            IF MID$(path$, i + 1, 1) = "." THEN EXIT FOR
            NEXT
            IF i < 0 THEN
            R$ = R$ + MID$(path$, x + 1) + "."
            i = LEN(path$)
            ELSE
            R$ = R$ + MID$(path$, x + 1, i - x + 1)
            END IF
            x = i + 1
            m = m + 1
            ELSE
            IF z$ = "?" THEN
            R$ = R$ + MID$(path$, x + 1, LEN(path$))
            m = m + 1
            x = LEN(path$)
            ELSE
            FOR i = LEN(path$) - 1 TO 0 STEP -1
            'IF MID$(path$, i + 1, 1) = z$ THEN EXIT FOR
            IF UCASE$(MID$(path$, i + 1, 1)) = UCASE$(z$) THEN EXIT FOR
            NEXT
            IF i < 0 THEN
            R$ = R$ + MID$(path$, x + 1, LEN(path$)) + z$
            x = LEN(path$)
            m = m + 1
            ELSE
            R$ = R$ + MID$(path$, x + 1, i - x)
            x = i + 1
            END IF
            END IF
            END IF
            ELSE
            IF ch$ = "." THEN
            DO WHILE x < LEN(path$)
            IF MID$(path$, x + 1, 1) = "." THEN
            x = x + 1
            EXIT DO
            END IF
            x = x + 1
            LOOP
            R$ = R$ + "."
            END IF
            END IF
            END IF
            END IF
            END IF
            NEXT
            DO WHILE RIGHT$(R$, 1) = "."
            R$ = LEFT$(R$, LEN(R$) - 1)
            LOOP
            R$ = RTRIM$(R$)
            maskNewName$ = R$
            END FUNCTION





            share|improve this answer





















            • 2





              Can you clarify how this answers what was asked in the question?

              – fixer1234
              Oct 13 '16 at 3:58











            • It replicates the function REN uses for wildcard matching such as processing REN *.TMP *.DOC depending on how the function is called before renaming the filenames.

              – eoredson
              Oct 13 '16 at 21:08
















            0














            I have managed to write this code in BASIC to mask wildcard filenames:



            REM inputs a filename and matches wildcards returning masked output filename.
            FUNCTION maskNewName$ (path$, mask$)
            IF path$ = "" THEN EXIT FUNCTION
            IF INSTR(path$, "?") OR INSTR(path$, "*") THEN EXIT FUNCTION
            x = 0
            R$ = ""
            FOR m = 0 TO LEN(mask$) - 1
            ch$ = MID$(mask$, m + 1, 1)
            q$ = MID$(path$, x + 1, 1)
            z$ = MID$(mask$, m + 2, 1)
            IF ch$ <> "." AND ch$ <> "*" AND ch$ <> "?" THEN
            IF LEN(q$) AND q$ <> "." THEN x = x + 1
            R$ = R$ + ch$
            ELSE
            IF ch$ = "?" THEN
            IF LEN(q$) AND q$ <> "." THEN R$ = R$ + q$: x = x + 1
            ELSE
            IF ch$ = "*" AND m = LEN(mask$) - 1 THEN
            WHILE x < LEN(path$)
            R$ = R$ + MID$(path$, x + 1, 1)
            x = x + 1
            WEND
            ELSE
            IF ch$ = "*" THEN
            IF z$ = "." THEN
            FOR i = LEN(path$) - 1 TO 0 STEP -1
            IF MID$(path$, i + 1, 1) = "." THEN EXIT FOR
            NEXT
            IF i < 0 THEN
            R$ = R$ + MID$(path$, x + 1) + "."
            i = LEN(path$)
            ELSE
            R$ = R$ + MID$(path$, x + 1, i - x + 1)
            END IF
            x = i + 1
            m = m + 1
            ELSE
            IF z$ = "?" THEN
            R$ = R$ + MID$(path$, x + 1, LEN(path$))
            m = m + 1
            x = LEN(path$)
            ELSE
            FOR i = LEN(path$) - 1 TO 0 STEP -1
            'IF MID$(path$, i + 1, 1) = z$ THEN EXIT FOR
            IF UCASE$(MID$(path$, i + 1, 1)) = UCASE$(z$) THEN EXIT FOR
            NEXT
            IF i < 0 THEN
            R$ = R$ + MID$(path$, x + 1, LEN(path$)) + z$
            x = LEN(path$)
            m = m + 1
            ELSE
            R$ = R$ + MID$(path$, x + 1, i - x)
            x = i + 1
            END IF
            END IF
            END IF
            ELSE
            IF ch$ = "." THEN
            DO WHILE x < LEN(path$)
            IF MID$(path$, x + 1, 1) = "." THEN
            x = x + 1
            EXIT DO
            END IF
            x = x + 1
            LOOP
            R$ = R$ + "."
            END IF
            END IF
            END IF
            END IF
            END IF
            NEXT
            DO WHILE RIGHT$(R$, 1) = "."
            R$ = LEFT$(R$, LEN(R$) - 1)
            LOOP
            R$ = RTRIM$(R$)
            maskNewName$ = R$
            END FUNCTION





            share|improve this answer





















            • 2





              Can you clarify how this answers what was asked in the question?

              – fixer1234
              Oct 13 '16 at 3:58











            • It replicates the function REN uses for wildcard matching such as processing REN *.TMP *.DOC depending on how the function is called before renaming the filenames.

              – eoredson
              Oct 13 '16 at 21:08














            0












            0








            0







            I have managed to write this code in BASIC to mask wildcard filenames:



            REM inputs a filename and matches wildcards returning masked output filename.
            FUNCTION maskNewName$ (path$, mask$)
            IF path$ = "" THEN EXIT FUNCTION
            IF INSTR(path$, "?") OR INSTR(path$, "*") THEN EXIT FUNCTION
            x = 0
            R$ = ""
            FOR m = 0 TO LEN(mask$) - 1
            ch$ = MID$(mask$, m + 1, 1)
            q$ = MID$(path$, x + 1, 1)
            z$ = MID$(mask$, m + 2, 1)
            IF ch$ <> "." AND ch$ <> "*" AND ch$ <> "?" THEN
            IF LEN(q$) AND q$ <> "." THEN x = x + 1
            R$ = R$ + ch$
            ELSE
            IF ch$ = "?" THEN
            IF LEN(q$) AND q$ <> "." THEN R$ = R$ + q$: x = x + 1
            ELSE
            IF ch$ = "*" AND m = LEN(mask$) - 1 THEN
            WHILE x < LEN(path$)
            R$ = R$ + MID$(path$, x + 1, 1)
            x = x + 1
            WEND
            ELSE
            IF ch$ = "*" THEN
            IF z$ = "." THEN
            FOR i = LEN(path$) - 1 TO 0 STEP -1
            IF MID$(path$, i + 1, 1) = "." THEN EXIT FOR
            NEXT
            IF i < 0 THEN
            R$ = R$ + MID$(path$, x + 1) + "."
            i = LEN(path$)
            ELSE
            R$ = R$ + MID$(path$, x + 1, i - x + 1)
            END IF
            x = i + 1
            m = m + 1
            ELSE
            IF z$ = "?" THEN
            R$ = R$ + MID$(path$, x + 1, LEN(path$))
            m = m + 1
            x = LEN(path$)
            ELSE
            FOR i = LEN(path$) - 1 TO 0 STEP -1
            'IF MID$(path$, i + 1, 1) = z$ THEN EXIT FOR
            IF UCASE$(MID$(path$, i + 1, 1)) = UCASE$(z$) THEN EXIT FOR
            NEXT
            IF i < 0 THEN
            R$ = R$ + MID$(path$, x + 1, LEN(path$)) + z$
            x = LEN(path$)
            m = m + 1
            ELSE
            R$ = R$ + MID$(path$, x + 1, i - x)
            x = i + 1
            END IF
            END IF
            END IF
            ELSE
            IF ch$ = "." THEN
            DO WHILE x < LEN(path$)
            IF MID$(path$, x + 1, 1) = "." THEN
            x = x + 1
            EXIT DO
            END IF
            x = x + 1
            LOOP
            R$ = R$ + "."
            END IF
            END IF
            END IF
            END IF
            END IF
            NEXT
            DO WHILE RIGHT$(R$, 1) = "."
            R$ = LEFT$(R$, LEN(R$) - 1)
            LOOP
            R$ = RTRIM$(R$)
            maskNewName$ = R$
            END FUNCTION





            share|improve this answer















            I have managed to write this code in BASIC to mask wildcard filenames:



            REM inputs a filename and matches wildcards returning masked output filename.
            FUNCTION maskNewName$ (path$, mask$)
            IF path$ = "" THEN EXIT FUNCTION
            IF INSTR(path$, "?") OR INSTR(path$, "*") THEN EXIT FUNCTION
            x = 0
            R$ = ""
            FOR m = 0 TO LEN(mask$) - 1
            ch$ = MID$(mask$, m + 1, 1)
            q$ = MID$(path$, x + 1, 1)
            z$ = MID$(mask$, m + 2, 1)
            IF ch$ <> "." AND ch$ <> "*" AND ch$ <> "?" THEN
            IF LEN(q$) AND q$ <> "." THEN x = x + 1
            R$ = R$ + ch$
            ELSE
            IF ch$ = "?" THEN
            IF LEN(q$) AND q$ <> "." THEN R$ = R$ + q$: x = x + 1
            ELSE
            IF ch$ = "*" AND m = LEN(mask$) - 1 THEN
            WHILE x < LEN(path$)
            R$ = R$ + MID$(path$, x + 1, 1)
            x = x + 1
            WEND
            ELSE
            IF ch$ = "*" THEN
            IF z$ = "." THEN
            FOR i = LEN(path$) - 1 TO 0 STEP -1
            IF MID$(path$, i + 1, 1) = "." THEN EXIT FOR
            NEXT
            IF i < 0 THEN
            R$ = R$ + MID$(path$, x + 1) + "."
            i = LEN(path$)
            ELSE
            R$ = R$ + MID$(path$, x + 1, i - x + 1)
            END IF
            x = i + 1
            m = m + 1
            ELSE
            IF z$ = "?" THEN
            R$ = R$ + MID$(path$, x + 1, LEN(path$))
            m = m + 1
            x = LEN(path$)
            ELSE
            FOR i = LEN(path$) - 1 TO 0 STEP -1
            'IF MID$(path$, i + 1, 1) = z$ THEN EXIT FOR
            IF UCASE$(MID$(path$, i + 1, 1)) = UCASE$(z$) THEN EXIT FOR
            NEXT
            IF i < 0 THEN
            R$ = R$ + MID$(path$, x + 1, LEN(path$)) + z$
            x = LEN(path$)
            m = m + 1
            ELSE
            R$ = R$ + MID$(path$, x + 1, i - x)
            x = i + 1
            END IF
            END IF
            END IF
            ELSE
            IF ch$ = "." THEN
            DO WHILE x < LEN(path$)
            IF MID$(path$, x + 1, 1) = "." THEN
            x = x + 1
            EXIT DO
            END IF
            x = x + 1
            LOOP
            R$ = R$ + "."
            END IF
            END IF
            END IF
            END IF
            END IF
            NEXT
            DO WHILE RIGHT$(R$, 1) = "."
            R$ = LEFT$(R$, LEN(R$) - 1)
            LOOP
            R$ = RTRIM$(R$)
            maskNewName$ = R$
            END FUNCTION






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Jun 7 '18 at 9:49









            phuclv

            9,06463889




            9,06463889










            answered Oct 13 '16 at 1:27









            eoredsoneoredson

            1013




            1013








            • 2





              Can you clarify how this answers what was asked in the question?

              – fixer1234
              Oct 13 '16 at 3:58











            • It replicates the function REN uses for wildcard matching such as processing REN *.TMP *.DOC depending on how the function is called before renaming the filenames.

              – eoredson
              Oct 13 '16 at 21:08














            • 2





              Can you clarify how this answers what was asked in the question?

              – fixer1234
              Oct 13 '16 at 3:58











            • It replicates the function REN uses for wildcard matching such as processing REN *.TMP *.DOC depending on how the function is called before renaming the filenames.

              – eoredson
              Oct 13 '16 at 21:08








            2




            2





            Can you clarify how this answers what was asked in the question?

            – fixer1234
            Oct 13 '16 at 3:58





            Can you clarify how this answers what was asked in the question?

            – fixer1234
            Oct 13 '16 at 3:58













            It replicates the function REN uses for wildcard matching such as processing REN *.TMP *.DOC depending on how the function is called before renaming the filenames.

            – eoredson
            Oct 13 '16 at 21:08





            It replicates the function REN uses for wildcard matching such as processing REN *.TMP *.DOC depending on how the function is called before renaming the filenames.

            – eoredson
            Oct 13 '16 at 21:08


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Super User!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsuperuser.com%2fquestions%2f475874%2fhow-does-the-windows-rename-command-interpret-wildcards%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Сан-Квентин

            8-я гвардейская общевойсковая армия

            Алькесар