31

Under Windows 10 cmd.exe, the command del *.tmp may silently delete files like project.tmpl.

Is there some option, workaround, registry entry, or ritual sacrifice to prevent this?

For the incredulous: on my machine, on which the C: drive was formatted by a Windows 10 installer in 2020:

Demo

(where the French error message means file not found).

Braiam
  • 4,709
  • 3
  • 26
  • 57
fgrieu
  • 776
  • 1
  • 8
  • 22
  • 1
    Are you asking if there is a registry edit that makes Windows ignores the del command? `del *.tmp` should only delete files with the `.tmp` file exttension that exist within the directory the command was issued in. – Ramhound Apr 26 '21 at 14:44
  • 15
    @Ramhound I think by default globs in windows match 8.3 filenames in addition to the long filenames. That is probably why the OP is asking. – Tim Seguine Apr 27 '21 at 10:16
  • 14
  • 1
    @TimSeguine there's no globbing in Windows. `file.exe *.txt` passes the raw `*.txt` string to the file.exe. Windows also doesn't split arguments and the program must parse its own command line to get the list of tokens. In reality the C runtime does that for you – phuclv Apr 27 '21 at 16:51
  • 4
    As already written in various answers, the problem is that a file normally has an "8.3" file name; probably `project.tmpl` has the "8.3" name `PROJEC~1.TMP`. The Windows API provides a function (`SetFileShortName()`) that allows changing (renaming) the "8.3" file name of a file without changing the "long" name. So your file might still be named `project.tmpl` but the "8.3" name may be `project.tpl` instead of `projec~1.tmp`. Unfortunately, I didn't find out if there is a "ready" command line command or if you'd have to write a short C/C++ program to do this... – Martin Rosenau Apr 28 '21 at 09:28
  • @MartinRosenau as I said, `fsutil` has everything you need. `fsutil file setshortname` will change the name to your desired one. `fsutil 8dot3name strip` will remove all short names – phuclv Apr 28 '21 at 09:40
  • @phuclv it's pretty common in my experience to call that feature globbing whether it is provided by the shell or not, but still thanks for the interesting fact which I actually did not know. – Tim Seguine Apr 28 '21 at 11:46
  • 1
    @TimSeguine because there's no globbing in Windows, talking about "default globs" there makes zero sense. You need to talk about how the wildcard pattern is treated in each program like forfiles.exe, where.exe, cmd.exe. powershell.exe... And the issue doesn't exist in most modern programs. Only cmd still retains that legacy behavior – phuclv Apr 29 '21 at 08:21
  • @phuclv I didn't say any thing about "default globs". The intended semantic grouping in that sentence is "by default". And I didn't invent using the words glob and wildcard interchangeably. https://en.wikipedia.org/wiki/Glob_(programming) It's a general programming term. And yes windows doesn't do wildcard expansion on the shell. I also never claimed it did. I was describing the behavior of the wildcard of the standard windows utility commands. They do their own wildcard expansion. Wildcard expansion is often referred to as globbing. Nothing I said is controversial. Dont at me again. – Tim Seguine Apr 30 '21 at 14:08

4 Answers4

36

A file may have short DOS 8.3 names and all internal commands from DOS era also work with both long and short name for compatibility reasons1 so a file with long extension may be accidentally matched. There are many similar questions:

It'd be better to use PowerShell because short names aren't matched anymore. dir in cmd must match the short names in order to not break legacy programs. PowerShell doesn't have that limitation. Just run Remove-Item *.tmp or any of its aliases like rm *.tmp, del *.tmp

In cmd you'll have to filter with findstr like this

for /F "delims=" %f in ('dir /B ^| findstr /I /E ".tmp"') do @del "%f"

(Replace %f with %%f in a batch file)

There are also many other solutions such as forfiles (as that's not a cmd's internal command) that you can find in How can I get the "dir" and "copy" commands to operate on "*.xyz" but not "*.xyz~"?


However it'll be even better to disable 8.3 name generation and remove all the short names. In fact since Windows 8 and Windows Server 2012 newly formatted volumes will have 8.3 name generation disabled by default for performance reasons. That'll also help avoid situations like this: WinXP dir command: 3 and 4 char extensions are the same?

In fact, recent versions of Windows Server don’t even enable 8.3 naming when you format new data volumes.

https://docs.microsoft.com/en-us/archive/blogs/josebda/windows-server-2012-file-server-tip-disable-8-3-naming-and-strip-those-short-names-too

If your drive still has 8.3 name generation enabled then run the following command to disable it on drive C:

fsutil 8dot3name set C: 1

