385. Java IO API - Chmod 示例:模拟 chmod 命令的文件权限更改

385. Java IO API - Chmod 示例:模拟 chmod 命令的文件权限更改

在这个示例中,我们将展示如何通过 Java 更改文件和目录的权限,类似于 UNIX 系统中的 chmod 命令。代码实现使用了 java.nio.file 包中的 API,特别是 PosixFilePermissionFileVisitor

java 复制代码
import java.nio.file.*;
import java.nio.file.attribute.*;
import static java.nio.file.attribute.PosixFilePermission.*;
import static java.nio.file.FileVisitResult.*;
import java.io.IOException;
import java.util.*;

/**
 * Sample code that changes the permissions of files in a similar manner to the
 * chmod(1) program.
 */

public class Chmod {

    /**
     * Compiles a list of one or more <em>symbolic mode expressions</em> that
     * may be used to change a set of file permissions. This method is
     * intended for use where file permissions are required to be changed in
     * a manner similar to the UNIX <i>chmod</i> program.
     *
     * <p> The {@code exprs} parameter is a comma separated list of expressions
     * where each takes the form:
     * <blockquote>
     * <i>who operator</i> [<i>permissions</i>]
     * </blockquote>
     * where <i>who</i> is one or more of the characters {@code 'u'}, {@code 'g'},
     * {@code 'o'}, or {@code 'a'} meaning the owner (user), group, others, or
     * all (owner, group, and others) respectively.
     *
     * <p> <i>operator</i> is the character {@code '+'}, {@code '-'}, or {@code
     * '='} signifying how permissions are to be changed. {@code '+'} means the
     * permissions are added, {@code '-'} means the permissions are removed, and
     * {@code '='} means the permissions are assigned absolutely.
     *
     * <p> <i>permissions</i> is a sequence of zero or more of the following:
     * {@code 'r'} for read permission, {@code 'w'} for write permission, and
     * {@code 'x'} for execute permission. If <i>permissions</i> is omitted
     * when assigned absolutely, then the permissions are cleared for
     * the owner, group, or others as identified by <i>who</i>. When omitted
     * when adding or removing then the expression is ignored.
     *
     * <p> The following examples demonstrate possible values for the {@code
     * exprs} parameter:
     *
     * <table border="0">
     * <tr>
     *   <td> {@code u=rw} </td>
     *   <td> Sets the owner permissions to be read and write. </td>
     * </tr>
     * <tr>
     *   <td> {@code ug+w} </td>
     *   <td> Sets the owner write and group write permissions. </td>
     * </tr>
     * <tr>
     *   <td> {@code u+w,o-rwx} </td>
     *   <td> Sets the owner write, and removes the others read, others write
     *     and others execute permissions. </td>
     * </tr>
     * <tr>
     *   <td> {@code o=} </td>
     *   <td> Sets the others permission to none (others read, others write and
     *     others execute permissions are removed if set) </td>
     * </tr>
     * </table>
     *
     * @param   exprs
     *          List of one or more <em>symbolic mode expressions</em>
     *
     * @return  A {@code Changer} that may be used to changer a set of
     *          file permissions
     *
     * @throws  IllegalArgumentException
     *          If the value of the {@code exprs} parameter is invalid
     */
    public static Changer compile(String exprs) {
        // minimum is who and operator (u= for example)
        if (exprs.length() < 2)
            throw new IllegalArgumentException("Invalid mode");

        // permissions that the changer will add or remove
        final Set<PosixFilePermission> toAdd = new HashSet<PosixFilePermission>();
        final Set<PosixFilePermission> toRemove = new HashSet<PosixFilePermission>();

        // iterate over each of expression modes
        for (String expr: exprs.split(",")) {
            // minimum of who and operator
            if (expr.length() < 2)
                throw new IllegalArgumentException("Invalid mode");

            int pos = 0;

            // who
            boolean u = false;
            boolean g = false;
            boolean o = false;
            boolean done = false;
            for (;;) {
                switch (expr.charAt(pos)) {
                    case 'u' : u = true; break;
                    case 'g' : g = true; break;
                    case 'o' : o = true; break;
                    case 'a' : u = true; g = true; o = true; break;
                    default : done = true;
                }
                if (done)
                    break;
                pos++;
            }
            if (!u && !g && !o)
                throw new IllegalArgumentException("Invalid mode");

            // get operator and permissions
            char op = expr.charAt(pos++);
            String mask = (expr.length() == pos) ? "" : expr.substring(pos);

            // operator
            boolean add = (op == '+');
            boolean remove = (op == '-');
            boolean assign = (op == '=');
            if (!add && !remove && !assign)
                throw new IllegalArgumentException("Invalid mode");

            // who= means remove all
            if (assign && mask.length() == 0) {
                assign = false;
                remove = true;
                mask = "rwx";
            }

            // permissions
            boolean r = false;
            boolean w = false;
            boolean x = false;
            for (int i=0; i<mask.length(); i++) {
                switch (mask.charAt(i)) {
                    case 'r' : r = true; break;
                    case 'w' : w = true; break;
                    case 'x' : x = true; break;
                    default:
                        throw new IllegalArgumentException("Invalid mode");
                }
            }

            // update permissions set
            if (add) {
                if (u) {
                    if (r) toAdd.add(OWNER_READ);
                    if (w) toAdd.add(OWNER_WRITE);
                    if (x) toAdd.add(OWNER_EXECUTE);
                }
                if (g) {
                    if (r) toAdd.add(GROUP_READ);
                    if (w) toAdd.add(GROUP_WRITE);
                    if (x) toAdd.add(GROUP_EXECUTE);
                }
                if (o) {
                    if (r) toAdd.add(OTHERS_READ);
                    if (w) toAdd.add(OTHERS_WRITE);
                    if (x) toAdd.add(OTHERS_EXECUTE);
                }
            }
            if (remove) {
                if (u) {
                    if (r) toRemove.add(OWNER_READ);
                    if (w) toRemove.add(OWNER_WRITE);
                    if (x) toRemove.add(OWNER_EXECUTE);
                }
                if (g) {
                    if (r) toRemove.add(GROUP_READ);
                    if (w) toRemove.add(GROUP_WRITE);
                    if (x) toRemove.add(GROUP_EXECUTE);
                }
                if (o) {
                    if (r) toRemove.add(OTHERS_READ);
                    if (w) toRemove.add(OTHERS_WRITE);
                    if (x) toRemove.add(OTHERS_EXECUTE);
                }
            }
            if (assign) {
                if (u) {
                    if (r) toAdd.add(OWNER_READ);
                      else toRemove.add(OWNER_READ);
                    if (w) toAdd.add(OWNER_WRITE);
                      else toRemove.add(OWNER_WRITE);
                    if (x) toAdd.add(OWNER_EXECUTE);
                      else toRemove.add(OWNER_EXECUTE);
                }
                if (g) {
                    if (r) toAdd.add(GROUP_READ);
                      else toRemove.add(GROUP_READ);
                    if (w) toAdd.add(GROUP_WRITE);
                      else toRemove.add(GROUP_WRITE);
                    if (x) toAdd.add(GROUP_EXECUTE);
                      else toRemove.add(GROUP_EXECUTE);
                }
                if (o) {
                    if (r) toAdd.add(OTHERS_READ);
                      else toRemove.add(OTHERS_READ);
                    if (w) toAdd.add(OTHERS_WRITE);
                      else toRemove.add(OTHERS_WRITE);
                    if (x) toAdd.add(OTHERS_EXECUTE);
                      else toRemove.add(OTHERS_EXECUTE);
                }
            }
        }

        // return changer
        return new Changer() {
            @Override
            public Set<PosixFilePermission> change(Set<PosixFilePermission> perms) {
                perms.addAll(toAdd);
                perms.removeAll(toRemove);
                return perms;
            }
        };
    }

