12

So, I'm looking for a command-line approach to get a short(ish) textual dump of relatively low-level metadata for a file, approximately the same way that stat does on *nix systems.

In other words, information that's readily available through Win32 or NT file APIs without actually reading the contents of the file, and definitely without having to go anywhere near the Shell (as seen in Explorer and the Save/Open dialogs, and anything using shell32.dll).

On NTFS, this info would tend be stored in the file's MFT entry, though I've heard that exactly which attributes end up in the MFT proper depends largely is largely a matter of which will fit, since many attributes are optional, repeatable, and/or variable size, some can be , though it's also possible to use more than one slot in the MFT. (In fact, it's even possible to store a file/directory's full contents in the MFT if the other attributes don't take too much space!) So, um, obviously "appears in the MFT" can't be use to actually define what I'm looking for.

Also, I'm not looking for tools that parse NTFS itself, or only work on NTFS filesystems: I only mention NTFS because, well, what other local FS is going to support more NT-specific things?

For concreteness, here's the stat on the Debian box I'm using as a router:

hydrogen% stat .
  File: ‘.’
  Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: fe00h/65024d    Inode: 507974      Links: 4
Access: (0755/drwxr-xr-x)  Uid: ( 1000/ naesten)   Gid: ( 1000/ naesten)
Access: 2016-08-31 14:12:47.650440935 -0400
Modify: 2016-09-20 17:26:15.935591584 -0400
Change: 2016-09-20 17:26:15.935591584 -0400
 Birth: -
hydrogen%

And here's the one from Git for Windows:

colored raster version of below "screenshot"

Sam@Sam-laptop MINGW64 ~
$ git --version
git version 2.8.2.windows.1

Sam@Sam-laptop MINGW64 ~
$ stat .
  File: '.'
  Size: 0               Blocks: 32         IO Block: 65536  directory
Device: 147ac391h/343589777d    Inode: 1970324836977201  Links: 1
Access: (0755/drwxr-xr-x)  Uid: (197608/     Sam)   Gid: (197121/ UNKNOWN)
Access: 2016-09-18 18:32:28.072538600 -0400
Modify: 2016-09-18 18:32:28.072538600 -0400
Change: 2016-09-18 18:32:28.072538600 -0400
 Birth: 2014-08-21 18:52:08.217444400 -0400

Sam@Sam-laptop MINGW64 ~
$

At this point, I was going to say that the "Birth" timestamp field is a Windowsism, but clearly that's not quite true because Debian's stat shows the field, only it's empty above because the filesystem doesn't actually have that field. (Which is, at least, much clearer than the "Access" timestamps: neither system is updating those in the traditional strictatime manner.)

Also note how the Windows port is displaying made-up file access permissions, an implausibly-large "inode number", and what I can only assume are also made up UID/GID numbers (on account of how NT uses variable-length hierarchical identifiers rather than fixed-size numbers to identify users and groups, and doesn't permit both user and group ownership at the same time; that is, on NT a file is owned by a single user OR group, and anyone else just gets their rights from the ACL).

Does MS provide a tool of this sort, presumably one that shows more actual information and less fairyland information?


My original text follows:

Yes, I know there are ports of stat, but (a) I'm concerned that information might fall through the cracks if:

  1. It doesn't commonly appear in Unix stat output
  2. It's missing from whichever stat upstream's code (if any)
  3. The porter(s) either
    1. Don't notice Windows provides the information
    2. Are building on a POSIX layer that doesn't plumb it through
    3. Aren't sure how best to present the information (or that it's even appropriate to do so) in the context of stat
    4. Don't have an unlimited amount of time on their hands

and (b) I was wondering if Microsoft supplied any analogous tool(s).

But really, any command-line way get sort of information stat might provide (regardless of whether it involves a program called stat), along with a description of the more-or-less NTFS-specific information it provides, would be of interest.

P.S. I'm on Windows 7, but don't let that stop you from mentioning things first shipped for/in later versions of Windows.

4

4 Answers 4

7

Get-Item /path/to/file | Format-List should get you what you need using only native Cmdlets.

