<p>Windows 上 C# 无法设置类似 Linux 的执行权限,因 NTFS 无“x”位;可执行性由文件扩展名和系统策略决定,File.SetAttributes 仅支持 ReadOnly 等基础属性,精细权限需用 FileSecurity 操作 ACL。</p>

Windows 上 C# 无法直接设置“执行权限”
在 Windows 系统中,File.SetAttributes 或 FileSystemAclExtensions 都不支持类似 Linux 的 x(执行)位。Windows 的“可执行”由文件扩展名(如 .exe、.bat)和调用上下文决定,不是 NTFS 权限字段的一部分。试图通过 ACL 添加“Execute”权限会失败或被忽略。
常见误操作:调用 FileSystemRights.ExecuteFile 并应用到文件上——它实际只影响目录遍历或作为子对象继承模板,对普通文件无运行效果。
- 真正控制“能否运行”的是:用户是否具备读取 + 执行该文件类型所需的权限(例如对
.exe是读取+运行策略,对脚本还依赖解释器权限) - 若目标是让脚本(如
.ps1)可运行,需改组策略或签名,而非改文件 ACL - 检查当前权限是否足够,可用
Get-AclPowerShell 命令验证
用 File.SetAttributes 控制基础读写隐藏属性
File.SetAttributes 是最轻量、跨平台兼容的方式,适用于设置 ReadOnly、Hidden、Archive 等标志位,但不涉及用户/组粒度的访问控制。
示例:设为只读(阻止多数写入操作)
File.SetAttributes(@"C:\temp\config.txt", FileAttributes.ReadOnly);
还原为普通文件:
File.SetAttributes(@"C:\temp\config.txt", FileAttributes.Normal);
- 仅对当前用户有效(不改变 ACL),且某些程序(如记事本)会自动清除只读位再保存
- 不能禁用管理员写入——即使设了
ReadOnly,管理员仍可通过绕过检查修改文件 - 适用于配置文件防误删/误改,但不是安全隔离手段
用 FileSecurity 和 SetAccessControl 修改 NTFS ACL
要精细控制“谁可以读、谁可以写”,必须操作 Windows ACL,使用 File.GetAccessControl 和 File.SetAccessControl。
示例:给当前用户添加“修改”权限(读+写+删)
var fs = File.GetAccessControl(@"C:\temp\data.log"); var sid = WindowsIdentity.GetCurrent().User; fs.AddAccessRule(new FileSystemAccessRule(sid, FileSystemRights.Modify, AccessControlType.Allow)); File.SetAccessControl(@"C:\temp\data.log", fs);
- 必须以管理员身份运行程序,否则抛出
UnauthorizedAccessException - 不要直接替换整个 ACL;应先
GetAccessControl,再AddAccessRule或RemoveAccessRule,避免清空系统默认权限(如 SYSTEM、Administrators) -
FileSystemRights.Write不包含删除能力;需用Modify或显式组合Write | Delete - 对网络路径(UNC)同样生效,但要求共享权限与 NTFS 权限同时满足
Linux/macOS 上 .NET 6+ 的 Unix 文件权限支持
如果你在 Linux 或 macOS 上用 .NET 6+ 运行 C# 程序,可直接设置 rwx 位:
File.SetUnixFileMode("/tmp/script.sh", UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);注意:UnixFileMode 在 Windows 上不可用,调用会抛出 PlatformNotSupportedException。
- 该 API 仅存在于
System.IO.FileSystem(.NET 6+),旧版 .NET Framework / .NET Core 3.1 不支持 - 设置后需确保文件扩展名和 shebang(如
#!/bin/bash)正确,否则 shell 仍可能拒绝执行 - 权限变更立即生效,无需刷新进程,但已打开的文件句柄不受影响
跨平台项目里,建议先用 RuntimeInformation.IsOSPlatform(OSPlatform.Linux) 做判断,再分支处理。










