HADOOP-7353. Cleanup FsShell and prevent masking of RTE stack traces. Contributed by Daryn Sharp.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1132764 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Todd Lipcon 2011-06-06 20:53:37 +00:00
parent 0bcd7e9f86
commit 44a35b5d9a
8 changed files with 282 additions and 221 deletions

View File

@ -280,6 +280,9 @@ Trunk (unreleased changes)
HADOOP-7341. Fix options parsing in CommandFormat (Daryn Sharp via todd) HADOOP-7341. Fix options parsing in CommandFormat (Daryn Sharp via todd)
HADOOP-7353. Cleanup FsShell and prevent masking of RTE stack traces.
(Daryn Sharp via todd)
Release 0.22.0 - Unreleased Release 0.22.0 - Unreleased
INCOMPATIBLE CHANGES INCOMPATIBLE CHANGES

View File

@ -18,9 +18,10 @@
package org.apache.hadoop.fs; package org.apache.hadoop.fs;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.regex.Matcher; import java.util.LinkedList;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -30,9 +31,6 @@
import org.apache.hadoop.fs.shell.Command; import org.apache.hadoop.fs.shell.Command;
import org.apache.hadoop.fs.shell.CommandFactory; import org.apache.hadoop.fs.shell.CommandFactory;
import org.apache.hadoop.fs.shell.FsCommand; import org.apache.hadoop.fs.shell.FsCommand;
import org.apache.hadoop.fs.shell.PathExceptions.PathNotFoundException;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.util.Tool; import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; import org.apache.hadoop.util.ToolRunner;
@ -46,23 +44,31 @@ public class FsShell extends Configured implements Tool {
private Trash trash; private Trash trash;
protected CommandFactory commandFactory; protected CommandFactory commandFactory;
private final String usagePrefix =
"Usage: hadoop fs [generic options]";
/** /**
* Default ctor with no configuration. Be sure to invoke
* {@link #setConf(Configuration)} with a valid configuration prior
* to running commands.
*/ */
public FsShell() { public FsShell() {
this(null); this(null);
} }
/**
* Construct a FsShell with the given configuration. Commands can be
* executed via {@link #run(String[])}
* @param conf the hadoop configuration
*/
public FsShell(Configuration conf) { public FsShell(Configuration conf) {
super(conf); super(conf);
fs = null;
trash = null;
commandFactory = new CommandFactory();
} }
protected FileSystem getFS() throws IOException { protected FileSystem getFS() throws IOException {
if(fs == null) if (fs == null) {
fs = FileSystem.get(getConf()); fs = FileSystem.get(getConf());
}
return fs; return fs;
} }
@ -75,93 +81,145 @@ protected Trash getTrash() throws IOException {
protected void init() throws IOException { protected void init() throws IOException {
getConf().setQuietMode(true); getConf().setQuietMode(true);
if (commandFactory == null) {
commandFactory = new CommandFactory(getConf());
commandFactory.addObject(new Help(), "-help");
commandFactory.addObject(new Usage(), "-usage");
registerCommands(commandFactory);
}
}
protected void registerCommands(CommandFactory factory) {
// TODO: DFSAdmin subclasses FsShell so need to protect the command
// registration. This class should morph into a base class for
// commands, and then this method can be abstract
if (this.getClass().equals(FsShell.class)) {
factory.registerCommands(FsCommand.class);
}
} }
/** /**
* Returns the Trash object associated with this shell. * Returns the Trash object associated with this shell.
* @return Path to the trash
* @throws IOException upon error
*/ */
public Path getCurrentTrashDir() throws IOException { public Path getCurrentTrashDir() throws IOException {
return getTrash().getCurrentTrashDir(); return getTrash().getCurrentTrashDir();
} }
/** // NOTE: Usage/Help are inner classes to allow access to outer methods
* Return an abbreviated English-language desc of the byte length // that access commandFactory
* @deprecated Consider using {@link org.apache.hadoop.util.StringUtils#byteDesc} instead.
*/
@Deprecated
public static String byteDesc(long len) {
return StringUtils.byteDesc(len);
}
/** /**
* @deprecated Consider using {@link org.apache.hadoop.util.StringUtils#limitDecimalTo2} instead. * Display help for commands with their short usage and long description
*/ */
@Deprecated protected class Usage extends FsCommand {
public static synchronized String limitDecimalTo2(double d) { public static final String NAME = "usage";
return StringUtils.limitDecimalTo2(d); public static final String USAGE = "[cmd ...]";
} public static final String DESCRIPTION =
"Displays the usage for given command or all commands if none\n" +
"is specified.";
private void printHelp(String cmd) { @Override
String summary = "hadoop fs is the command to execute fs commands. " + protected void processRawArguments(LinkedList<String> args) {
"The full syntax is: \n\n" + if (args.isEmpty()) {
"hadoop fs [-fs <local | file system URI>] [-conf <configuration file>]\n\t" + printUsage(System.out);
"[-D <property=value>]\n\t" +
"[-report]";
String conf ="-conf <configuration file>: Specify an application configuration file.";
String D = "-D <property=value>: Use value for given property.";
String fs = "-fs [local | <file system URI>]: \tSpecify the file system to use.\n" +
"\t\tIf not specified, the current configuration is used, \n" +
"\t\ttaken from the following, in increasing precedence: \n" +
"\t\t\tcore-default.xml inside the hadoop jar file \n" +
"\t\t\tcore-site.xml in $HADOOP_CONF_DIR \n" +
"\t\t'local' means use the local file system as your DFS. \n" +
"\t\t<file system URI> specifies a particular file system to \n" +
"\t\tcontact. This argument is optional but if used must appear\n" +
"\t\tappear first on the command line. Exactly one additional\n" +
"\t\targument must be specified. \n";
String help = "-help [cmd]: \tDisplays help for given command or all commands if none\n" +
"\t\tis specified.\n";
Command instance = commandFactory.getInstance("-" + cmd);
if (instance != null) {
printHelp(instance);
} else if ("fs".equals(cmd)) {
System.out.println(fs);
} else if ("conf".equals(cmd)) {
System.out.println(conf);
} else if ("D".equals(cmd)) {
System.out.println(D);
} else if ("help".equals(cmd)) {
System.out.println(help);
} else { } else {
System.out.println(summary); for (String arg : args) printUsage(System.out, arg);
for (String thisCmdName : commandFactory.getNames()) { }
instance = commandFactory.getInstance(thisCmdName); }
}
/**
* Displays short usage of commands sans the long description
*/
protected class Help extends FsCommand {
public static final String NAME = "help";
public static final String USAGE = "[cmd ...]";
public static final String DESCRIPTION =
"Displays help for given command or all commands if none\n" +
"is specified.";
@Override
protected void processRawArguments(LinkedList<String> args) {
if (args.isEmpty()) {
printHelp(System.out);
} else {
for (String arg : args) printHelp(System.out, arg);
}
}
}
/*
* The following are helper methods for getInfo(). They are defined
* outside of the scope of the Help/Usage class because the run() method
* needs to invoke them too.
*/
// print all usages
private void printUsage(PrintStream out) {
printInfo(out, null, false);
}
// print one usage
private void printUsage(PrintStream out, String cmd) {
printInfo(out, cmd, false);
}
// print all helps
private void printHelp(PrintStream out) {
printInfo(out, null, true);
}
// print one help
private void printHelp(PrintStream out, String cmd) {
printInfo(out, cmd, true);
}
private void printInfo(PrintStream out, String cmd, boolean showHelp) {
if (cmd != null) {
// display help or usage for one command
Command instance = commandFactory.getInstance("-" + cmd);
if (instance == null) {
throw new UnknownCommandException(cmd);
}
if (showHelp) {
printInstanceHelp(out, instance);
} else {
printInstanceUsage(out, instance);
}
} else {
// display help or usage for all commands
out.println(usagePrefix);
// display list of short usages
ArrayList<Command> instances = new ArrayList<Command>();
for (String name : commandFactory.getNames()) {
Command instance = commandFactory.getInstance(name);
if (!instance.isDeprecated()) { if (!instance.isDeprecated()) {
System.out.println("\t[" + instance.getUsage() + "]"); System.out.println("\t[" + instance.getUsage() + "]");
instances.add(instance);
}
}
// display long descriptions for each command
if (showHelp) {
for (Command instance : instances) {
out.println();
printInstanceHelp(out, instance);
}
}
out.println();
ToolRunner.printGenericCommandUsage(out);
} }
} }
System.out.println("\t[-help [cmd]]\n");
System.out.println(fs); private void printInstanceUsage(PrintStream out, Command instance) {
out.println(usagePrefix + " " + instance.getUsage());
for (String thisCmdName : commandFactory.getNames()) {
instance = commandFactory.getInstance(thisCmdName);
if (!instance.isDeprecated()) {
printHelp(instance);
}
}
System.out.println(help);
}
} }
// TODO: will eventually auto-wrap the text, but this matches the expected // TODO: will eventually auto-wrap the text, but this matches the expected
// output for the hdfs tests... // output for the hdfs tests...
private void printHelp(Command instance) { private void printInstanceHelp(PrintStream out, Command instance) {
boolean firstLine = true; boolean firstLine = true;
for (String line : instance.getDescription().split("\n")) { for (String line : instance.getDescription().split("\n")) {
String prefix; String prefix;
@ -176,118 +234,49 @@ private void printHelp(Command instance) {
} }
/** /**
* Displays format of commands. * run
*
*/ */
private void printUsage(String cmd) { public int run(String argv[]) throws Exception {
String prefix = "Usage: java " + FsShell.class.getSimpleName(); // initialize FsShell
init();
Command instance = commandFactory.getInstance(cmd); int exitCode = -1;
if (instance != null) { if (argv.length < 1) {
System.err.println(prefix + " [" + instance.getUsage() + "]"); printUsage(System.err);
} else if ("-fs".equals(cmd)) {
System.err.println("Usage: java FsShell" +
" [-fs <local | file system URI>]");
} else if ("-conf".equals(cmd)) {
System.err.println("Usage: java FsShell" +
" [-conf <configuration file>]");
} else if ("-D".equals(cmd)) {
System.err.println("Usage: java FsShell" +
" [-D <[property=value>]");
} else { } else {
System.err.println("Usage: java FsShell"); String cmd = argv[0];
for (String name : commandFactory.getNames()) { Command instance = null;
instance = commandFactory.getInstance(name); try {
if (!instance.isDeprecated()) { instance = commandFactory.getInstance(cmd);
System.err.println(" [" + instance.getUsage() + "]"); if (instance == null) {
throw new UnknownCommandException();
}
exitCode = instance.run(Arrays.copyOfRange(argv, 1, argv.length));
} catch (IllegalArgumentException e) {
displayError(cmd, e.getLocalizedMessage());
if (instance != null) {
printInstanceUsage(System.err, instance);
}
} catch (Exception e) {
// instance.run catches IOE, so something is REALLY wrong if here
LOG.debug("Error", e);
displayError(cmd, "Fatal internal error");
e.printStackTrace(System.err);
} }
} }
System.err.println(" [-help [cmd]]"); return exitCode;
System.err.println(); }
ToolRunner.printGenericCommandUsage(System.err);
private void displayError(String cmd, String message) {
for (String line : message.split("\n")) {
System.err.println(cmd.substring(1) + ": " + line);
} }
} }
/** /**
* run * Performs any necessary cleanup
* @throws IOException upon error
*/ */
public int run(String argv[]) throws Exception {
// TODO: This isn't the best place, but this class is being abused with
// subclasses which of course override this method. There really needs
// to be a better base class for all commands
commandFactory.setConf(getConf());
commandFactory.registerCommands(FsCommand.class);
if (argv.length < 1) {
printUsage("");
return -1;
}
int exitCode = -1;
int i = 0;
String cmd = argv[i++];
// initialize FsShell
try {
init();
} catch (RPC.VersionMismatch v) {
LOG.debug("Version mismatch", v);
System.err.println("Version Mismatch between client and server" +
"... command aborted.");
return exitCode;
} catch (IOException e) {
LOG.debug("Error", e);
System.err.println("Bad connection to FS. Command aborted. Exception: " +
e.getLocalizedMessage());
return exitCode;
}
try {
Command instance = commandFactory.getInstance(cmd);
if (instance != null) {
exitCode = instance.run(Arrays.copyOfRange(argv, i, argv.length));
} else if ("-help".equals(cmd)) {
if (i < argv.length) {
printHelp(argv[i]);
} else {
printHelp("");
}
} else {
System.err.println(cmd + ": Unknown command");
printUsage("");
}
} catch (Exception e) {
exitCode = 1;
LOG.debug("Error", e);
displayError(cmd, e);
if (e instanceof IllegalArgumentException) {
exitCode = -1;
printUsage(cmd);
}
}
return exitCode;
}
// TODO: this is a quick workaround to accelerate the integration of
// redesigned commands. this will be removed this once all commands are
// converted. this change will avoid having to change the hdfs tests
// every time a command is converted to use path-based exceptions
private static Pattern[] fnfPatterns = {
Pattern.compile("File (.*) does not exist\\."),
Pattern.compile("File does not exist: (.*)"),
Pattern.compile("`(.*)': specified destination directory doest not exist")
};
private void displayError(String cmd, Exception e) {
String message = e.getLocalizedMessage().split("\n")[0];
for (Pattern pattern : fnfPatterns) {
Matcher matcher = pattern.matcher(message);
if (matcher.matches()) {
message = new PathNotFoundException(matcher.group(1)).getMessage();
break;
}
}
System.err.println(cmd.substring(1) + ": " + message);
}
public void close() throws IOException { public void close() throws IOException {
if (fs != null) { if (fs != null) {
fs.close(); fs.close();
@ -297,9 +286,11 @@ public void close() throws IOException {
/** /**
* main() has some simple utility methods * main() has some simple utility methods
* @param argv the command and its arguments
* @throws Exception upon error
*/ */
public static void main(String argv[]) throws Exception { public static void main(String argv[]) throws Exception {
FsShell shell = new FsShell(); FsShell shell = newShellInstance();
int res; int res;
try { try {
res = ToolRunner.run(shell, argv); res = ToolRunner.run(shell, argv);
@ -308,4 +299,26 @@ public static void main(String argv[]) throws Exception {
} }
System.exit(res); System.exit(res);
} }
// TODO: this should be abstract in a base class
protected static FsShell newShellInstance() {
return new FsShell();
}
/**
* The default ctor signals that the command being executed does not exist,
* while other ctor signals that a specific command does not exist. The
* latter is used by commands that process other commands, ex. -usage/-help
*/
@SuppressWarnings("serial")
static class UnknownCommandException extends IllegalArgumentException {
private final String cmd;
UnknownCommandException() { this(null); }
UnknownCommandException(String cmd) { this.cmd = cmd; }
@Override
public String getMessage() {
return ((cmd != null) ? "`"+cmd+"': " : "") + "Unknown command";
}
}
} }

View File

@ -42,6 +42,13 @@
@InterfaceStability.Evolving @InterfaceStability.Evolving
abstract public class Command extends Configured { abstract public class Command extends Configured {
/** default name of the command */
public static String NAME;
/** the command's usage switches and arguments format */
public static String USAGE;
/** the command's long description */
public static String DESCRIPTION;
protected String[] args; protected String[] args;
protected String name; protected String name;
protected int exitCode = 0; protected int exitCode = 0;
@ -70,14 +77,6 @@ protected Command(Configuration conf) {
/** @return the command's name excluding the leading character - */ /** @return the command's name excluding the leading character - */
abstract public String getCommandName(); abstract public String getCommandName();
/**
* Name the command
* @param cmdName as invoked
*/
public void setCommandName(String cmdName) {
name = cmdName;
}
protected void setRecursive(boolean flag) { protected void setRecursive(boolean flag) {
recursive = flag; recursive = flag;
} }
@ -120,13 +119,15 @@ public int runAll() {
* expand arguments, and then process each argument. * expand arguments, and then process each argument.
* <pre> * <pre>
* run * run
* \-> {@link #processOptions(LinkedList)} * |-> {@link #processOptions(LinkedList)}
* \-> {@link #expandArguments(LinkedList)} -> {@link #expandArgument(String)}* * \-> {@link #processRawArguments(LinkedList)}
* |-> {@link #expandArguments(LinkedList)}
* | \-> {@link #expandArgument(String)}*
* \-> {@link #processArguments(LinkedList)} * \-> {@link #processArguments(LinkedList)}
* \-> {@link #processArgument(PathData)}* * |-> {@link #processArgument(PathData)}*
* \-> {@link #processPathArgument(PathData)} * | |-> {@link #processPathArgument(PathData)}
* \-> {@link #processPaths(PathData, PathData...)} * | \-> {@link #processPaths(PathData, PathData...)}
* \-> {@link #processPath(PathData)}* * | \-> {@link #processPath(PathData)}*
* \-> {@link #processNonexistentPath(PathData)} * \-> {@link #processNonexistentPath(PathData)}
* </pre> * </pre>
* Most commands will chose to implement just * Most commands will chose to implement just
@ -144,7 +145,7 @@ public int run(String...argv) {
"DEPRECATED: Please use '"+ getReplacementCommand() + "' instead."); "DEPRECATED: Please use '"+ getReplacementCommand() + "' instead.");
} }
processOptions(args); processOptions(args);
processArguments(expandArguments(args)); processRawArguments(args);
} catch (IOException e) { } catch (IOException e) {
displayError(e); displayError(e);
} }
@ -170,6 +171,19 @@ public int run(String...argv) {
*/ */
protected void processOptions(LinkedList<String> args) throws IOException {} protected void processOptions(LinkedList<String> args) throws IOException {}
/**
* Allows commands that don't use paths to handle the raw arguments.
* Default behavior is to expand the arguments via
* {@link #expandArguments(LinkedList)} and pass the resulting list to
* {@link #processArguments(LinkedList)}
* @param args the list of argument strings
* @throws IOException
*/
protected void processRawArguments(LinkedList<String> args)
throws IOException {
processArguments(expandArguments(args));
}
/** /**
* Expands a list of arguments into {@link PathData} objects. The default * Expands a list of arguments into {@link PathData} objects. The default
* behavior is to call {@link #expandArgument(String)} on each element * behavior is to call {@link #expandArgument(String)} on each element
@ -353,7 +367,26 @@ public void displayError(String message) {
* @param message warning message to display * @param message warning message to display
*/ */
public void displayWarning(String message) { public void displayWarning(String message) {
err.println(getCommandName() + ": " + message); err.println(getName() + ": " + message);
}
/**
* The name of the command. Will first try to use the assigned name
* else fallback to the command's preferred name
* @return name of the command
*/
public String getName() {
return (name == null)
? getCommandField("NAME")
: name.startsWith("-") ? name.substring(1) : name; // this is a historical method
}
/**
* Define the name of the command.
* @param name as invoked
*/
public void setName(String name) {
this.name = name;
} }
/** /**
@ -361,7 +394,7 @@ public void displayWarning(String message) {
* @return "name options" * @return "name options"
*/ */
public String getUsage() { public String getUsage() {
String cmd = "-" + getCommandName(); String cmd = "-" + getName();
String usage = isDeprecated() ? "" : getCommandField("USAGE"); String usage = isDeprecated() ? "" : getCommandField("USAGE");
return usage.isEmpty() ? cmd : cmd + " " + usage; return usage.isEmpty() ? cmd : cmd + " " + usage;
} }
@ -400,9 +433,10 @@ public String getReplacementCommand() {
private String getCommandField(String field) { private String getCommandField(String field) {
String value; String value;
try { try {
value = (String)this.getClass().getField(field).get(null); value = this.getClass().getField(field).get(this).toString();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(StringUtils.stringifyException(e)); throw new RuntimeException(
"failed to get " + this.getClass().getSimpleName()+"."+field, e);
} }
return value; return value;
} }

View File

@ -19,7 +19,8 @@
package org.apache.hadoop.fs.shell; package org.apache.hadoop.fs.shell;
import java.util.Arrays; import java.util.Arrays;
import java.util.Hashtable; import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
@ -35,8 +36,11 @@
@InterfaceStability.Unstable @InterfaceStability.Unstable
public class CommandFactory extends Configured implements Configurable { public class CommandFactory extends Configured implements Configurable {
private Hashtable<String, Class<? extends Command>> classMap = private Map<String, Class<? extends Command>> classMap =
new Hashtable<String, Class<? extends Command>>(); new HashMap<String, Class<? extends Command>>();
private Map<String, Command> objectMap =
new HashMap<String, Command>();
/** Factory constructor for commands */ /** Factory constructor for commands */
public CommandFactory() { public CommandFactory() {
@ -79,14 +83,20 @@ public void addClass(Class<? extends Command> cmdClass, String ... names) {
} }
/** /**
* Returns the class implementing the given command. The * Register the given object as handling the given list of command
* class must have been registered via * names. Avoid calling this method and use
* {@link #addClass(Class, String...)} * {@link #addClass(Class, String...)} whenever possible to avoid
* @param cmd name of the command * startup overhead from excessive command object instantiations. This
* @return instance of the requested command * method is intended only for handling nested non-static classes that
* are re-usable. Namely -help/-usage.
* @param cmdObject the object implementing the command names
* @param names one or more command names that will invoke this class
*/ */
protected Class<? extends Command> getClass(String cmd) { public void addObject(Command cmdObject, String ... names) {
return classMap.get(cmd); for (String name : names) {
objectMap.put(name, cmdObject);
classMap.put(name, null); // just so it shows up in the list of commands
}
} }
/** /**
@ -109,11 +119,13 @@ public Command getInstance(String cmd) {
public Command getInstance(String cmdName, Configuration conf) { public Command getInstance(String cmdName, Configuration conf) {
if (conf == null) throw new NullPointerException("configuration is null"); if (conf == null) throw new NullPointerException("configuration is null");
Command instance = null; Command instance = objectMap.get(cmdName);
Class<? extends Command> cmdClass = getClass(cmdName); if (instance == null) {
Class<? extends Command> cmdClass = classMap.get(cmdName);
if (cmdClass != null) { if (cmdClass != null) {
instance = ReflectionUtils.newInstance(cmdClass, conf); instance = ReflectionUtils.newInstance(cmdClass, conf);
instance.setCommandName(cmdName); instance.setName(cmdName);
}
} }
return instance; return instance;
} }

View File

@ -54,9 +54,7 @@ public static void registerCommands(CommandFactory factory) {
private boolean showQuotas; private boolean showQuotas;
/** Constructor */ /** Constructor */
public Count() { public Count() {}
setCommandName(NAME);
}
/** Constructor /** Constructor
* @deprecated invoke via {@link FsShell} * @deprecated invoke via {@link FsShell}
@ -67,7 +65,6 @@ public Count() {
@Deprecated @Deprecated
public Count(String[] cmd, int pos, Configuration conf) { public Count(String[] cmd, int pos, Configuration conf) {
super(conf); super(conf);
setCommandName(NAME);
this.args = Arrays.copyOfRange(cmd, pos, cmd.length); this.args = Arrays.copyOfRange(cmd, pos, cmd.length);
} }

View File

@ -100,7 +100,7 @@ protected InputStream getInputStream(PathData item) throws IOException {
*/ */
public static class Text extends Cat { public static class Text extends Cat {
public static final String NAME = "text"; public static final String NAME = "text";
public static final String SHORT_USAGE = Cat.USAGE; public static final String USAGE = Cat.USAGE;
public static final String DESCRIPTION = public static final String DESCRIPTION =
"Takes a source file and outputs the file in text format.\n" + "Takes a source file and outputs the file in text format.\n" +
"The allowed formats are zip and TextRecordInputStream."; "The allowed formats are zip and TextRecordInputStream.";

View File

@ -65,8 +65,10 @@ protected FsCommand(Configuration conf) {
super(conf); super(conf);
} }
// historical abstract method in Command
@Override
public String getCommandName() { public String getCommandName() {
return name.startsWith("-") ? name.substring(1) : name; return getName();
} }
// abstract method that normally is invoked by runall() which is // abstract method that normally is invoked by runall() which is

View File

@ -39,7 +39,7 @@
<comparators> <comparators>
<comparator> <comparator>
<type>SubstringComparator</type> <type>SubstringComparator</type>
<expected-output>hadoop fs is the command to execute fs commands. The full syntax is</expected-output> <expected-output>Usage: hadoop fs [generic options]</expected-output>
</comparator> </comparator>
</comparators> </comparators>
</test> </test>
@ -730,7 +730,7 @@
<comparators> <comparators>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
<expected-output>^-help \[cmd\]:( |\t)*Displays help for given command or all commands if none( )*</expected-output> <expected-output>^-help \[cmd ...\]:( |\t)*Displays help for given command or all commands if none( )*</expected-output>
</comparator> </comparator>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>