.NET中如何识别Linux文件类型(常规文件、目录、符号链接等)
Linux文件类型判断的.NET托管实现方案
.NET 5及以上版本已经提供原生托管API支持Linux文件类型判断,不需要调用外部bash进程,低版本.NET也可以通过P/Invoke调用libc接口实现,以下是具体实现方式:
核心判断逻辑
.NET 在System.IO命名空间下提供了File.GetUnixFileMode(string path)方法,可直接读取Unix系系统的文件类型与权限标识位,该API返回的UnixFileMode枚举已经包含7种Linux文件类型的对应掩码:
UnixFileMode.Regular:常规文件(对应标识-)UnixFileMode.Directory:目录(对应标识d)UnixFileMode.CharacterDevice:字符设备文件(对应标识c)UnixFileMode.BlockDevice:块设备文件(对应标识b)UnixFileMode.Socket:本地套接字文件(对应标识s)UnixFileMode.NamedPipe:命名管道(对应标识p)UnixFileMode.SymbolicLink:符号链接(对应标识l)
常规文件判断示例
你需要的「仅当路径存在且为常规文件返回真值」的逻辑可以这么实现:
using System.IO; public static bool IsRegularFile(string path) { // 先过滤不存在的路径 if (!File.Exists(path)) return false; try { var mode = File.GetUnixFileMode(path); // 按位与提取文件类型位,匹配常规文件标识 return (mode & UnixFileMode.TypeMask) == UnixFileMode.Regular; } catch (IOException) { // 无访问权限、文件被占用等异常场景返回false return false; } catch (PlatformNotSupportedException) { // Windows平台兼容逻辑,File.Exists默认仅对常规文件返回true return File.Exists(path); } }
注意:
File.GetUnixFileMode默认会跟随符号链接,如果你需要判断路径本身是否为符号链接而非指向的目标类型,可以使用FileInfo.LinkTarget属性判断,或者打开文件句柄时指定O_NOFOLLOW参数避免跟随符号链接。
通用文件类型判断封装
可以封装通用方法返回对应文件类型:
public enum UnixFileType { Regular, Directory, CharacterDevice, BlockDevice, Socket, NamedPipe, SymbolicLink, Unknown } public static UnixFileType GetUnixFileType(string path) { if (!File.Exists(path) && !Directory.Exists(path)) return UnixFileType.Unknown; try { var mode = File.GetUnixFileMode(path); var typeFlag = mode & UnixFileMode.TypeMask; return typeFlag switch { UnixFileMode.Regular => UnixFileType.Regular, UnixFileMode.Directory => UnixFileType.Directory, UnixFileMode.CharacterDevice => UnixFileType.CharacterDevice, UnixFileMode.BlockDevice => UnixFileType.BlockDevice, UnixFileMode.Socket => UnixFileType.Socket, UnixFileMode.NamedPipe => UnixFileType.NamedPipe, UnixFileMode.SymbolicLink => UnixFileType.SymbolicLink, _ => UnixFileType.Unknown }; } catch { return UnixFileType.Unknown; } }
低于.NET 5版本的兼容方案
如果使用的是.NET Core 3.1及更早版本,没有内置的GetUnixFileModeAPI,可以通过P/Invoke调用libc的stat函数实现,也是纯托管实现不需要调用外部进程:
using System.Runtime.InteropServices; // 适配x64架构Linux的stat结构体定义,其他架构需要对应调整字段顺序和长度 [StructLayout(LayoutKind.Sequential)] private struct StatBuf { public ulong st_dev; public ulong st_ino; public ulong st_nlink; public uint st_mode; public uint st_uid; public uint st_gid; public ulong st_rdev; public long st_size; public long st_blksize; public long st_blocks; public long st_atime; public long st_atime_nsec; public long st_mtime; public long st_mtime_nsec; public long st_ctime; public long st_ctime_nsec; } [DllImport("libc", SetLastError = true)] private static extern int stat(string path, out StatBuf buf); // Linux sys/stat.h中定义的文件类型掩码和标识 private const uint S_IFMT = 0xF000; private const uint S_IFREG = 0x8000; // 常规文件 private const uint S_IFDIR = 0x4000; // 目录 private const uint S_IFCHR = 0x2000; // 字符设备 private const uint S_IFBLK = 0x6000; // 块设备 private const uint S_IFIFO = 0x1000; // 命名管道 private const uint S_IFLNK = 0xA000; // 符号链接 private const uint S_IFSOCK = 0xC000; // 套接字 public static bool IsRegularFileLegacy(string path) { if (!File.Exists(path)) return false; var buf = new StatBuf(); if (stat(path, out buf) != 0) return false; return (buf.st_mode & S_IFMT) == S_IFREG; }
内容的提问来源于stack exchange,提问作者Maxim V. Pavlov
相关产品推荐
相关产品推荐