Get-Item gets the details about whatever file you are interested in. Format-List will, somewhat unintuitively, expose more parameters to the PowerShell host than would be seen otherwise. It also formats it as a list, as you would expect.

 ~> Get-Item ./temp.txt | Format-List


    Directory: /Users/brianshacklett


Name           : temp.txt
Length         : 989
CreationTime   : 4/5/18 9:52:04 PM
LastWriteTime  : 4/5/18 9:52:04 PM
LastAccessTime : 6/26/18 7:58:18 PM
Mode           : ------
LinkType       :
Target         :
VersionInfo    : File:             /Users/brianshacklett/temp.txt
                 InternalName:
                 OriginalFilename:
                 FileVersion:
                 FileDescription:
                 Product:
                 ProductVersion:
                 Debug:            False
                 Patched:          False
                 PreRelease:       False
                 PrivateBuild:     False
                 SpecialBuild:     False
                 Language:

If the verbosity bothers you, you can use aliases to shorten this to gi /path to file | fl

2
6

I just threw together a PowerShell script. It includes pretty much everything that makes sense for Windows, except the access controls, which are usually too large to fit in a summary. You can see them with the icacls utility.

$obj = $args[0]
If ($obj.GetType().Name -eq 'String') {$obj = gi $obj}
'File: ' + $obj.FullName
'Size: ' + $obj.Length
$extents = [string](fsutil file queryextents "$($obj.FullName)")
If (-$extents.StartsWith('i')) {
  'Clusters: ' + ($extents.Substring(26) -split 'LCN')[0]
  'LCN: ' + $extents.Substring(42)
} Else {
  'Clusters: stored in file table'
}
'Attributes: ' + $obj.Attributes
$volumeinfo = (fsutil fsinfo volumeinfo "$([System.IO.Path]::GetPathRoot($obj.FullName)[0] + ':')")
$volumeinfo | ? {$_.StartsWith('Volume Serial')} | % {$_.Replace(' :', ':')}
$fileid = (fsutil file queryfileid "$($obj.FullName)")
'File ID: ' + $fileid.Substring(11)
$links = (fsutil hardlink list "$($obj.FullName)")
'Links: ' + ([string[]]$links).Length
'Owner: ' + $obj.GetAccessControl().Owner
''
'Access: ' + $obj.LastAccessTime
'Modify: ' + $obj.LastWriteTime
'Create: ' + $obj.CreationTime
'' # Extra blank line for readability

It uses the normal .NET/PowerShell file system entry properties and calls out to the fsutil utility for the tricky stuff. Since that utility isn't a PowerShell cmdlet, I had to do some messy string chopping to get the right information.

To use it, save it as a .ps1 file and see the Enabling Scripts section of the PowerShell tag wiki. Example of output:

File: C:\Users\Ben\test\blank.ps1
Size: 8
Clusters: stored in file table
Attributes: Archive
Volume Serial Number: 0x9c67b42c
File ID: 0x0000000000000000000700000014428b
Links: 1
Owner: POWERSHELL\Ben

Access: 07/29/2016 20:01:25
Modify: 07/29/2016 20:02:43
Create: 07/29/2016 20:01:25
6

There are no exact equivalent but the closest you can get with Windows built-in utilities is with the fsutil command. It'll give you most of the things stat provides, along with more detailed information about the underlying file system structure. But unlike stat it needs to be run under administrator rights. You can also use wmic (or the Get-WmiObject (gwmi alias) PowerShell version) for a lot of useful data. For a few other options you need to get with other commands

