How can I get the "dir" and "copy" commands to operate on "*.xyz" but not "*.xyz~"?

When I use copy *.txt somefolder\ the system seems to copy all *.txt~ files too, which is not what I want. A similar same effect can be seen with dir:

C:\Users\Paul\Documents\Programs\Proffy>dir *.txt
 Volume in drive C is Vista
 Volume Serial Number is EC23-AD6B

 Directory of C:\Users\Paul\Documents\Programs\Proffy

29/11/2008  13:54            35,821 COPYING.txt
31/10/2009  21:54             1,644 INSTRUCTIONS.txt
06/06/2009  15:57             1,393 INSTRUCTIONS.txt~
04/01/2009  11:59               116 Notes.txt
19/04/2009  16:53               134 README.txt
04/01/2009  12:42               132 README.txt~
31/10/2009  21:30               197 TODO.txt
31/10/2009  19:10               414 TODO.txt~
               8 File(s)         39,851 bytes
               0 Dir(s)  41,938,862,080 bytes free

C:\Users\Paul\Documents\Programs\Proffy>

How can I get dir and copy to only operate on files that end with .txt and not .txt~?


Apparently the shell considers both the short and the long name for wildcard expansion. Longer explanation can be found in shf301's answer. This is unfortunate and probably a left-over from Ye Olde Days of DOS because that's what cmd is trying to be compatible with—sort of—after all.

Several options here:

  1. Use forfiles, which has a different semantic for wildcard expansion:

    forfiles /m *.txt /c "cmd /c copy @file foo"
    

    This is available at least on Vista and later.

  2. Use for and check the extension:

    for %a in (*.txt) do @if %~xa==.txt @copy "%i" foo
    

    Unfortunately for also returns any files with the .txt~ extension when only using wildcard expansion. That's why we need to check the extension a second time.

  3. Use xcopy. While xcopy has the same semantics for wildcard expansion as the shell you can give it a file with names to ignore:

    echo .txt~>tmpfile
    xcopy *.txt foo /exclude:tmpfile
    del tmpfile
    
  4. Use robocopy. While robocopy has the same semantics for wildcard expansion as the shell you can give it a list of files/wildcards to ignore:

    robocopy . foo *.txt /XF *.txt~
    
  5. Use for, dir and findstr in an appropriate combination. This essentially just filters out all lines that have a ~ at the end and operates on the rest. The if variant above was more elegant, I think.

    for /f "usebackq delims=" %i in (`dir /b *.txt ^| findstr /r "[^~]$"`) do @copy "%i" foo
    
  6. Just for completeness: PowerShell:

    Copy-Item *.txt foo
    

My solution was to execute del *.xyz~ prior to my copy *.xyz. Not brilliant, but it works.