or run fsutil 8dot3name set 1 to disable it on all volumes

The setting can also be set in registry. The corresponding key is HKLM\System\CurrentControlSet\Control\FileSystem\NtfsDisable8dot3NameCreation. The value for the fsutil and the registry key is like this

  • 0: Enables 8dot3 name creation for all volumes on the system.
  • 1: Disables 8dot3 name creation for all volumes on the system.
  • 2: Sets 8dot3 name creation on a per volume basis.
  • 3: Disables 8dot3 name creation for all volumes except the system volume.

If the names are there it can also be removed with fsutil 8dot3name strip

Note that fsutil must be run with admin privileges


1Old commands list their files using the old FindFirstFile API which matches both long and short names. See Why does FindFirstFile find short names?. New code should use FindFirstFileEx instead to avoid that

phuclv
  • 26,555
  • 15
  • 113
  • 235
  • 3
    While I confess I could use powershell in the case at hand, there are times where it's not an option because cmd.exe is invoked by some essential legacy tool (like Keil's uVision 4). – fgrieu Apr 26 '21 at 16:01
  • @fgrieu if the cmd command can't be controlled by you then you'll have to disable and strip 8.3 names like I said – phuclv Apr 26 '21 at 16:09
  • 1
    @phuclv I wonder if there is another factor which enables 8.3 names? Like @fgrieu I also have Windows 10 machine which has only ever had W10 installation and see same issue with cmd.exe `del *.tmp`. It seems `fsutil 8dot3name strip C:` doesn't turn off 8.3 as it warns of many registry keys using 8.3 filenames - though I won't risk "/f" option. – DuncG Apr 26 '21 at 17:30
  • 3
    @fgrieu can't you just run PS script from batch script in that case? – PTwr Apr 26 '21 at 23:31
  • @DuncG I've never seen that. Probably some installed program enabled or added short names – phuclv Apr 27 '21 at 03:10
  • In PowerShell, [`dir` is an alias for `Get-ChildItem`](http://pvm-professionalengineering.blogspot.com/2019/10/powershell-aliases-and-missing.html) (other aliases for `Get-ChildItem` are `ls` and `gci`). This may cause some confusion. – Peter Mortensen Apr 27 '21 at 11:06
  • @fgrieu if it consoles you, about two years ago I've spent a day looking around because NTFS directories created under WSL are by default case sensitive (used it for git) and in IAR "case sensitive" means "all lower" or "all uper" (can't remember right now). That said... uVision 4? That's some serious legacy, even in our world. I first started doing embedded in 2013 and it was uVision 5. – jaskij Apr 27 '21 at 20:29
  • 3
    Disabling 8.3 names generation will work out badly if you learn to rely on having this and then accidentally delete some files on a flash drive (which might've been formatted and populated on another machine) this way. – Ruslan Apr 27 '21 at 21:38
  • 4
    @Ruslan relying on 8.3 names will work out badly because those names aren't shown by default and can be changed arbitrarily, so [a naive wildcard like `*1.*` may delete all files](https://superuser.com/q/370300/241386) because short names tend to have a number in their names. Never use short names in any cases. All applications already support long names. Anything that doesn't accept a long path or path with spaces is shitty and must be thrown away – phuclv Apr 28 '21 at 02:35
  • @DuncG in case of corporate PCs then it may be enforced by group policy – phuclv Apr 28 '21 at 04:48
  • @phuclv I built my home PC from new components. The SSD might have been pre-formatted, but I think your suggestion is more likely: that some app install depended on and caused 8.3 activation. The registry key scan (on deactivate attempt) warns mostly of paths for Windows apps, except for a pathname to the current desktop screensaver SCR (which is mine). – DuncG Apr 28 '21 at 07:28
  • You seem to have misunderstood my comment. I mean that if you do rely on the fact that you've disabled 8.3 names on all your systems, you're still not guarded from getting a removable drive that does contain such names. – Ruslan Apr 28 '21 at 07:32
  • @Jan Dorniak: I'm doing Smart Cards, some of them still are 8051 on steroids, and occasionally I dive into old projects. – fgrieu Apr 28 '21 at 07:58
  • @fgrieu oof. Legacy and security. Sounds like a major pain. uVision 5 wasn't that bad as far as embedded IDEs ago, although I've never touched uVision 4. I'm still sad Microchip didn't keep or expand on Atmel Studio. – jaskij Apr 28 '21 at 08:05
9

The reason this happens is that Windows has retained compatibility with file names from MS-DOS, where file names were limited to a 3-character extension. To facilitate the transition, Windows 95 introduced a compatibility mode where every file has an “8.3” alias, also known as the file's “short name”: in addition to project.tmpl, the same file is also accessible as PROJEC~1.TMP. You can see these short names in directory listings with dir /x. The wildcard pattern *.tmp matches this alternative name (file names are case-insensitive). Microsoft documents this for dir; it applies to other commands as well, including del.

I can reproduce the behavior you've observed on my corporate Windows 10 machine (not upgraded from a previous Windows 10). I have local admin privileges, but not domain admin, and I'm not allowed to run fsutil 8dot3name query c: (Error: Access is denied.).

If you control the part of the system that's producing the temporary files, a robust workaround is to give them an extension that is anything but 3 (or 0) characters long. *.tm or *.temp would not match 8.3 aliases of file names with a different extension. Alternatively, put those temporary files in a separate directory and just remove the whole directory when you want to delete them.

Gilles 'SO- stop being evil'
  • 69,786
  • 21
  • 137
  • 178
  • Did you elevate your cmd window before trying (must have „Administrator:“ in title). I don’t think this is or normally prevented from local admins. – eckes Apr 28 '21 at 01:12
  • 1
    @eckes Yes, as I wrote, I tried this as local administrator (or whatever it means when I start a cmd window using “run as administrator” and it says “Administrator” in the window title). – Gilles 'SO- stop being evil' Apr 28 '21 at 08:40
8

In addition to the CMD-based solution provided in phuclv's answer, you can use the not-well-known-but-very-cool FORFILES command:

FORFILES /M *.tmp /C "CMD /C ECHO @path && DEL @path"

The "search mask" parameter (/M) does not match on the 8.3 names, hence .tmp and .tmpl are two different extensions without any additional filtering. The search mask is also case-insensitive.

For example:

C:\TEMP>echo a > del_test_1.tmp

C:\TEMP>echo a > del_test_2.TMP

C:\TEMP>echo a > del_test_3.tmpl

C:\TEMP>dir *.tmp
 Volume in drive C is Windows
 Volume Serial Number is B0F0-812B

 Directory of C:\TEMP

04/28/2021  11:46 AM                 4 del_test_1.tmp
04/28/2021  11:48 AM                 4 del_test_2.TMP
04/28/2021  11:48 AM                 4 del_test_3.tmpl
               3 File(s)             12 bytes
               0 Dir(s)  179,312,234,496 bytes free

C:\TEMP>FORFILES /M *.tmp /C "CMD /C ECHO @path && DEL @path"

"C:\TEMP\del_test_1.tmp"
"C:\TEMP\del_test_2.TMP"

C:\TEMP>dir *.tmp
 Volume in drive C is Windows
 Volume Serial Number is B0F0-812B

 Directory of C:\TEMP

04/28/2021  11:48 AM                 4 del_test_3.tmpl
               1 File(s)              4 bytes
               0 Dir(s)  179,310,305,280 bytes free
Solomon Rutzky
  • 233
  • 1
  • 8
4

You need one for loop to avoid some 3rd file with additional characters after ".tmp" to be deleted too...

for /t tokens^=* %i in ('where .:*.tmp')do echo\ del "%~i"

The where command will return only files .end with .tmp (only)...

You can also try /Recursive:

for /t tokens^=* %i in ('where/r "D:\MyProject\Folder" *.tmp')do echo\ del "%~i"

  • I can't explain the reason for this event that happened to you, but it happens to me too...

  • Now I know... @phuclv explained in his answer ... plz, consider his answer

> reg query "HKLM\System\CurrentControlSet\Control\FileSystem" /v "NtfsDisable8dot3NameCreation"

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\FileSystem
    NtfsDisable8dot3NameCreation    REG_DWORD    0x0

enter image description here

enter image description here


  • Using For /fwith whereto keep files *.tmp*

enter image description here

  • I also don't know how to explain why this event doesn't happen with @spikey_richie...
Io-oI
  • 7,588
  • 3
  • 12
  • 41
  • 2
    the reason is simple: [`FindFirstFile` matches short names](https://devblogs.microsoft.com/oldnewthing/20050720-16/?p=34883) and all cmd commands use it for DOS compatibility. New apps should use `FindFirstFileEx` to prevent this from happening. [WinXP dir command: 3 and 4 char extensions are the same?](https://superuser.com/q/238900/241386) – phuclv Apr 26 '21 at 15:41
  • @phuclv yep, thanks – Io-oI Apr 26 '21 at 15:43
  • 2
    Alternatively you could write `for %i in (*.tmp) do @if /i "%~xi" == ".tmp" del "%~i"`? – Neil Apr 27 '21 at 10:09