Below are how to get the analogous information to the format options in stat (in PowerShell, with cmd version if available) that you can use to customize the output. Otherwise just dump the fsutil file layout, fsutil fsinfo sectorinfo and fsutil fsinfo ntfsinfo output directly

  • Access rights: Windows access rights are very different from POSIX ones. Therefore there are no analog to %a. However there does exist similar information

  • Number of blocks allocated (%b): Run fsutil file layout path\to\file or fsutil volume filelayout path\to\file and count the total number of clusters allocated in the $DATA stream.

    For example for the below output we have 4 extents allocated, each is 82, 432.419, 259 and 155.076-cluster long respectively. As a result the number of blocks allocated is 82 + 432419 + 259 + 155076 = 587836. A quick trick is to add the VCN number of the last cluster with it's length: 432760 + 155076 = 587836

      Stream                  : 0x080  ::$DATA
          Attributes          : 0x00000000: *NONE*
          Flags               : 0x00000000: *NONE*
          Size                : 2.407.772.410
          Allocated Size      : 2.407.776.256
          Extents             : 4 Extents
                              : 1: VCN: 0 Clusters: 82 LCN: 1.620.482
                              : 2: VCN: 82 Clusters: 432.419 LCN: 5.331.392
                              : 3: VCN: 432.501 Clusters: 259 LCN: 3.230.067
                              : 4: VCN: 432.760 Clusters: 155.076 LCN: 9.299.239
    
  • The size in bytes of each block reported by %b (%B):

      fsutil fsinfo ntfsinfo <drive> | findstr /c:"Bytes Per Cluster"`
    
  • SELinux security context string (%C): No comparable feature

  • Device number (%d, %D): No similar counterpart. But you can use the following command if you want to get the device ID

      (Get-WmiObject win32_volume | where { $_.driveletter -eq '<drive>' }).deviceid
    
  • Raw mode in hex (%f): No equivalent form. See %a/%A above

  • File type (%F): No direct equivalent, since Windows file and driver models are very different and there are no such things like character devices on Windows so you won't get "character special file". However generally you can use (ls path\to\file).Mode to get the file mode like %A above, and (ls path\to\file).LinkType to get link type

  • Group name of owner (%G):

      (Get-Acl file.ext).Group
    
  • Group ID of owner (%g):

      (gwmi win32_useraccount | where { $_.caption -eq "$((Get-Acl file.ext).Group)" }).SID
    
  • Number of hard links (%h): There are no similar information. However fsutil hardlink list path\to\file prints all the hard links of a file, so we can easily count them with

      (fsutil hardlink list path\to\file | Measure-Object).Count
    
    • Alternatively use (fsutil file layout path\to\file | sls -CaseSensitive -Pattern '^Link.+\s+:\s+0x[0-9a-f]+:\s*HLINK Name\s+:' | Measure-Object).Count
  • Inode number (%i): There's no inode in Windows, but NTFS (and possibly newer file systems like ReFS) has an equivalent called file ID that can be extracted from fsutil file layout output or directly with

      fsutil file queryfileid path\to\file
    
  • File name (%n): This is quite obvious

  • Quoted file name with dereference if symbolic link (%N):

  • I/O block size (%o): see %s for file systems below

  • Total size, in bytes (%s): (ls path\to\file).Length. Or can be easily seen in cmd's dir output, and in fsutil file layout path\to\file output under the Size field like above

  • Device type (%t, %T): See %T for file system type below

  • User name of owner (%U):

      (Get-Acl path\to\file).Owner
    
  • User ID of owner (%u): You can get the user SID with the below command

      (gwmi win32_useraccount | where { $_.caption -eq "$((Get-Acl D:\zz.bat).owner)" }).SID
    
  • File time:

    • Last access time (%x): (ls path\to\file).LastAccessTime
    • To get the last access time since Epoch (%X): (ls path\to\file).LastAccessTime.Ticks or (ls path\to\file).LastAccessTime.ToFileTime() (depending on which Epoch you want: 1/1/0001 or 1/1/1601) to get the file time in 100ns resolution
    • Similarly last modification time (%y, %Y) can be retrived with LastWriteTime
    • Getting last metadata change time (%z, %Z) is trickier and you may need to call win32 APIs from PowerShell

For file systems generally you can use fsutil fsinfo ntfsinfo <drive> or fsutil fsinfo sectorinfo <drive> to find detailed information

  • Free blocks available to non-superuser (%a): No equivalent. But I think you can check it with fsutil quota query

  • Total data blocks in file system (%b):

      fsutil fsinfo ntfsinfo <drive> | findstr /c:"Number Sectors"
    
  • Total file nodes in file system (%c): If it's about the number of inodes then there's no MFT record limit on NTFS as well as many newer Linux filesystems like Btrfs or XFS. The MFT file size will be increased to store more file records. The total number of MFT records can be checked with fsutil fsinfo ntfsinfo <drive>

  • Free file nodes in file system (%d): Not sure what this is. If it's about the number of unused inodes then as said above, there's generally no upper limit. Possibly fsutil volume allocationreport <drive> will work

  • Free blocks in file system (%f):

    • fsutil fsinfo ntfsinfo <drive> | findstr /c:"Free Clusters"
    • More detailed information can be found with fsutil volume allocationreport <drive>
  • File System ID in hex (%i): I don't know what's the exact synonymous on Windows but these may provide that information

      fsutil fsinfo ntfsinfo <drive> | findstr /c:"Resource Manager Identifier" /c:"NTFS Volume Serial Number"
      (gwmi win32_volume | where { $_.driveletter -eq 'd:' }).serialnumber`
    
  • Maximum length of filenames (%l):

      (gwmi win32_volume | where { $_.driveletter -eq <drive>  }).maximumfilenamelength
    
  • Block size (for faster transfers) (%s):

      fsutil fsinfo sectorinfo <drive> | findstr /c:"Performance"
    
  • Fundamental block size (for block counts) (%S):

      fsutil fsinfo sectorinfo <drive> | sls physical
      fsutil fsinfo ntfsinfo <drive> | sls physical
      (gwmi win32_volume | where { $_.driveletter -eq 'd:' }).blocksize
    
  • Type: For %T you can use fsutil fsinfo volumeinfo <drive> | findstr /c:"File System Name". I'm not sure what %t is but it seems that's the magic number of the partition

