This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 9e9dfacae4 GROOVY-11742: posix commands should support variable
assignment (amendments for /tail)
9e9dfacae4 is described below
commit 9e9dfacae4672fc12cd297745ca9eec5879b9b46
Author: Paul King <[email protected]>
AuthorDate: Mon Aug 25 15:48:03 2025 +1000
GROOVY-11742: posix commands should support variable assignment (amendments
for /tail)
---
.../groovy/org/apache/groovy/groovysh/Main.groovy | 11 +-
.../groovy/groovysh/jline/GroovyPosixCommands.java | 120 +++++++++++++++++----
2 files changed, 109 insertions(+), 22 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 3d07e07472..84a8be0e53 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', '/wc', '/sort']
+ private static POSIX_FILE_CMDS = ['/wc', '/sort']
@SuppressWarnings("resource")
protected static class ExtraConsoleCommands extends JlineCommandRegistry
implements CommandRegistry {
@@ -114,6 +114,7 @@ class Main {
'/ls' : new CommandMethods((Function) this::ls,
this::optFileCompleter),
'/grep' : new CommandMethods((Function) this::grepcmd,
this::optFileCompleter),
'/head' : new CommandMethods((Function) this::headcmd,
this::optFileCompleter),
+ '/tail' : new CommandMethods((Function) this::tailcmd,
this::optFileCompleter),
'/cat' : new CommandMethods((Function) this::cat,
this::optFileCompleter),
"/!" : new CommandMethods((Function) this::shell,
this::defaultCompleter)
]
@@ -199,6 +200,14 @@ class Main {
}
}
+ private void tailcmd(CommandInput input) {
+ try {
+ GroovyPosixCommands.tail(context(input), ['/tail',
*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)
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 5cde0fcc24..dcf56c2446 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
@@ -31,6 +31,7 @@ import org.jline.utils.OSUtils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -115,15 +116,17 @@ 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 ...]",
+ "Usage: /head [-n lines | -c bytes | -q | -v] [file|variable ...]",
" -? --help Show help",
" -n --lines=LINES Print line counts",
" -c --bytes=BYTES Print byte counts",
+ " -q --quiet Never output filename headers",
+ " -v --verbose Always output filename headers",
};
Options opt = parseOptions(context, usage, argv);
if (opt.isSet("lines") && opt.isSet("bytes")) {
- throw new IllegalArgumentException("usage: head [-n # | -c #]
[file ...]");
+ throw new IllegalArgumentException("usage: /head [-n # | -c # | -q
| -v] [file|variable ...]");
}
int nbLines = Integer.MAX_VALUE;
@@ -144,32 +147,107 @@ public class GroovyPosixCommands extends PosixCommands {
boolean first = true;
List<NamedInputStream> sources = getSources(context, argv, args);
for (NamedInputStream nis : sources) {
- if (!first && args.size() > 1) {
- context.out().println();
+ boolean filenameHeader = sources.size() > 1;
+ if (opt.isSet("verbose")) {
+ filenameHeader = true;
+ } else if (opt.isSet("quiet")) {
+ filenameHeader = false;
}
- if (args.size() > 1) {
+ if (filenameHeader) {
+ if (!first) {
+ context.out().println();
+ }
context.out().println("==> " + nis.getName() + " <==");
}
+ doHead(context, nis.getInputStream(), nbLines, nbBytes);
+ first = false;
+ }
+ }
- 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++;
- }
+ private static void doHead(Context context, InputStream is, final int
nbLines, final int nbBytes) throws IOException {
+ 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);
+ }
+ } else {
+ byte[] buffer = new byte[nbBytes];
+ int bytesRead = is.read(buffer);
+ if (bytesRead > 0) {
+ context.out().write(buffer, 0, bytesRead);
+ }
+ is.close();
+ }
+ }
+
+ public static void tail(Context context, Object[] argv) throws Exception {
+ final String[] usage = {
+ "/tail - display last lines of files or variables",
+ "Usage: /tail [-n lines | -c bytes | -q | -v] [file|variable ...]",
+ " -? --help Show help",
+ " -n --lines=LINES Number of lines to print",
+ " -c --bytes=BYTES Number of bytes to print",
+ " -q --quiet Never output filename headers",
+ " -v --verbose Always output filename headers",
+ };
+ Options opt = parseOptions(context, usage, argv);
+
+ if (opt.isSet("lines") && opt.isSet("bytes")) {
+ throw new IllegalArgumentException("usage: /tail [-c # | -n # | -q
| -v] [file|variable ...]");
+ }
+
+ int lines = opt.isSet("lines") ? opt.getNumber("lines") : 10;
+ int bytes = opt.isSet("bytes") ? opt.getNumber("bytes") : -1;
+
+ List<String> args = opt.args();
+ if (args.isEmpty()) {
+ args = Collections.singletonList("-");
+ }
+
+ List<NamedInputStream> sources = getSources(context, argv, args);
+ boolean filenameHeader = sources.size() > 1;
+ if (opt.isSet("verbose")) {
+ filenameHeader = true;
+ } else if (opt.isSet("quiet")) {
+ filenameHeader = false;
+ }
+ for (NamedInputStream nis : sources) {
+ if (filenameHeader) {
+ context.out().println("==> " + nis.getName() + " <==");
+ }
+ tailInputStream(context, nis.getInputStream(), lines, bytes);
+ }
+ }
+
+ private static void tailInputStream(Context context, InputStream is, int
lines, int bytes) throws IOException {
+ if (bytes > 0) {
+ // Read all and keep last bytes
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[8192];
+ int n;
+ while ((n = is.read(buffer)) != -1) {
+ baos.write(buffer, 0, n);
+ }
+ byte[] data = baos.toByteArray();
+ int start = Math.max(0, data.length - bytes);
+ context.out().write(data, start, data.length - start);
+ } else {
+ // Read all and keep last lines
+ List<String> allLines = new ArrayList<>();
+ try (BufferedReader reader = new BufferedReader(new
java.io.InputStreamReader(is))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ allLines.add(line);
}
- is.close();
}
- first = false;
+ int start = Math.max(0, allLines.size() - lines);
+ for (int i = start; i < allLines.size(); i++) {
+ context.out().println(allLines.get(i));
+ }
}
}