    /**
     * A task that <i>changes</i> a set of {@link PosixFilePermission} elements.
     */
    public interface Changer {
        /**
         * Applies the changes to the given set of permissions.
         *
         * @param   perms
         *          The set of permissions to change
         *
         * @return  The {@code perms} parameter
         */
        Set<PosixFilePermission> change(Set<PosixFilePermission> perms);
    }

    /**
     * Changes the permissions of the file using the given Changer.
     */
    static void chmod(Path file, Changer changer) {
        try {
            Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);
            Files.setPosixFilePermissions(file, changer.change(perms));
        } catch (IOException x) {
            System.err.println(x);
        }
    }

    /**
     * Changes the permission of each file and directory visited
     */
    static class TreeVisitor implements FileVisitor<Path> {
        private final Changer changer;

        TreeVisitor(Changer changer) {
            this.changer = changer;
        }

        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
            chmod(dir, changer);
            return CONTINUE;
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            chmod(file, changer);
            return CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
            if (exc != null)
                System.err.println("WARNING: " + exc);
            return CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) {
            System.err.println("WARNING: " + exc);
            return CONTINUE;
        }
    }

    static void usage() {
        System.err.println("java Chmod [-R] symbolic-mode-list file...");
        System.exit(-1);
    }

    public static void main(String[] args) throws IOException {
        if (args.length < 2)
            usage();
        int argi = 0;
        int maxDepth = 0;
        if (args[argi].equals("-R")) {
            if (args.length < 3)
                usage();
            argi++;
            maxDepth = Integer.MAX_VALUE;
        }

        // compile the symbolic mode expressions
        Changer changer = compile(args[argi++]);
        TreeVisitor visitor = new TreeVisitor(changer);

        Set<FileVisitOption> opts = Collections.emptySet();
        while (argi < args.length) {
            Path file = Paths.get(args[argi]);
            Files.walkFileTree(file, opts, maxDepth, visitor);
            argi++;
        }
    }
}
代码结构说明
  1. compile 方法: 这个方法接受一个符号模式表达式(symbolic mode expression),并生成一个 Changer 对象,用于更改文件权限。符号模式表达式的格式如下:

    • who(谁):指定权限应用的对象,可以是 u(用户)、g(组)、o(其他用户)或 a(所有用户)。
    • operator(操作符):+(添加权限)、-(移除权限)、=(设置绝对权限)。
    • permissions(权限):r(读)、w(写)、x(执行)。

    例如:

    • u=rw:设置用户(owner)具有读和写权限。
    • ug+w:设置用户和组添加写权限。
    • o-:移除其他用户的所有权限。
  2. Changer 接口: Changer 接口的 change 方法接受当前权限的集合,并返回一个新的权限集合,应用了 compile 方法中定义的更改。

  3. chmod 方法: 这个方法用来实际改变文件的权限。它首先获取当前文件的权限,然后调用 Changer 对象的 change 方法来更改权限,最后将修改后的权限设置回文件。

  4. TreeVisitor 类: TreeVisitor 实现了 FileVisitor 接口,它会遍历目录树中的每个文件,并对每个文件或目录应用权限更改。preVisitDirectoryvisitFile 方法会在访问目录或文件时调用 chmod 方法修改其权限。

  5. main 方法: main 方法是程序的入口,它解析命令行参数,调用 compile 方法生成权限更改器,并使用 FileVisitor 遍历指定目录中的所有文件。