The (Get-WmiObject win32_ALIAS | where { $_.FILTERFIELD -eq 'VALUE' }).GETFIELD commands above can be changed to wmic ALIAS where FILTERFIELD='VALUE' get GETFIELD /value. However wmic was deprecated so in the future it may be removed

1
  • This is all nice and dandy, but FWIW fsutil file layout didn't exist before W10 1803. fsutil volume filelayout could do more or less the same things though (that in turn was introduced around W8).
    – mirh
    Commented Jan 12, 2023 at 0:22
0

The tool you're using is using an environment that acts as a thin layer between Windows and Linux and as such needs to emulate certain bits and pieces to let Linux tools do their thing. This explains why there is some fabricated information which is (hopefully) matched in a consistent matter. You'd have to look at the specifics of the environment implementing it to make sense of it.

On the other hand it's (for me) not exactly clear what you're looking for. As you said there are various APIs available which you don't want to use. Is just that "emulated" information relevant to you?

Otherwise you'll probably need an array of tools to get all the information you need. Access rights and ACLs are pretty deeply rooted in NTFS to my knowledge. Tools like icalcs could help you with getting information on the access rights on a command line basis. To get more indepth information (for parts of it) you might want to consider using WMI as shown in that example. A simple dir or get-childitem from a PowerShell might give you more information about the creation times and such. I don't know about any built-in tools that give you a run down on all of that information. What you might do is use the information about the APIs and wrap them in a PowerShell script. This might come close to what you actually want to do. I'm not sure if there is a script out there that does that already.

3
  • Yeah, I have a rough idea of how Windows ports of stat work, this one turns out to be using MSYS2. As to what I wanted, I was curious whether MS had some roughly-analogous tool already built, perhaps in one of their many "kits", or that there was some well-known third-party freeware tool. And it's not that I refuse to use APIs or anything, it's just that that's more work. (Anyway, if that were my aim, I would have asked about the APIs on SO.)
    – SamB
    Commented Sep 28, 2016 at 18:46
  • Or at least, SO would have been the place I'd have considered asking about the APIs: I suspect that the question you linked, What is lstat() alternative in windows? would have come to my attention as I was working on my title if not before. (Huh, not only does that "show the title of a linked question" feature not work in comments, apparently neither does the <> notation?)
    – SamB
    Commented Sep 28, 2016 at 18:55
  • ... though it's not all that well answered ...
    – SamB
    Commented Sep 28, 2016 at 19:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .