首頁 > 軟體

通過Java實現自己動手寫ls命令

2022-10-12 14:01:52

介紹

在前面的文章Linux命令系列之ls——原來最簡單的ls這麼複雜當中,我們仔細的介紹了關於ls命令的使用和輸出結果,在本篇文章當中我們用Java程式碼自己實現ls命令,更加深入的瞭解ls命令。

程式碼實現

檔案操作的基本原理

如果我們使用Java實現一個簡單的ls命令其實並不難,因為Java已經給我們提供了一些比較方便和檔案系統相關的api了,困難的是理解api是在做什麼事兒!

事實上這些api都是作業系統給我們提供的,然後Java進行了一些列的封裝,將這些操作給我們進行提供,我們仔細來看一下封裝的層次,首先作業系統會給我們提供很多系統呼叫用於和裝置(磁碟、CPU)進行互動,比如說和檔案的互動就是讀寫資料,當然我們的Java程式也需要這些操作,因此JVM也需要給我們提供這些操作,因此JVM就對系統呼叫進行了一系列的封裝,在Java當中具體的形式就是用native修飾的方法。

如果你是一個比較有經驗Java程式設計師那麼一定見過Java當中的native方法,這些方法都是Java給我們封裝的底層介面,比如說在FileInputStream當中有一個read方法,這個方法就是讀取檔案當中的內容,我們看一下這個方法是如何實現的:

    public int read() throws IOException {
        return read0();
    }

這裡讓大家的感受更加深入一點,我在這裡貼一張FileInputStream的原始碼圖片:

從上面的圖看當我們呼叫FileInputStream方法的時候確實呼叫了native方法。我們再來看一些與檔案操作相關的api,他們也是使用Java給我們封裝的native方法實現的。

上面主要談了一些基本的檔案操作過程的原理,簡要說明了Java將很多系統操作封裝成native方法供我們呼叫,現在我們來看看要想實現ls命令,我們需要哪些api。

檢視一個目錄下面有哪些檔案和目錄

