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

paulk pushed a commit to branch GROOVY_5_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 83a1361413bd318c880b66830f743d1540b0d94d
Author: Paul King <[email protected]>
AuthorDate: Mon Aug 25 13:22:51 2025 +1000

    GROOVY-11742: posix commands should support variable assignment (amendments 
for /cat and /head)
---
 .../groovy/org/apache/groovy/groovysh/Main.groovy  |  13 +-
 .../groovy/groovysh/jline/GroovyPosixCommands.java | 137 +++++++++++++++------
 2 files changed, 110 insertions(+), 40 deletions(-)

diff --git 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Main.groovy
 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Main.groovy
index d9865599fe..3d07e07472 100644
--- 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Main.groovy
+++ 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Main.groovy
@@ -82,7 +82,7 @@ import static org.jline.jansi.AnsiRenderer.render
 class Main {
     private static final MessageSource messages = new MessageSource(Main)
     public static final String INTERPRETER_MODE_PREFERENCE_KEY = 
'interpreterMode'
-    private static POSIX_FILE_CMDS = ['/tail', '/head', '/wc', '/sort']
+    private static POSIX_FILE_CMDS = ['/tail', '/wc', '/sort']
 
     @SuppressWarnings("resource")
     protected static class ExtraConsoleCommands extends JlineCommandRegistry 
implements CommandRegistry {
@@ -113,6 +113,7 @@ class Main {
                 '/echo' : new CommandMethods((Function) this::echo, 
this::defaultCompleter),
                 '/ls'   : new CommandMethods((Function) this::ls, 
this::optFileCompleter),
                 '/grep' : new CommandMethods((Function) this::grepcmd, 
this::optFileCompleter),
+                '/head' : new CommandMethods((Function) this::headcmd, 
this::optFileCompleter),
                 '/cat'  : new CommandMethods((Function) this::cat, 
this::optFileCompleter),
                 "/!"    : new CommandMethods((Function) this::shell, 
this::defaultCompleter)
             ]
@@ -190,6 +191,14 @@ class Main {
             }
         }
 
+        private void headcmd(CommandInput input) {
+            try {
+                GroovyPosixCommands.head(context(input), ['/head', 
*input.xargs()] as Object[])
+            } catch (Exception e) {
+                saveException(e)
+            }
+        }
+
         private GroovyPosixContext context(CommandInput input) {
             GroovyPosixContext ctx = new GroovyPosixContext(input.in(), 
input.out(), input.err(),
                 posix.context.currentDir(), input.terminal(), 
scriptEngine::get)
@@ -198,7 +207,7 @@ class Main {
 
         private void cat(CommandInput input) {
             try {
-                GroovyPosixCommands.cat(context(input), ['/cat', 
*input.args()] as String[])
+                GroovyPosixCommands.cat(context(input), ['/cat', 
*input.xargs()] as Object[])
             } catch (Exception e) {
                 saveException(e)
             }
diff --git 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixCommands.java
 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixCommands.java
index 3686e2cd9c..5cde0fcc24 100644
--- 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixCommands.java
+++ 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixCommands.java
@@ -76,10 +76,10 @@ import java.util.stream.Stream;
 // https://github.com/jline/jline3/pull/1390
 public class GroovyPosixCommands extends PosixCommands {
 
-    public static void cat(Context context, String[] argv) throws Exception {
+    public static void cat(Context context, Object[] argv) throws Exception {
         final String[] usage = {
-            "/cat - concatenate and print FILES",
-            "Usage: /cat [OPTIONS] [FILES]",
+            "/cat - concatenate and print FILES or VARIABLES",
+            "Usage: /cat [OPTIONS] [FILES] [VARIABLES]",
             "  -? --help                show help",
             "  -n                       number the output lines, starting at 1"
         };
@@ -89,28 +89,14 @@ public class GroovyPosixCommands extends PosixCommands {
         if (args.isEmpty()) {
             args = Collections.singletonList("-");
         }
-        List<InputStream> expanded = new ArrayList<>();
-        for (String arg : args) {
-            if ("-".equals(arg)) {
-                expanded.add(context.in());
-            } else {
-                expanded.addAll(maybeExpandGlob(context, arg).stream()
-                    .map(p -> {
-                        try {
-                            return p.toUri().toURL().openStream();
-                        } catch (IOException e) {
-                            throw new RuntimeException(e);
-                        }
-                    })
-                    .collect(Collectors.toList()));
-            }
-        }
-        for (InputStream is : expanded) {
-            cat(context, new BufferedReader(new InputStreamReader(is)), 
opt.isSet("n"));
+        List<NamedInputStream> sources = getSources(context, argv, args);
+        for (NamedInputStream nis : sources) {
+            InputStream is = nis.getInputStream();
+            doCat(context, new BufferedReader(new InputStreamReader(is)), 
opt.isSet("n"));
         }
     }
 
-    private static void cat(Context context, BufferedReader reader, boolean 
numbered) throws IOException {
+    private static void doCat(Context context, BufferedReader reader, boolean 
numbered) throws IOException {
         String line;
         int lineno = 1;
         try {
@@ -126,6 +112,75 @@ public class GroovyPosixCommands extends PosixCommands {
         }
     }
 
+    public static void head(Context context, Object[] argv) throws Exception {
+        final String[] usage = {
+            "/head - display first lines of files or variables",
+            "Usage: /head [-n lines | -c bytes] [file|variable ...]",
+            "  -? --help                    Show help",
+            "  -n --lines=LINES             Print line counts",
+            "  -c --bytes=BYTES             Print byte counts",
+        };
+        Options opt = parseOptions(context, usage, argv);
+
+        if (opt.isSet("lines") && opt.isSet("bytes")) {
+            throw new IllegalArgumentException("usage: head [-n # | -c #] 
[file ...]");
+        }
+
+        int nbLines = Integer.MAX_VALUE;
+        int nbBytes = Integer.MAX_VALUE;
+        if (opt.isSet("lines")) {
+            nbLines = opt.getNumber("lines");
+        } else if (opt.isSet("bytes")) {
+            nbBytes = opt.getNumber("bytes");
+        } else {
+            nbLines = 10; // default
+        }
+
+        List<String> args = opt.args();
+        if (args.isEmpty()) {
+            args = Collections.singletonList("-");
+        }
+
+        boolean first = true;
+        List<NamedInputStream> sources = getSources(context, argv, args);
+        for (NamedInputStream nis : sources) {
+            if (!first && args.size() > 1) {
+                context.out().println();
+            }
+            if (args.size() > 1) {
+                context.out().println("==> " + nis.getName() + " <==");
+            }
+
+            InputStream is = nis.getInputStream();
+            if (nbLines != Integer.MAX_VALUE) {
+                try (BufferedReader reader = new BufferedReader(new 
InputStreamReader(is))) {
+                    String line;
+                    int count = 0;
+                    while ((line = reader.readLine()) != null && count < 
nbLines) {
+                        context.out().println(line);
+                        count++;
+                    }
+                }
+            } else {
+                byte[] buffer = new byte[nbBytes];
+                int bytesRead = is.read(buffer);
+                if (bytesRead > 0) {
+                    context.out().write(buffer, 0, bytesRead);
+                }
+                is.close();
+            }
+            first = false;
+        }
+    }
+
+    private static InputStream newInputStream(Path p) {
+        try {
+            return Files.newInputStream(p);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     public static void ls(Context context, String[] argv) throws Exception {
         final String[] usage = {
             "/ls - list files",
@@ -583,18 +638,7 @@ public class GroovyPosixCommands extends PosixCommands {
         if (args.isEmpty()) {
             args.add("-");
         }
-        List<GrepSource> sources = new ArrayList<>();
-        for (String arg : args) {
-            if ("-".equals(arg)) {
-                sources.add(new GrepSource(context.in(), "(standard input)"));
-            } else if (arg.startsWith("[Ljava.lang.String;@")) {
-                sources.add(new GrepSource(variableInputStream(argv, arg), 
arg));
-            } else {
-                sources.addAll(maybeExpandGlob(context, arg).stream()
-                    .map(gp -> new GrepSource(gp, gp.toString()))
-                    .collect(Collectors.toList()));
-            }
-        }
+        List<NamedInputStream> sources = getSources(context, argv, args);
         boolean filenameHeader = sources.size() > 1;
         if (opt.isSet("header")) {
             filenameHeader = true;
@@ -602,7 +646,7 @@ public class GroovyPosixCommands extends PosixCommands {
             filenameHeader = false;
         }
         boolean match = false;
-        for (GrepSource src : sources) {
+        for (NamedInputStream src : sources) {
             List<String> lines = new ArrayList<>();
             boolean firstPrint = true;
             int nb = 0;
@@ -763,6 +807,22 @@ public class GroovyPosixCommands extends PosixCommands {
         }
     }
 
+    private static List<NamedInputStream> getSources(Context context, Object[] 
argv, List<String> args) {
+        List<NamedInputStream> sources = new ArrayList<>();
+        for (String arg : args) {
+            if ("-".equals(arg)) {
+                sources.add(new NamedInputStream(context.in(), "(standard 
input)"));
+            } else if (arg.startsWith("[Ljava.lang.String;@")) {
+                sources.add(new NamedInputStream(variableInputStream(argv, 
arg), arg));
+            } else {
+                sources.addAll(maybeExpandGlob(context, arg).stream()
+                    .map(gp -> new NamedInputStream(newInputStream(gp), 
gp.toString()))
+                    .collect(Collectors.toList()));
+            }
+        }
+        return sources;
+    }
+
     private static ByteArrayInputStream variableInputStream(Object[] argv, 
String arg) {
         String[] found = (String[]) Arrays.stream(argv).filter(v -> 
v.toString().equals(arg)).findFirst().get();
         ByteArrayInputStream inputStream = new 
ByteArrayInputStream(ArrayGroovyMethods.join(found, 
"\n").getBytes(StandardCharsets.UTF_8));
@@ -808,18 +868,19 @@ public class GroovyPosixCommands extends PosixCommands {
         Less less = new Less(context.terminal(), context.currentDir(), opt);
         less.run(sources);
     }
-    private static class GrepSource {
+
+    private static class NamedInputStream {
         private final InputStream inputStream;
         private final Path path;
         private final String name;
 
-        public GrepSource(InputStream inputStream, String name) {
+        public NamedInputStream(InputStream inputStream, String name) {
             this.inputStream = inputStream;
             this.path = null;
             this.name = name;
         }
 
-        public GrepSource(Path path, String name) {
+        public NamedInputStream(Path path, String name) {
             this.inputStream = null;
             this.path = path;
             this.name = name;

Reply via email to