This is an automated email from the ASF dual-hosted git repository.

SpriCoder pushed a commit to branch fs/inner-view
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/fs/inner-view by this push:
     new 600b516f8ae add head
600b516f8ae is described below

commit 600b516f8aee83624b622378ff8b235be0540f8f
Author: spricoder <[email protected]>
AuthorDate: Wed Apr 29 20:09:11 2026 +0800

    add head
---
 .../org/apache/iotdb/cli/fs/FilesystemShell.java   | 17 ++++-
 .../iotdb/cli/fs/command/FilesystemCommand.java    | 27 ++++++--
 .../cli/fs/command/FilesystemCommandParser.java    | 73 +++++++++++++++++++++-
 .../org/apache/iotdb/cli/utils/JlineUtils.java     |  2 +-
 .../apache/iotdb/cli/fs/FilesystemShellTest.java   | 40 ++++++++++++
 .../fs/command/FilesystemCommandParserTest.java    | 28 +++++++++
 6 files changed, 175 insertions(+), 12 deletions(-)

diff --git 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/FilesystemShell.java 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/FilesystemShell.java
index 4c8372bb8ad..6a3cf9e976d 100644
--- 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/FilesystemShell.java
+++ 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/FilesystemShell.java
@@ -44,7 +44,7 @@ public class FilesystemShell {
   private static final int DEFAULT_READ_LIMIT = 20;
   private static final List<String> COMMANDS =
       Arrays.asList(
-          "pwd", "ls", "ll", "cd", "stat", "cat", "paste", "tree", "help", 
"exit", "quit");
+          "pwd", "ls", "ll", "cd", "stat", "cat", "head", "paste", "tree", 
"help", "exit", "quit");
 
   private final CliContext ctx;
   private final FilesystemSchemaProvider provider;
@@ -74,7 +74,10 @@ public class FilesystemShell {
         printNode(provider.describe(resolve(command.getPath())));
         return true;
       case CAT:
-        printRows(provider.read(resolve(command.getPath()), 
DEFAULT_READ_LIMIT));
+        printSequentialReads(command.getPaths(), DEFAULT_READ_LIMIT);
+        return true;
+      case HEAD:
+        printRows(provider.read(resolve(command.getPath()), 
command.getLimit()));
         return true;
       case PASTE:
         printRows(provider.read(resolve(command.getPaths()), 
DEFAULT_READ_LIMIT));
@@ -172,6 +175,12 @@ public class FilesystemShell {
     }
   }
 
