mirror of https://github.com/alibaba/arthas.git
grep command support regexp(-e) and -n/-v/-n/-A/-B/-C #899
parent
fde8d02608
commit
ea96c8d0ce
@ -0,0 +1,41 @@
|
||||
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.Description;
|
||||
import com.taobao.middleware.cli.annotations.Name;
|
||||
import com.taobao.middleware.cli.annotations.Summary;
|
||||
|
||||
/**
|
||||
* @see com.taobao.arthas.core.shell.command.internal.GrepHandler
|
||||
*/
|
||||
@Name("grep")
|
||||
@Summary("grep command for pipes (-e -m -n -v -A -B -C -f )\n" )
|
||||
@Description(Constants.EXAMPLE +
|
||||
"sysprop | grep java \n" +
|
||||
" sysenv | grep -v JAVA -n\n" +
|
||||
" sysenv | grep -e \"(?i)(JAVA|sun)\" -m 3 -C 2\n" +
|
||||
" sysenv | grep -v JAVA -A2 -B3\n" +
|
||||
" sysenv | grep -e JAVA -f /tmp/t.log \n" +
|
||||
" thread | grep -m 10 -e \"TIMED_WAITING|WAITING\"\n\n"
|
||||
+"-e, --regexp use PATTERN for matching\n"
|
||||
+"-m, --max-count=NUM stop after NUM selected lines\n"
|
||||
+"-n, --line-number print line number with output lines\n"
|
||||
+"-v, --invert-match select non-matching lines\n"
|
||||
+"-A, --after-context=NUM print NUM lines of trailing context\n"
|
||||
+"-B, --before-context=NUM print NUM lines of leading context\n"
|
||||
+"-C, --context=NUM print NUM lines of output context\n"
|
||||
// +"-f, --output=File output result to file, filename endsWith :false for disable append mode\n"
|
||||
+ Constants.WIKI + Constants.WIKI_HOME + "grep")
|
||||
public class GrepCommand extends AnnotatedCommand {
|
||||
@Override
|
||||
public void process(CommandProcess process) {
|
||||
process.write("The grep command only for pipes ").write("\n");
|
||||
final Description ann = GrepCommand.class.getAnnotation(Description.class);
|
||||
if (ann != null) {
|
||||
process.write(ann.value()).write("\n");
|
||||
}
|
||||
process.end();
|
||||
}
|
||||
}
|
@ -1,52 +1,167 @@
|
||||
package com.taobao.arthas.core.shell.command.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import com.taobao.arthas.core.shell.cli.CliToken;
|
||||
import com.taobao.middleware.cli.Argument;
|
||||
import com.taobao.middleware.cli.CLIs;
|
||||
import com.taobao.middleware.cli.CommandLine;
|
||||
import com.taobao.middleware.cli.Option;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author beiwei30 on 12/12/2016.
|
||||
*/
|
||||
public class GrepHandler extends StdoutHandler {
|
||||
public static final String NAME = "grep";
|
||||
private static final Pattern TRIM_PATTERN;
|
||||
static {
|
||||
//默认删除右边的空白字符是为了解决-n 因空白字符导致显示换行的问题
|
||||
//ie: sysprop | grep -n java
|
||||
final String p = System.getProperty("arthas_grep_trim_pattern", "[ \\f\t\\v]+$");
|
||||
TRIM_PATTERN = "NOP".equals(p) ? null : Pattern.compile(p);
|
||||
}
|
||||
|
||||
private String keyword;
|
||||
private boolean ignoreCase;
|
||||
|
||||
// -v, --invert-match select non-matching lines
|
||||
private final boolean invertMatch;
|
||||
//-e, --regexp=PATTERN use PATTERN for matching
|
||||
private final Pattern pattern;
|
||||
// -n, --line-number print line number with output lines
|
||||
private final boolean showLineNumber;
|
||||
/*
|
||||
-B, --before-context=NUM print NUM lines of leading context
|
||||
-A, --after-context=NUM print NUM lines of trailing context
|
||||
-C, --context=NUM print NUM lines of output context
|
||||
*/
|
||||
private final int beforeLines;
|
||||
private final int afterLines;
|
||||
//-m, --max-count=NUM stop after NUM selected lines
|
||||
private final int maxCount;
|
||||
|
||||
public static StdoutHandler inject(List<CliToken> tokens) {
|
||||
List<String> args = StdoutHandler.parseArgs(tokens, NAME);
|
||||
CommandLine commandLine = CLIs.create(NAME)
|
||||
.addOption(new Option().setShortName("i").setLongName("ignore-case").setFlag(true))
|
||||
.addOption(new Option().setShortName("v").setLongName("invert-match").setFlag(true))
|
||||
.addOption(new Option().setShortName("n").setLongName("line-number").setFlag(true))
|
||||
.addOption(new Option().setShortName("B").setLongName("before-context").setSingleValued(true))
|
||||
.addOption(new Option().setShortName("A").setLongName("after-context").setSingleValued(true))
|
||||
.addOption(new Option().setShortName("C").setLongName("context").setSingleValued(true))
|
||||
.addOption(new Option().setShortName("e").setLongName("regexp").setFlag(true))
|
||||
.addOption(new Option().setShortName("f").setLongName("output").setSingleValued(true))
|
||||
.addOption(new Option().setShortName("m").setLongName("max-count").setSingleValued(true))
|
||||
.addArgument(new Argument().setArgName("keyword").setIndex(0))
|
||||
.parse(args);
|
||||
Boolean ignoreCase = commandLine.isFlagEnabled("ignore-case");
|
||||
String keyword = commandLine.getArgumentValue(0);
|
||||
return new GrepHandler(keyword, ignoreCase);
|
||||
final boolean invertMatch = commandLine.isFlagEnabled("invert-match");
|
||||
final boolean regexpMode = commandLine.isFlagEnabled("regexp");
|
||||
final boolean showLineNumber = commandLine.isFlagEnabled("line-number");
|
||||
int context = getInt(commandLine, "context", 0);
|
||||
int beforeLines = getInt(commandLine, "before-context", 0);
|
||||
int afterLines = getInt(commandLine, "after-context", 0);
|
||||
if (context > 0) {
|
||||
if (beforeLines < 1) {
|
||||
beforeLines = context;
|
||||
}
|
||||
if (afterLines < 1 ){
|
||||
afterLines = context;
|
||||
}
|
||||
}
|
||||
final int maxCount = getInt(commandLine, "max-count", 0);
|
||||
return new GrepHandler(keyword, ignoreCase, invertMatch, regexpMode, showLineNumber
|
||||
, beforeLines, afterLines, maxCount);
|
||||
}
|
||||
|
||||
private static final int getInt(CommandLine cmdline, String name , int defaultValue) {
|
||||
final String v = cmdline.getOptionValue(name);
|
||||
final int ret = v== null ? defaultValue : Integer.parseInt(v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private GrepHandler(String keyword, boolean ignoreCase) {
|
||||
this.keyword = keyword;
|
||||
private GrepHandler(String keyword, boolean ignoreCase, boolean invertMatch, boolean regexpMode
|
||||
, boolean showLineNumber, int beforeLines, int afterLines,int maxCount) {
|
||||
this.ignoreCase = ignoreCase;
|
||||
this.invertMatch = invertMatch;
|
||||
this.showLineNumber = showLineNumber;
|
||||
this.beforeLines = beforeLines > 0 ? beforeLines : 0;
|
||||
this.afterLines = afterLines > 0 ? afterLines : 0;
|
||||
this.maxCount = maxCount > 0 ? maxCount : 0;
|
||||
if (regexpMode) {
|
||||
final int flags = ignoreCase ? Pattern.CASE_INSENSITIVE : 0;
|
||||
this.pattern = Pattern.compile(keyword, flags);
|
||||
} else {
|
||||
this.pattern = null;
|
||||
}
|
||||
this.keyword = ignoreCase ? keyword.toLowerCase() : keyword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
String[] lines = input.split("\n");
|
||||
for (String line : lines) {
|
||||
if (ignoreCase) {
|
||||
line = line.toLowerCase();
|
||||
keyword = keyword.toLowerCase();
|
||||
}
|
||||
int continueCount = 0 ;
|
||||
int lastStartPos = 0 ;
|
||||
int lastContinueLineNum = -1;
|
||||
int matchCount = 0;
|
||||
for (int lineNum = 0 ; lineNum < lines.length ;) {
|
||||
String line = TRIM_PATTERN.matcher(lines[lineNum++]).replaceAll("");
|
||||
final boolean match;
|
||||
if (pattern == null) {
|
||||
match = (ignoreCase ? line.toLowerCase() : line).contains(keyword);
|
||||
} else {
|
||||
match = pattern.matcher(line).find();
|
||||
}
|
||||
if (invertMatch ? !match : match) {
|
||||
matchCount++;
|
||||
if (beforeLines > continueCount) {
|
||||
int n = lastContinueLineNum == -1 ? ( beforeLines >= lineNum ? 1 : lineNum - beforeLines )
|
||||
: lineNum - beforeLines - continueCount;
|
||||
if ( n >= lastContinueLineNum || lastContinueLineNum == -1 ) {
|
||||
StringBuilder beforeSb = new StringBuilder();
|
||||
for (int i = n ; i < lineNum ; i++) {
|
||||
appendLine(beforeSb, i, lines[i - 1]);
|
||||
}
|
||||
output.insert(lastStartPos, beforeSb);
|
||||
}
|
||||
} // end handle before lines
|
||||
|
||||
if (line.contains(keyword)) {
|
||||
output.append(line).append("\n");
|
||||
lastStartPos = output.length();
|
||||
appendLine(output, lineNum, line);
|
||||
|
||||
if (afterLines > continueCount) {
|
||||
int last = lineNum + afterLines - continueCount;
|
||||
if (last > lines.length) {
|
||||
last = lines.length;
|
||||
}
|
||||
for(int i = lineNum ; i < last ; i++) {
|
||||
appendLine(output, i+1, lines[i]);
|
||||
lineNum ++;
|
||||
continueCount++;
|
||||
lastStartPos = output.length();
|
||||
}
|
||||
} //end handle afterLines
|
||||
|
||||
continueCount++;
|
||||
if(maxCount > 0 && matchCount >= maxCount) {
|
||||
break;
|
||||
}
|
||||
} else { // not match
|
||||
if(continueCount > 0) {
|
||||
lastContinueLineNum = lineNum -1 ;
|
||||
continueCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
final String str = output.toString();// output.length() > 0 ? output.substring(0, output.length()-1) : "";
|
||||
return str;
|
||||
}
|
||||
|
||||
protected void appendLine(StringBuilder output, int lineNum, String line) {
|
||||
if(showLineNumber) {
|
||||
output.append(lineNum).append(':');
|
||||
}
|
||||
return output.toString();
|
||||
output.append(line).append('\n');
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,160 @@
|
||||
package com.taobao.arthas.core.shell.command.interna;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.charset.Charset;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import com.taobao.arthas.core.shell.command.internal.GrepHandler;
|
||||
import com.taobao.arthas.core.util.FileUtils;
|
||||
|
||||
public class GrepHandlerTest {
|
||||
|
||||
private static final class Hold {
|
||||
static final Constructor<?> constructor;
|
||||
static {
|
||||
constructor = GrepHandler.class.getDeclaredConstructors()[0];
|
||||
constructor.setAccessible(true);
|
||||
}
|
||||
public static GrepHandler createInst(String keyword, boolean ignoreCase, boolean invertMatch, boolean regexpMode
|
||||
, boolean showLineNumber, int beforeLines, int afterLines, String output) {
|
||||
return createInst(keyword, ignoreCase, invertMatch, regexpMode, showLineNumber, beforeLines, afterLines, output, 0);
|
||||
}
|
||||
//new GrepHandler(keyword, ignoreCase, invertMatch, regexpMode, showLineNumber, beforeLines, afterLines,output)
|
||||
public static GrepHandler createInst(String keyword, boolean ignoreCase, boolean invertMatch, boolean regexpMode
|
||||
, boolean showLineNumber, int beforeLines, int afterLines, String output, int maxCount) {
|
||||
try {
|
||||
final Object[] initargs = new Object[] { keyword,ignoreCase,invertMatch,regexpMode,showLineNumber
|
||||
,beforeLines,afterLines,maxCount
|
||||
};
|
||||
GrepHandler handler = (GrepHandler)constructor.newInstance(initargs);
|
||||
return handler;
|
||||
}catch(RuntimeException ex) {
|
||||
throw ex;
|
||||
}catch(Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void test4grep_ABC() { //-A -B -C
|
||||
Object[][] samples = new Object[][] {
|
||||
{"ABC\n1\n2\n3\n4\nc", "ABC", 0, 4, "ABC\n1\n2\n3\n4"},
|
||||
{"ABC\n1\n2\n3\n4\nABC\n5", "ABC", 2, 1, "ABC\n1\n3\n4\nABC\n5"},
|
||||
{"ABC\n1\n2\n3\n4\na", "ABC", 2, 1, "ABC\n1"},
|
||||
{"ABC\n1\n2\n3\n4\nb", "ABC", 0, 0, "ABC"},
|
||||
{"ABC\n1\n2\n3\n4\nc", "ABC", 0, 5, "ABC\n1\n2\n3\n4\nc"},
|
||||
{"ABC\n1\n2\n3\n4\nc", "ABC", 0, 10, "ABC\n1\n2\n3\n4\nc"},
|
||||
{"ABC\n1\n2\n3\n4\nc", "ABC", 0, 2, "ABC\n1\n2"},
|
||||
{"1\n2\n3\n4\nABC", "ABC", 5, 1, "1\n2\n3\n4\nABC"},
|
||||
{"1\n2\n3\n4\nABC", "ABC", 4, 1, "1\n2\n3\n4\nABC"},
|
||||
{"1\n2\n3\n4\nABC", "ABC", 2, 1, "3\n4\nABC"}
|
||||
};
|
||||
|
||||
for(Object[] args : samples) {
|
||||
String word = (String)args[1];
|
||||
int beforeLines = (Integer)args[2];
|
||||
int afterLines = (Integer)args[3];
|
||||
GrepHandler handler = Hold.createInst(word,false,false,true,false,beforeLines,afterLines,null);
|
||||
String input = (String)args[0];
|
||||
final String ret = handler.apply(input);
|
||||
final String expected = (String)args[4];
|
||||
Assert.assertEquals(expected, ret.substring(0,ret.length()-1));
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void test4grep_v() {//-v
|
||||
Object[][] samples = new Object[][] {
|
||||
{"ABC\n1\n2\nc", "ABC", 0, 4, "1\n2\nc"},
|
||||
{"ABC\n1\n2\n", "ABC", 0, 0, "1\n2"},
|
||||
{"ABC\n1\n2\nc", "ABC", 0, 1, "1\n2\nc"}
|
||||
};
|
||||
|
||||
for(Object[] args : samples) {
|
||||
String word = (String)args[1];
|
||||
int beforeLines = (Integer)args[2];
|
||||
int afterLines = (Integer)args[3];
|
||||
GrepHandler handler = Hold.createInst(word,false,true,true,false,beforeLines,afterLines,null);
|
||||
String input = (String)args[0];
|
||||
final String ret = handler.apply(input);
|
||||
final String expected = (String)args[4];
|
||||
Assert.assertEquals(expected, ret.substring(0,ret.length()-1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test4grep_e() {//-e
|
||||
Object[][] samples = new Object[][] {
|
||||
{"java\n1python\n2\nc", "java|python", "java\n1python"},
|
||||
{"java\n1python\n2\nc", "ja|py", "java\n1python"}
|
||||
};
|
||||
|
||||
for(Object[] args : samples) {
|
||||
String word = (String)args[1];
|
||||
GrepHandler handler = Hold.createInst(word,false,false,true,false,0,0,null);
|
||||
String input = (String)args[0];
|
||||
final String ret = handler.apply(input);
|
||||
final String expected = (String)args[2];
|
||||
Assert.assertEquals(expected, ret.substring(0,ret.length()-1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test4grep_m() {//-e
|
||||
Object[][] samples = new Object[][] {
|
||||
{"java\n1python\n2\nc", "java|python", "java", 1},
|
||||
{"java\n1python\n2\nc", "ja|py", "java\n1python",2},
|
||||
{"java\n1python\n2\nc", "ja|py", "java\n1python",3}
|
||||
};
|
||||
|
||||
for(Object[] args : samples) {
|
||||
String word = (String)args[1];
|
||||
int maxCount = args.length > 3 ? (Integer)args[3] : 0;
|
||||
GrepHandler handler = Hold.createInst(word,false,false,true,false,0,0,null, maxCount);
|
||||
String input = (String)args[0];
|
||||
final String ret = handler.apply(input);
|
||||
final String expected = (String)args[2];
|
||||
Assert.assertEquals(expected, ret.substring(0,ret.length()-1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test4grep_n() {//-n
|
||||
Object[][] samples = new Object[][] {
|
||||
{"java\n1\npython\n2\nc","1:java\n3:python", "java|python" },
|
||||
{"java\n1\npython\njava\nc","1:java\n4:java", "java",false }
|
||||
};
|
||||
|
||||
for(Object[] args : samples) {
|
||||
String word = (String)args[2];
|
||||
boolean regexpMode = args.length >3 ? (Boolean)args[3] : true;
|
||||
GrepHandler handler = Hold.createInst(word,false,false,regexpMode,true,0,0,null);
|
||||
String input = (String)args[0];
|
||||
final String ret = handler.apply(input);
|
||||
final String expected = (String)args[1];
|
||||
Assert.assertEquals(expected, ret.substring(0,ret.length()-1));
|
||||
}
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void test4grep_f() throws Exception {//-f
|
||||
// Object[][] samples = new Object[][] {
|
||||
// {"java\n1\npython\n2\nc","1:java\n3:python", "java|python" },
|
||||
// {"java\n1\npython\njava\nc","1:java\n4:java", "java",false }
|
||||
// };
|
||||
// String output = File.createTempFile("arthas_test", ".log").getAbsolutePath();
|
||||
// final Charset charset = Charset.forName("UTF-8");
|
||||
// for(Object[] args : samples) {
|
||||
// String word = (String)args[2];
|
||||
// boolean regexpMode = args.length >3 ? (Boolean)args[3] : true;
|
||||
// GrepHandler handler = Hold.createInst(word,false,false,regexpMode,true,0,0,output+":false");
|
||||
// String input = (String)args[0];
|
||||
// final String ret = handler.apply(input);
|
||||
// final String expected = (String)args[1];
|
||||
// Assert.assertEquals(expected, ret.substring(0,ret.length()-1));
|
||||
// final String ret2 = FileUtils.readFileToString(new File(output), charset);
|
||||
// Assert.assertEquals(expected, ret.substring(0,ret2.length()-1));
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
Loading…
Reference in New Issue