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;
