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

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