Why does `dir *.*` give me all files and folders?

When I run dir *.* it produces unexpected results. Even files and folders without any dot in their names are listed. For example

C:\>dir *.*
 Volume in drive C is System
 Volume Serial Number is AC0A-29DA

 Directory of C:\

14-03-2017  05:17 PM             8,192 ntuser.dat
03-01-2017  07:10 PM    <DIR>          Perl520
28-03-2017  10:13 AM    <DIR>          Program Files
28-03-2017  10:13 AM    <DIR>          Program Files (x86)
04-01-2017  03:25 PM    <JUNCTION>     Python27 [C:\Program Files\Anaconda]
06-02-2017  10:16 PM    <DIR>          SAP
28-03-2017  04:10 PM               152 useragent.log
03-01-2017  03:04 PM    <DIR>          Users
16-03-2017  04:24 PM    <DIR>          VM
22-03-2017  11:13 AM    <DIR>          Windows
               2 File(s)          8,344 bytes
               8 Dir(s)  270,172,966,912 bytes free

Why is that? Is there any way to list only files with a dot?


I am writing this answer because OP has emphasized that:

what I'm interested is why *.* matches all files, as stated in the question

The DIR command comes from a time when:

  1. Period (.) was not allowed as a character in file or folder names
  2. File and folder names were restricted to 8 characters for name and 3 characters for extensions

Therefore, by that standard, *.* meant whatever the name and whatever the extension. It did not mean a string containing a ".", which may or may not have characters before or after the ".".

Microsoft policy is preserving backward compatibility. Hence, that interpretation of *.* is retained.

But in Windows PowerShell, *.* means a string containing a ".", which may or may not have characters before or after the ".".


Why is that?

One could find an answer to "Why is that?" in Wildcards article:

The * wildcard will match any sequence of characters
               (0 or more, including NULL characters)

The ? wildcard will match a single character
               (or a NULL at the end of a filename)

Wildcard matching rules

  • * Generally matches any 0 or more characters, with one exception (see next rule). The non-greedy wildcard is free to match as many or as few characters as are necessary for the remainder of the mask to match.

  • *. At end of mask matches any 0 or more characters except for {dot}. In actuality, the rule applies with any number of {dot} and {space} characters between the * and terminal {dot}. The regular expression for this term is "[*][. ]*[.]$"

  • ? Match 0 or one character, except for {dot}. The only time it matches 0 characters is when it matches the end of the name, or the position before a {dot}. The question mark can also be used more than once to match more than one character.

Implication. The last {dot} in a file/folder name separates base name and extension. So

  • dir *. displays all items with no extension, and
  • dir *.* displays all items with extension of zero or more characters.

Strictly speaking, dir *. displays all items with no period (.) in name. (BTW, Naming Files, Paths, and Namespaces MSDN article says explicitly that "it is acceptable to specify a period as the first character of a name".)

Is there any way to list only files with a dot?

I don't think so. However, there is a workaround with a fitting regular expression.

PowerShell (full scope solution if used in a Powershell console):

:: PowerShell - no extension, full syntax
PowerShell -c "Get-ChildItem | Where-Object {$_.Name -match '^.[^\.]*$'}"
:: PowerShell -    extension, alias syntax
PowerShell -c "dir | ? {$_.Name -match '^..*\...*$'}"

Cmd (an idea only, might require some elaboration):

:: CMD/batch - no extension
for /F "delims=" %%G in ('dir /OGN /B ^| findstr "^.[^\.]*$"') do @echo %%~tG %%~aG %%~zG %%~nxG
:: CMD/batch -    extension
for /F "delims=" %%G in ('dir /OGN /B ^| findstr "^..*\...*$"') do @echo %%~tG %%~aG %%~zG %%~nxG

Addendum: a bonus and explanation

An intuitive guess that Name is concatenated BaseName and Extension does not hold. The following script proves it using cmd and PowerShell core features, and the weird ^..*\...*$ regex is derived from it's results.

@ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "_workingDirectory=%~1"
if "%_workingDirectory%"=="%tmp%\tests_SU_1193102" (
  >NUL 2>&1 (
      mkdir "%_workingDirectory%"
      pushd "%_workingDirectory%"
      rem make directories
      mkdir     .Fldr-Ext
      mkdir     aFldr-Ext
      mkdir     .Fldr.Ext
      mkdir     aFldr.Ext
      rem create files
      copy  NUL .File-Ext 
      copy  NUL aFile-Ext 
      copy  NUL .File.Ext 
      copy  NUL aFile.Ext
      popd
  )
) else if "%_workingDirectory%"=="" set "_workingDirectory=%CD%"
pushd "%_workingDirectory%"
set "_first=ItemName  Attributes    BaseName Extension"
echo ON
::                        dir /OGN | findstr "Ext$"
for /F "delims=" %%G in ('dir /OGN /B') do @((if defined _first (echo %_first%&echo(&set "_first="))&echo %%~nxG %%~aG  %%~nG   %%~xG)
::   Get-ChildItem | Select-Object -Property Mode, BaseName, Extension, Name
PowerShell -c "dir | select -pr Name, Mode, BaseName, Extension | sort -pr @{Expression='Mode';Descending=$true}, @{Expression='Name';Descending=$false}"

Output:

==> D:\bat\BaseName_vs_Extension.bat "%tmp%\tests_SU_1193102"

==> for /F "delims=" %G in ('dir /OGN /B') do @((if defined _first (echo ItemName  Attributes   BaseName Extension  & echo(  & set "_first=" ) )  & echo %~nxG %~aG     %~nG    %~xG )
ItemName  Attributes    BaseName Extension

.Fldr.Ext d----------   .Fldr   .Ext
.Fldr-Ext d----------           .Fldr-Ext
aFldr.Ext d----------   aFldr   .Ext
aFldr-Ext d----------   aFldr-Ext
.File.Ext --a--------   .File   .Ext
.File-Ext --a--------           .File-Ext
aFile.Ext --a--------   aFile   .Ext
aFile-Ext --a--------   aFile-Ext

==> PowerShell -c "dir | select -pr Name, Mode, BaseName, Extension | sort -pr @{Expression='Mode';Descending=$true}, @{Expression='Name';Descending=$false}"

Name      Mode   BaseName  Extension
----      ----   --------  ---------
.Fldr.Ext d----- .Fldr.Ext .Ext
.Fldr-Ext d----- .Fldr-Ext .Fldr-Ext
aFldr.Ext d----- aFldr.Ext .Ext
aFldr-Ext d----- aFldr-Ext
.File.Ext -a---- .File     .Ext
.File-Ext -a----           .File-Ext
aFile.Ext -a---- aFile     .Ext
aFile-Ext -a---- aFile-Ext

Compare definition of BaseName property, different for files and folders:

PS D:\PShell> Get-ChildItem | Get-Member -Name BaseName | Format-List -property TypeName, Definition


TypeName   : System.IO.DirectoryInfo
Definition : System.Object BaseName {get=$this.Name;}

TypeName   : System.IO.FileInfo
Definition : System.Object BaseName {get=if ($this.Extension.Length -gt 
             0){$this.Name.Remove($this.Name.Length - 
             $this.Extension.Length)}else{$this.Name};}

My original answer was based on unforgivable misunderstanding:

Read dir /?, use dir /A:-D:

/A          Displays files with specified attributes.
attributes   D  Directories                R  Read-only files
             H  Hidden files               A  Files ready for archiving
             S  System files               I  Not content indexed files
             L  Reparse Points             -  Prefix meaning not

Another approach: apply findstr regex as dir *.* | findstr /V "<.*>"