From a0c33db7a8a17ca17eb1f221af3640f9e2ee2f35 Mon Sep 17 00:00:00 2001 From: pipetee Date: Wed, 18 Dec 2019 15:48:29 +0800 Subject: [PATCH] Add tee command for pipes (#976) --- .../core/command/BuiltinCommandPack.java | 2 + .../core/command/basic1000/TeeCommand.java | 48 +++++++++++ .../shell/command/internal/StdoutHandler.java | 4 +- .../shell/command/internal/TeeHandler.java | 80 +++++++++++++++++++ site/src/site/sphinx/en/tee.md | 25 ++++++ site/src/site/sphinx/tee.md | 25 ++++++ 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/command/basic1000/TeeCommand.java create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/command/internal/TeeHandler.java create mode 100644 site/src/site/sphinx/en/tee.md create mode 100644 site/src/site/sphinx/tee.md diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java index 7b86d1ff112..a067cf361c9 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java +++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java @@ -13,6 +13,7 @@ import com.taobao.arthas.core.command.basic1000.StopCommand; import com.taobao.arthas.core.command.basic1000.SystemEnvCommand; import com.taobao.arthas.core.command.basic1000.SystemPropertyCommand; +import com.taobao.arthas.core.command.basic1000.TeeCommand; import com.taobao.arthas.core.command.basic1000.VMOptionCommand; import com.taobao.arthas.core.command.basic1000.VersionCommand; import com.taobao.arthas.core.command.hidden.JulyCommand; @@ -102,6 +103,7 @@ private static void initCommands() { commands.add(Command.create(PwdCommand.class)); commands.add(Command.create(MBeanCommand.class)); commands.add(Command.create(GrepCommand.class)); + commands.add(Command.create(TeeCommand.class)); commands.add(Command.create(ProfilerCommand.class)); } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/TeeCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/TeeCommand.java new file mode 100644 index 00000000000..113beec4d8b --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/TeeCommand.java @@ -0,0 +1,48 @@ +package com.taobao.arthas.core.command.basic1000; + + +import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.shell.command.AnnotatedCommand; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.middleware.cli.annotations.*; + +/** + * @author min.yang + */ +@Name("tee") +@Summary("tee command for pipes." ) +@Description(Constants.EXAMPLE + + " sysprop | tee /path/to/logfile | grep java \n" + + " sysprop | tee -a /path/to/logfile | grep java \n" + + Constants.WIKI + Constants.WIKI_HOME + "tee") +public class TeeCommand extends AnnotatedCommand { + + private String filePath; + private boolean append; + + @Argument(index = 0, argName = "file", required = false) + @Description("File path") + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + @Option(shortName = "a", longName = "append", flag = true) + @Description("Append to file") + public void setRegEx(boolean append) { + this.append = append; + } + + @Override + public void process(CommandProcess process) { + process.write("The tee command only for pipes. See 'tee --help'\n"); + process.end(); + } + + public String getFilePath() { + return filePath; + } + + public boolean isAppend() { + return append; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/shell/command/internal/StdoutHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/command/internal/StdoutHandler.java index 3939b8adb9b..8ed3914e94f 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/command/internal/StdoutHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/command/internal/StdoutHandler.java @@ -30,7 +30,9 @@ public static StdoutHandler inject(List tokens) { return PlainTextHandler.inject(tokens); } else if (firstTextToken.value().equals(WordCountHandler.NAME)) { return WordCountHandler.inject(tokens); - } else { + } else if (firstTextToken.value().equals(TeeHandler.NAME)){ + return TeeHandler.inject(tokens); + } else{ return null; } } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/command/internal/TeeHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/command/internal/TeeHandler.java new file mode 100644 index 00000000000..78fbe6e0e6b --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/shell/command/internal/TeeHandler.java @@ -0,0 +1,80 @@ +package com.taobao.arthas.core.shell.command.internal; + +import com.taobao.arthas.core.command.basic1000.TeeCommand; +import com.taobao.arthas.core.shell.cli.CliToken; +import com.taobao.arthas.core.util.StringUtils; +import com.taobao.middleware.cli.CLI; +import com.taobao.middleware.cli.CommandLine; +import com.taobao.middleware.cli.annotations.CLIConfigurator; + +import java.io.*; +import java.util.List; + +/** + * @author min.yang + */ +public class TeeHandler extends StdoutHandler implements CloseFunction { + public static final String NAME = "tee"; + private PrintWriter out; + private static CLI cli = null; + + public TeeHandler(String filePath, boolean append) throws IOException { + if (StringUtils.isEmpty(filePath)) { + return; + } + File file = new File(filePath); + + if (file.isDirectory()) { + throw new IOException(filePath + ": Is a directory"); + } + + if (!file.exists()) { + File parentFile = file.getParentFile(); + if (parentFile != null) { + parentFile.mkdirs(); + } + } + out = new PrintWriter(new BufferedWriter(new FileWriter(file, append))); + } + + public static StdoutHandler inject(List tokens) { + List args = StdoutHandler.parseArgs(tokens, NAME); + + TeeCommand teeCommand = new TeeCommand(); + if (cli == null) { + cli = CLIConfigurator.define(TeeCommand.class); + } + CommandLine commandLine = cli.parse(args, true); + + try { + CLIConfigurator.inject(commandLine, teeCommand); + } catch (Throwable e) { + throw new RuntimeException(e); + } + + String filePath = teeCommand.getFilePath(); + boolean append = teeCommand.isAppend(); + try { + return new TeeHandler(filePath, append); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String apply(String data) { + data = super.apply(data); + if (out != null) { + out.write(data); + out.flush(); + } + return data; + } + + @Override + public void close() { + if (out != null) { + out.close(); + } + } +} diff --git a/site/src/site/sphinx/en/tee.md b/site/src/site/sphinx/en/tee.md new file mode 100644 index 00000000000..771871c7bfe --- /dev/null +++ b/site/src/site/sphinx/en/tee.md @@ -0,0 +1,25 @@ +tee +=== + +> Similar to the traditional `tee` command. + + +``` + USAGE: + tee [-a] [-h] [file] + + SUMMARY: + tee command for pipes. + + EXAMPLES: + sysprop | tee /path/to/logfile | grep java + sysprop | tee -a /path/to/logfile | grep java + + WIKI: + https://alibaba.github.io/arthas/tee + + OPTIONS: + -a, --append Append to file + -h, --help this help + File path +``` \ No newline at end of file diff --git a/site/src/site/sphinx/tee.md b/site/src/site/sphinx/tee.md new file mode 100644 index 00000000000..28a3d5a8158 --- /dev/null +++ b/site/src/site/sphinx/tee.md @@ -0,0 +1,25 @@ +tee +=== + +> 类似传统的`tee`命令。 + + +``` + USAGE: + tee [-a] [-h] [file] + + SUMMARY: + tee command for pipes. + + EXAMPLES: + sysprop | tee /path/to/logfile | grep java + sysprop | tee -a /path/to/logfile | grep java + + WIKI: + https://alibaba.github.io/arthas/tee + + OPTIONS: + -a, --append Append to file + -h, --help this help + File path +``` \ No newline at end of file