+  private void printSequentialReads(List<String> paths, int limit) throws 
SQLException {
+    for (String path : paths) {
+      printRows(provider.read(resolve(path), limit));
+    }
+  }
+
   private static String joinValues(SqlRow row) {
     StringBuilder builder = new StringBuilder();
     for (String value : row.asMap().values()) {
@@ -191,8 +200,10 @@ public class FilesystemShell {
     ctx.getPrinter().println("ll [path]");
     ctx.getPrinter().println("cd <path>");
     ctx.getPrinter().println("stat [path]");
-    ctx.getPrinter().println("cat <path>");
+    ctx.getPrinter().println("cat <path>...");
+    ctx.getPrinter().println("head [-n lines] <path>");
     ctx.getPrinter().println("paste <path>...");
+    ctx.getPrinter().println("tree [-L depth] [path]");
     ctx.getPrinter().println("exit");
   }
 
diff --git 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/command/FilesystemCommand.java
 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/command/FilesystemCommand.java
index f375d008669..2cdd492fdc5 100644
--- 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/command/FilesystemCommand.java
+++ 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/command/FilesystemCommand.java
@@ -31,6 +31,7 @@ public class FilesystemCommand {
     CD,
     STAT,
     CAT,
+    HEAD,
     PASTE,
     TREE,
     SQL,
@@ -43,6 +44,7 @@ public class FilesystemCommand {
   private final String path;
   private final List<String> paths;
   private final int depth;
+  private final int limit;
   private final String statement;
   private final String errorMessage;
 
@@ -51,39 +53,48 @@ public class FilesystemCommand {
       String path,
       List<String> paths,
       int depth,
+      int limit,
       String statement,
       String errorMessage) {
     this.type = type;
     this.path = path;
     this.paths = paths;
     this.depth = depth;
+    this.limit = limit;
     this.statement = statement;
     this.errorMessage = errorMessage;
   }
 
   public static FilesystemCommand simple(Type type) {
-    return new FilesystemCommand(type, "", Collections.emptyList(), -1, "", 
"");
+    return new FilesystemCommand(type, "", Collections.emptyList(), -1, -1, 
"", "");
   }
 
   public static FilesystemCommand path(Type type, String path) {
-    return new FilesystemCommand(type, path, Collections.singletonList(path), 
-1, "", "");
+    return new FilesystemCommand(type, path, Collections.singletonList(path), 
-1, -1, "", "");
   }
 
   public static FilesystemCommand paths(Type type, List<String> paths) {
     String path = paths.isEmpty() ? "" : paths.get(0);
-    return new FilesystemCommand(type, path, 
Collections.unmodifiableList(paths), -1, "", "");
+    return new FilesystemCommand(type, path, 
Collections.unmodifiableList(paths), -1, -1, "", "");
+  }
+
+  public static FilesystemCommand head(String path, int limit) {
+    return new FilesystemCommand(
+        Type.HEAD, path, Collections.singletonList(path), -1, limit, "", "");
   }
 
   public static FilesystemCommand tree(String path, int depth) {
-    return new FilesystemCommand(Type.TREE, path, 
Collections.singletonList(path), depth, "", "");
+    return new FilesystemCommand(
+        Type.TREE, path, Collections.singletonList(path), depth, -1, "", "");
   }
 
   public static FilesystemCommand sql(String statement) {
-    return new FilesystemCommand(Type.SQL, "", Collections.emptyList(), -1, 
statement, "");
+    return new FilesystemCommand(Type.SQL, "", Collections.emptyList(), -1, 
-1, statement, "");
   }
 
   public static FilesystemCommand invalid(String errorMessage) {
-    return new FilesystemCommand(Type.INVALID, "", Collections.emptyList(), 
-1, "", errorMessage);
+    return new FilesystemCommand(
+        Type.INVALID, "", Collections.emptyList(), -1, -1, "", errorMessage);
   }
 
   public Type getType() {
@@ -102,6 +113,10 @@ public class FilesystemCommand {
     return depth;
   }
 
+  public int getLimit() {
+    return limit;
+  }
+
   public String getStatement() {
     return statement;
   }
diff --git 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/command/FilesystemCommandParser.java
 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/command/FilesystemCommandParser.java
index 5fc2021edcd..7bdab81d178 100644
--- 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/command/FilesystemCommandParser.java
+++ 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/command/FilesystemCommandParser.java
@@ -27,6 +27,7 @@ public class FilesystemCommandParser {
 
   private static final String DEFAULT_PATH = ".";
   private static final int DEFAULT_TREE_DEPTH = Integer.MAX_VALUE;
+  private static final int DEFAULT_HEAD_LIMIT = 10;
 
   private FilesystemCommandParser() {}
 
@@ -53,7 +54,7 @@ public class FilesystemCommandParser {
     String[] tokens = line.split("\\s+");
     String command = tokens[0].toLowerCase(Locale.ROOT);
     if ("ls".equals(command)) {
-      return FilesystemCommand.path(FilesystemCommand.Type.LS, 
pathArgument(tokens));
+      return parseLs(tokens);
     }
     if ("ll".equals(command)) {
       return FilesystemCommand.path(FilesystemCommand.Type.LL, 
pathArgument(tokens));
@@ -65,7 +66,10 @@ public class FilesystemCommandParser {
       return FilesystemCommand.path(FilesystemCommand.Type.STAT, 
pathArgument(tokens));
     }
     if ("cat".equals(command)) {
-      return FilesystemCommand.path(FilesystemCommand.Type.CAT, 
pathArgument(tokens));
+      return parseCat(tokens);
+    }
+    if ("head".equals(command)) {
+      return parseHead(tokens);
     }
     if ("paste".equals(command)) {
       return parsePaste(tokens);
@@ -95,6 +99,71 @@ public class FilesystemCommandParser {
     return FilesystemCommand.paths(FilesystemCommand.Type.PASTE, paths);
   }
 
+  private static FilesystemCommand parseLs(String[] tokens) {
+    FilesystemCommand.Type type = FilesystemCommand.Type.LS;
+    String path = DEFAULT_PATH;
+
+    for (int i = 1; i < tokens.length; i++) {
+      String token = tokens[i];
+      if (token.startsWith("-")) {
+        for (int j = 1; j < token.length(); j++) {
+          char option = token.charAt(j);
+          if (option == 'l') {
+            type = FilesystemCommand.Type.LL;
+          } else if (option != 'a') {
+            return FilesystemCommand.invalid("Unsupported ls option: -" + 
option);
+          }
+        }
+      } else {
+        path = token;
+      }
+    }
+    return FilesystemCommand.path(type, path);
+  }
+
+  private static FilesystemCommand parseCat(String[] tokens) {
+    if (tokens.length < 2) {
+      return FilesystemCommand.path(FilesystemCommand.Type.CAT, DEFAULT_PATH);
+    }
+    List<String> paths = new ArrayList<>();
+    for (int i = 1; i < tokens.length; i++) {
+      paths.add(tokens[i]);
+    }
+    return FilesystemCommand.paths(FilesystemCommand.Type.CAT, paths);
+  }
+
+  private static FilesystemCommand parseHead(String[] tokens) {
+    String path = DEFAULT_PATH;
+    int limit = DEFAULT_HEAD_LIMIT;
+
+    for (int i = 1; i < tokens.length; i++) {
+      String token = tokens[i];
+      if ("-n".equals(token)) {
+        if (i + 1 >= tokens.length) {
+          return FilesystemCommand.invalid("Missing head line count");
+        }
+        try {
+          limit = Integer.parseInt(tokens[++i]);
+        } catch (NumberFormatException e) {
+          return FilesystemCommand.invalid("Invalid head line count: " + 
tokens[i]);
+        }
+      } else if (token.startsWith("-") && token.length() > 1) {
+        try {
+          limit = Integer.parseInt(token.substring(1));
+        } catch (NumberFormatException e) {
+          return FilesystemCommand.invalid("Unsupported head option: " + 
token);
+        }
+      } else {
+        path = token;
+      }
+    }
+
+    if (limit < 0) {
+      return FilesystemCommand.invalid("Invalid head line count: " + limit);
+    }
+    return FilesystemCommand.head(path, limit);
+  }
+
   private static FilesystemCommand parseTree(String[] tokens) {
     String path = DEFAULT_PATH;
     int depth = DEFAULT_TREE_DEPTH;
diff --git 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/utils/JlineUtils.java 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/utils/JlineUtils.java
index b244b00ebf4..0a98529f6af 100644
--- a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/utils/JlineUtils.java
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/utils/JlineUtils.java
@@ -152,7 +152,7 @@ public class JlineUtils {
   static Completer createCompleter(String accessMode) {
     if ("filesystem".equalsIgnoreCase(accessMode)) {
       return new StringsCompleter(
-          "pwd", "ls", "ll", "cd", "stat", "cat", "paste", "tree", "help", 
"exit", "quit");
+          "pwd", "ls", "ll", "cd", "stat", "cat", "head", "paste", "tree", 
"help", "exit", "quit");
     }
     return new StringsCompleter(SQL_KEYWORDS);
   }
diff --git 
a/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/FilesystemShellTest.java
 
b/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/FilesystemShellTest.java
index e49ee0daf8a..e4ea9161a85 100644
--- 
a/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/FilesystemShellTest.java
+++ 
b/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/FilesystemShellTest.java
@@ -106,6 +106,20 @@ public class FilesystemShellTest {
     verify(provider).list(FsPath.absolute("/"));
   }
 
+  @Test
+  public void executeLsLongOptionPrintsLongListing() throws SQLException {
+    when(provider.list(FsPath.absolute("/")))
+        .thenReturn(
+            Arrays.asList(
+                new FsNode("testtest", FsPath.absolute("/testtest"), 
FsNodeType.TABLE_DATABASE)));
+
+    assertTrue(shell.execute("ls -l /"));
+
+    assertTrue(out.toString().contains("dr-xr-xr-x"));
+    assertTrue(out.toString().contains("testtest"));
+    verify(provider).list(FsPath.absolute("/"));
+  }
+
   @Test
   public void executeCdUpdatesCurrentPath() throws SQLException {
     when(provider.describe(FsPath.absolute("/root")))
@@ -153,6 +167,32 @@ public class FilesystemShellTest {
     verify(provider).read(FsPath.absolute("/db1/table1"), 20);
   }
 
+  @Test
+  public void executeCatReadsMultiplePathsSequentially() throws SQLException {
+    when(provider.read(FsPath.absolute("/db1/table1/tag1"), 20))
+        .thenReturn(Arrays.asList(SqlRow.of("Time", "1", "tag1", "a")));
+    when(provider.read(FsPath.absolute("/db1/table1/s1"), 20))
+        .thenReturn(Arrays.asList(SqlRow.of("Time", "1", "s1", "42")));
+
+    assertTrue(shell.execute("cat /db1/table1/tag1 /db1/table1/s1"));
+
+    assertTrue(out.toString().contains("1\ta"));
+    assertTrue(out.toString().contains("1\t42"));
+    verify(provider).read(FsPath.absolute("/db1/table1/tag1"), 20);
+    verify(provider).read(FsPath.absolute("/db1/table1/s1"), 20);
+  }
+
+  @Test
+  public void executeHeadReadsPathWithLimit() throws SQLException {
+    when(provider.read(FsPath.absolute("/db1/table1"), 5))
+        .thenReturn(Arrays.asList(SqlRow.of("Time", "1", "tag1", "a", "s1", 
"42")));
+
+    assertTrue(shell.execute("head -n 5 /db1/table1"));
+
+    assertTrue(out.toString().contains("1\ta\t42"));
+    verify(provider).read(FsPath.absolute("/db1/table1"), 5);
+  }
+
   @Test
   public void executePasteReadsMultiplePaths() throws SQLException {
     when(provider.read(
diff --git 
a/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/command/FilesystemCommandParserTest.java
 
b/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/command/FilesystemCommandParserTest.java
index 360e5cbe593..2816912ab0b 100644
--- 
a/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/command/FilesystemCommandParserTest.java
+++ 
b/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/command/FilesystemCommandParserTest.java
@@ -43,6 +43,14 @@ public class FilesystemCommandParserTest {
     assertEquals("/db1", command.getPath());
   }
 
+  @Test
+  public void parseLsLongOptionAsLongListCommand() {
+    FilesystemCommand command = FilesystemCommandParser.parse("ls -la /db1");
+
+    assertEquals(FilesystemCommand.Type.LL, command.getType());
+    assertEquals("/db1", command.getPath());
+  }
+
   @Test
   public void parsePathCommand() {
     FilesystemCommand command = FilesystemCommandParser.parse("  ls   /root/sg 
 ");
@@ -59,6 +67,26 @@ public class FilesystemCommandParserTest {
     assertEquals("/db1/table1", command.getPath());
   }
 
+  @Test
+  public void parseCatMultiplePaths() {
+    FilesystemCommand command =
+        FilesystemCommandParser.parse("cat /db1/table1/tag1 /db1/table1/s1");
+
+    assertEquals(FilesystemCommand.Type.CAT, command.getType());
+    assertEquals(2, command.getPaths().size());
+    assertEquals("/db1/table1/tag1", command.getPaths().get(0));
+    assertEquals("/db1/table1/s1", command.getPaths().get(1));
+  }
+
+  @Test
+  public void parseHeadLimitAndPath() {
+    FilesystemCommand command = FilesystemCommandParser.parse("head -n 5 
/db1/table1");
+
+    assertEquals(FilesystemCommand.Type.HEAD, command.getType());
+    assertEquals("/db1/table1", command.getPath());
+    assertEquals(5, command.getLimit());
+  }
+
   @Test
   public void parsePastePaths() {
     FilesystemCommand command =

Reply via email to