在Java當中給我們提供了一個類File,我們可以使用這個類去得到一個目錄下面有哪些檔案和目錄。

  public void fileTest() {
    File file = new File("./");
    // file.listFiles() 將當前 file 對應的目錄下所有的檔案和目錄都得到
    for (File listFile : file.listFiles()) {
      System.out.println(listFile.getName()); // 將檔案或者目錄的名字列印
    }

檢視檔案和目錄的後設資料

在Java當中給我們提供了一個工具類檢視檔案的一些元資訊(metadata),比如說檔案的uid(使用者id)、gid(使用者組id)、檔案的大小和檔案的連結數目(nlink)。

  Path path = Paths.get(".");
  System.out.println(Files.getAttribute(path, "unix:dev")); // 列印儲存當前目錄資料的裝置的裝置id
  System.out.println(Files.getAttribute(path, "unix:ino")); // 列印儲存當前目錄資料inode號
  System.out.println(Files.getAttribute(path, "unix:mode"));// 列印儲存當前目錄資料的mode資料 這個資料主要用於表示檔案的型別
  System.out.println(Files.getAttribute(path, "unix:uid")); // 列印儲存當前目錄所屬使用者的使用者id
  System.out.println(Files.getAttribute(path, "unix:gid")); // 列印儲存當前目錄所屬組的組id
  System.out.println(Files.getAttribute(path, "unix:size"));// 列印儲存當前目錄資料所佔的空間大小
  System.out.println(Files.getAttribute(path, "unix:nlink"));// 列印儲存當前目錄資料的連結數

除了上面的方式,我們還可以使用下面的方式去得到檔案的後設資料:

  public void attrTest02() throws IOException {
    Path path = Paths.get("."); // 傳入的引數就是檔案或者目錄的路徑 這個傳入的就是當前目錄
    PosixFileAttributes attr = Files.readAttributes(path, PosixFileAttributes.class, NOFOLLOW_LINKS);
    System.out.println(attr.owner()); // 列印使用者名稱
    System.out.println(attr.group()); // 列印使用者組名
    System.out.println(attr.isRegularFile()); // 是不是一般檔案
    System.out.println(attr.isSymbolicLink()); // 是不是一個符號連結
    System.out.println(attr.isDirectory()); // 是否是目錄
    System.out.println(attr.isOther()); //其他型別
    System.out.println(attr.permissions()); // 列印檔案的許可權 是否可讀 可寫 可執行
    System.out.println(attr.lastAccessTime()); // 上一次存取時間
    System.out.println(attr.creationTime()); // 建立時間
    System.out.println(attr.lastModifiedTime()); // 上一次修改時間
    System.out.println(attr.fileKey()); // 列印檔案其他相關引數 主要是裝置id和inode編號
    System.out.println(attr.size()); // 檔案的大小
  }
root // 這裡是使用者名稱
root // 這裡是使用者組名
false
false
true
false
[GROUP_READ, OTHERS_EXECUTE, OWNER_WRITE, OWNER_EXECUTE, OTHERS_READ, OWNER_READ, GROUP_EXECUTE]
2022-10-09T18:08:47.791072133Z
2022-10-09T13:10:51Z
2022-10-09T18:08:23.746949182Z
(dev=1000012,ino=16176823)
192

檔案許可權

在Java當中給我們提供了一個類表示檔案的9中許可權(檔案的作者的讀寫執行,作者所在組的讀寫執行,和其他人的讀寫執行,一共九種許可權):

package java.nio.file.attribute;

public enum PosixFilePermission {

    /**
     * Read permission, owner. 作者讀許可權
     */
    OWNER_READ,

    /**
     * Write permission, owner. 作者寫許可權
     */
    OWNER_WRITE,

    /**
     * Execute/search permission, owner. 作者的執行許可權
     */
    OWNER_EXECUTE,

    /**
     * Read permission, group. 作者所在組的讀許可權
     */
    GROUP_READ,

    /**
     * Write permission, group.作者所在組的寫許可權
     */
    GROUP_WRITE,

    /**
     * Execute/search permission, group.
     */
    GROUP_EXECUTE,

    /**
     * Read permission, others. 其他人讀許可權
     */
    OTHERS_READ,

    /**
     * Write permission, others. 其他人寫許可權
     */
    OTHERS_WRITE,

    /**
     * Execute/search permission, others. 其他人執行許可權
     */
    OTHERS_EXECUTE;
}

在上面檢視檔案或者目錄的後設資料的時候我們已經得到的檔案的所有許可權資訊:

System.out.println(attr.permissions());
//[GROUP_READ, OTHERS_EXECUTE, OWNER_WRITE, OWNER_EXECUTE, OTHERS_READ, OWNER_READ, GROUP_EXECUTE]

函數返回的是一個集合set,裡面存放的就是檔案的各種許可權的資訊,比如在我們的例子當中我們可以看到,有組讀,其他人執行,作者自己寫,作者執行,其他人讀,作者讀許可權,如果我們想判斷某種許可權,只需要看看集合當中是否包含即可。

完整程式碼實現

在上面我們已經談到了所有的關於實現 ls 命令的細節了,接下來看一下我們的程式碼實現:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Objects;
import java.util.Set;

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;

public class LS {

  public static boolean hasRight(Set<PosixFilePermission> set, PosixFilePermission
                                 permission) {
    return set.contains(permission);
  }

  public static void echoCharacter(Set<PosixFilePermission> set) {
    // user
    if (hasRight(set, PosixFilePermission.OWNER_READ))
      System.out.print('r');
      else
      System.out.print('-');
    if (hasRight(set, PosixFilePermission.OWNER_WRITE))
      System.out.print('w');
    else
      System.out.print('-');
    if (hasRight(set, PosixFilePermission.OWNER_EXECUTE))
      System.out.print('x');
    else
      System.out.print('-');

    // group
    if (hasRight(set, PosixFilePermission.GROUP_READ))
      System.out.print('r');
    else
      System.out.print('-');
    if (hasRight(set, PosixFilePermission.GROUP_WRITE))
      System.out.print('w');
    else
      System.out.print('-');
    if (hasRight(set, PosixFilePermission.GROUP_EXECUTE))
      System.out.print('x');
    else
      System.out.print('-');

    // others
    if (hasRight(set, PosixFilePermission.OTHERS_READ))
      System.out.print('r');
    else
      System.out.print('-');
    if (hasRight(set, PosixFilePermission.OTHERS_WRITE))
      System.out.print('w');
    else
      System.out.print('-');
    if (hasRight(set, PosixFilePermission.OTHERS_EXECUTE))
      System.out.print('x');
    else
      System.out.print('-');
  }

  public static void echoType(PosixFileAttributes attributes) {
    if (attributes.isDirectory())
      System.out.print('d');
    else if (attributes.isRegularFile())
      System.out.print('-');
    else if (attributes.isSymbolicLink())
      System.out.print('l');
    else
      System.out.print('o');
  }

  public static void echoFileInformation(String args) throws IOException {
    Path path = Paths.get(args);
    PosixFileAttributes attributes = Files.readAttributes(path, PosixFileAttributes.class, NOFOLLOW_LINKS);
    echoType(attributes);
    echoCharacter(attributes.permissions());

    System.out.printf("t%-2d", Files.getAttribute(path, "unix:nlink"));
    System.out.print("t" + attributes.owner().getName());
    System.out.print("t" + attributes.group().getName());
    System.out.printf("t%-5d", attributes.size());
    System.out.printf("t %10s", attributes.lastAccessTime());
    System.out.println("t" + path.getFileName());
  }

  public static void main(String[] args) throws IOException {

    File file = new File(args[0]);
    for (File listFile : Objects.requireNonNull(file.listFiles())) {
      echoFileInformation(listFile.toString());
    }
  }
}

上面的程式碼很短,如果大家瞭解了上main所談到的api的話,就應該很容易理解了。下面我們看看程式的輸出結果:

可以看到我們的程式的輸出結果和ls命令的輸出結果是一樣的,只是在時間的表示上有所差別而已,這一點沒什麼關係。

以上就是通過Java實現自己動手寫ls命令的詳細內容,更多關於Java實現ls命令的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com