示例代码解释
java 复制代码
public class Chmod {
    public static Changer compile(String exprs) {
        // 验证表达式的格式
        // ...
        
        // 解析权限更改的集合
        // ...

        // 返回权限更改器
        return new Changer() {
            @Override
            public Set<PosixFilePermission> change(Set<PosixFilePermission> perms) {
                perms.addAll(toAdd);    // 添加权限
                perms.removeAll(toRemove);  // 移除权限
                return perms;
            }
        };
    }

    static void chmod(Path file, Changer changer) {
        try {
            // 获取文件当前的权限
            Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);
            // 应用权限更改
            Files.setPosixFilePermissions(file, changer.change(perms));
        } catch (IOException e) {
            System.err.println(e);
        }
    }

    // 访问文件树时调用的文件访问器
    static class TreeVisitor implements FileVisitor<Path> {
        private final Changer changer;

        TreeVisitor(Changer changer) {
            this.changer = changer;
        }

        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
            chmod(dir, changer); // 修改目录权限
            return CONTINUE;
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            chmod(file, changer); // 修改文件权限
            return CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
            if (exc != null)
                System.err.println("警告: " + exc);
            return CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) {
            System.err.println("警告: " + exc);
            return CONTINUE;
        }
    }

    // 主函数,解析参数并启动程序
    public static void main(String[] args) throws IOException {
        if (args.length < 2) {
            System.err.println("用法:java Chmod [-R] symbolic-mode-list file...");
            System.exit(-1);
        }
        int argi = 0;
        int maxDepth = 0;
        if (args[argi].equals("-R")) {  // 如果指定了 -R 参数,递归修改权限
            if (args.length < 3)
                System.exit(-1);
            argi++;
            maxDepth = Integer.MAX_VALUE;
        }

        // 编译符号模式表达式
        Changer changer = compile(args[argi++]);
        TreeVisitor visitor = new TreeVisitor(changer);

        Set<FileVisitOption> opts = Collections.emptySet();
        while (argi < args.length) {
            Path file = Paths.get(args[argi]);
            // 遍历目录并应用权限更改
            Files.walkFileTree(file, opts, maxDepth, visitor);
            argi++;
        }
    }
}
使用示例

假设我们希望更改某个文件或目录的权限,类似于 UNIX 中的 chmod 命令:

java 复制代码
java Chmod u=rw file.txt

这会将 file.txt 的所有者权限设置为可读和可写。

如果要递归地更改整个目录树的权限,可以使用 -R 参数:

java 复制代码
java Chmod -R u+rwx,go-rwx /path/to/directory

这将递归地为 /path/to/directory 中的所有文件和子目录设置所有者(user)具有读、写和执行权限,同时移除组(group)和其他用户(others)的所有权限。

注意事项
  1. 递归操作: -R 参数可以让权限更改应用到目录中的所有文件和子目录。
  2. 权限管理: 使用 +-= 操作符,可以灵活地控制权限的添加、删除或重置。
相关推荐
陆枫Larry2 小时前
搞懂 package.json 和 package-lock.json
前端
竹林8182 小时前
Solana前端开发:从连接钱包到发送交易,我如何用@solana/web3.js搞定第一个DApp
前端·javascript
沙振宇2 小时前
【Web】使用Vue3+PlayCanvas开发3D游戏(十一)渲染3D高斯泼溅效果
前端·游戏·3d
星浩AI2 小时前
手把手带你跑通智能体 A2A 实战案例
后端·langchain·agent
cool32002 小时前
4D实验八:Dubbo微服务 + 注册中心
前端·kubernetes
军军君012 小时前
数字孪生监控大屏实战模板:商圈大数据监控
前端·javascript·vue.js·typescript·前端框架·echarts·three
j_xxx404_2 小时前
Linux:缓冲区
linux·运维·c++·后端
希望永不加班2 小时前
SpringBoot 中 AOP 实现接口限流
java·spring boot·后端·spring