HADOOP-7286. Refactor the du/dus/df commands to conform to new FsCommand class. Contributed by Daryn Sharp.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1103968 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Todd Lipcon 2011-05-17 00:53:51 +00:00
parent 4d205d50f6
commit b04fb035ec
9 changed files with 330 additions and 275 deletions

View File

@ -158,6 +158,9 @@ Trunk (unreleased changes)
HADOOP-7291. Update Hudson job not to run test-contrib. (Nigel Daley via eli) HADOOP-7291. Update Hudson job not to run test-contrib. (Nigel Daley via eli)
HADOOP-7286. Refactor the du/dus/df commands to conform to new FsCommand
class. (Daryn Sharp via todd)
OPTIMIZATIONS OPTIMIZATIONS
BUG FIXES BUG FIXES

View File

@ -38,7 +38,6 @@ 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.CommandFormat; import org.apache.hadoop.fs.shell.CommandFormat;
import org.apache.hadoop.fs.shell.FsCommand; import org.apache.hadoop.fs.shell.FsCommand;
import org.apache.hadoop.fs.shell.PathData;
import org.apache.hadoop.fs.shell.PathExceptions.PathNotFoundException; import org.apache.hadoop.fs.shell.PathExceptions.PathNotFoundException;
import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.ipc.RPC; import org.apache.hadoop.ipc.RPC;
@ -64,7 +63,6 @@ public class FsShell extends Configured implements Tool {
static final String GET_SHORT_USAGE = "-get [-ignoreCrc] [-crc] <src> <localdst>"; static final String GET_SHORT_USAGE = "-get [-ignoreCrc] [-crc] <src> <localdst>";
static final String COPYTOLOCAL_SHORT_USAGE = GET_SHORT_USAGE.replace( static final String COPYTOLOCAL_SHORT_USAGE = GET_SHORT_USAGE.replace(
"-get", "-copyToLocal"); "-get", "-copyToLocal");
static final String DU_USAGE="-du [-s] [-h] <paths...>";
/** /**
*/ */
@ -310,128 +308,6 @@ public class FsShell extends Configured implements Tool {
System.err.println("Option '-moveToLocal' is not implemented yet."); System.err.println("Option '-moveToLocal' is not implemented yet.");
} }
/**
* Show the size of a partition in the filesystem that contains
* the specified <i>path</i>.
* @param path a path specifying the source partition. null means /.
* @throws IOException
*/
void df(String path) throws IOException {
if (path == null) path = "/";
final Path srcPath = new Path(path);
final FileSystem srcFs = srcPath.getFileSystem(getConf());
if (! srcFs.exists(srcPath)) {
throw new PathNotFoundException(path);
}
final FsStatus stats = srcFs.getStatus(srcPath);
final int PercentUsed = (int)(100.0f * (float)stats.getUsed() / (float)stats.getCapacity());
System.out.println("Filesystem\t\tSize\tUsed\tAvail\tUse%");
System.out.printf("%s\t\t%d\t%d\t%d\t%d%%\n",
path,
stats.getCapacity(), stats.getUsed(), stats.getRemaining(),
PercentUsed);
}
/**
* Show the size of all files that match the file pattern <i>src</i>
* @param cmd
* @param pos ignore anything before this pos in cmd
* @throws IOException
* @see org.apache.hadoop.fs.FileSystem#globStatus(Path)
*/
void du(String[] cmd, int pos) throws IOException {
CommandFormat c = new CommandFormat(
"du", 0, Integer.MAX_VALUE, "h", "s");
List<String> params;
try {
params = c.parse(cmd, pos);
} catch (IllegalArgumentException iae) {
System.err.println("Usage: java FsShell " + DU_USAGE);
throw iae;
}
boolean humanReadable = c.getOpt("h");
boolean summary = c.getOpt("s");
// Default to cwd
if (params.isEmpty()) {
params.add(".");
}
List<UsagePair> usages = new ArrayList<UsagePair>();
for (String src : params) {
Path srcPath = new Path(src);
FileSystem srcFs = srcPath.getFileSystem(getConf());
FileStatus globStatus[] = srcFs.globStatus(srcPath);
FileStatus statusToPrint[];
if (summary) {
statusToPrint = globStatus;
} else {
Path statPaths[] = FileUtil.stat2Paths(globStatus, srcPath);
try {
statusToPrint = srcFs.listStatus(statPaths);
} catch(FileNotFoundException fnfe) {
statusToPrint = null;
}
}
if ((statusToPrint == null) || ((statusToPrint.length == 0) &&
(!srcFs.exists(srcPath)))){
throw new PathNotFoundException(src);
}
if (!summary) {
System.out.println("Found " + statusToPrint.length + " items");
}
for (FileStatus stat : statusToPrint) {
long length;
if (summary || stat.isDirectory()) {
length = srcFs.getContentSummary(stat.getPath()).getLength();
} else {
length = stat.getLen();
}
usages.add(new UsagePair(String.valueOf(stat.getPath()), length));
}
}
printUsageSummary(usages, humanReadable);
}
/**
* Show the summary disk usage of each dir/file
* that matches the file pattern <i>src</i>
* @param cmd
* @param pos ignore anything before this pos in cmd
* @throws IOException
* @see org.apache.hadoop.fs.FileSystem#globStatus(Path)
*/
void dus(String[] cmd, int pos) throws IOException {
String newcmd[] = new String[cmd.length + 1];
System.arraycopy(cmd, 0, newcmd, 0, cmd.length);
newcmd[cmd.length] = "-s";
du(newcmd, pos);
}
private void printUsageSummary(List<UsagePair> usages,
boolean humanReadable) {
int maxColumnWidth = 0;
for (UsagePair usage : usages) {
String toPrint = humanReadable ?
StringUtils.humanReadableInt(usage.bytes) : String.valueOf(usage.bytes);
if (toPrint.length() > maxColumnWidth) {
maxColumnWidth = toPrint.length();
}
}
for (UsagePair usage : usages) {
String toPrint = humanReadable ?
StringUtils.humanReadableInt(usage.bytes) : String.valueOf(usage.bytes);
System.out.printf("%-"+ (maxColumnWidth + BORDER) +"s", toPrint);
System.out.println(usage.path);
}
}
/** /**
* Move files that match the file pattern <i>srcf</i> * Move files that match the file pattern <i>srcf</i>
* to a destination file. * to a destination file.
@ -639,8 +515,8 @@ public class FsShell extends Configured implements Tool {
String summary = "hadoop fs is the command to execute fs commands. " + String summary = "hadoop fs is the command to execute fs commands. " +
"The full syntax is: \n\n" + "The full syntax is: \n\n" +
"hadoop fs [-fs <local | file system URI>] [-conf <configuration file>]\n\t" + "hadoop fs [-fs <local | file system URI>] [-conf <configuration file>]\n\t" +
"[-D <property=value>] [-df [<path>]] [-du [-s] [-h] <path>]\n\t" + "[-D <property=value>]\n\t" +
"[-dus <path>] [-mv <src> <dst>] [-cp <src> <dst>]\n\t" + "[-mv <src> <dst>] [-cp <src> <dst>]\n\t" +
"[-put <localsrc> ... <dst>] [-copyFromLocal <localsrc> ... <dst>]\n\t" + "[-put <localsrc> ... <dst>] [-copyFromLocal <localsrc> ... <dst>]\n\t" +
"[-moveFromLocal <localsrc> ... <dst>] [" + "[-moveFromLocal <localsrc> ... <dst>] [" +
GET_SHORT_USAGE + "\n\t" + GET_SHORT_USAGE + "\n\t" +
@ -662,25 +538,6 @@ public class FsShell extends Configured implements Tool {
"\t\tappear first on the command line. Exactly one additional\n" + "\t\tappear first on the command line. Exactly one additional\n" +
"\t\targument must be specified. \n"; "\t\targument must be specified. \n";
String df = "-df [<path>]: \tShows the capacity, free and used space of the filesystem.\n"+
"\t\tIf the filesystem has multiple partitions, and no path to a particular partition\n"+
"\t\tis specified, then the status of the root partitions will be shown.\n";
String du = "-du [-s] [-h] <path>: \tShow the amount of space, in bytes, used by the files that \n" +
"\t\tmatch the specified file pattern. The following flags are optional:\n" +
"\t\t -s Rather than showing the size of each individual file that\n" +
"\t\t matches the pattern, shows the total (summary) size.\n" +
"\t\t -h Formats the sizes of files in a human-readable fashion\n" +
"\t\t rather than a number of bytes.\n" +
"\n" +
"\t\tNote that, even without the -s option, this only shows size summaries\n" +
"\t\tone level deep into a directory.\n" +
"\t\tThe output is in the form \n" +
"\t\t\tsize\tname(full path)\n";
String dus = "-dus <path>: \tShow the amount of space, in bytes, used by the files that \n" +
"\t\tmatch the specified file pattern. This is equivalent to -du -s above.\n";
String mv = "-mv <src> <dst>: Move files that match the specified file pattern <src>\n" + String mv = "-mv <src> <dst>: Move files that match the specified file pattern <src>\n" +
"\t\tto a destination <dst>. When moving multiple files, the \n" + "\t\tto a destination <dst>. When moving multiple files, the \n" +
"\t\tdestination must be a directory. \n"; "\t\tdestination must be a directory. \n";
@ -720,12 +577,6 @@ public class FsShell extends Configured implements Tool {
System.out.println(conf); System.out.println(conf);
} else if ("D".equals(cmd)) { } else if ("D".equals(cmd)) {
System.out.println(D); System.out.println(D);
} else if ("df".equals(cmd)) {
System.out.println(df);
} else if ("du".equals(cmd)) {
System.out.println(du);
} else if ("dus".equals(cmd)) {
System.out.println(dus);
} else if ("mv".equals(cmd)) { } else if ("mv".equals(cmd)) {
System.out.println(mv); System.out.println(mv);
} else if ("cp".equals(cmd)) { } else if ("cp".equals(cmd)) {
@ -750,14 +601,13 @@ public class FsShell extends Configured implements Tool {
System.out.println(summary); System.out.println(summary);
for (String thisCmdName : commandFactory.getNames()) { for (String thisCmdName : commandFactory.getNames()) {
instance = commandFactory.getInstance(thisCmdName); instance = commandFactory.getInstance(thisCmdName);
System.out.println("\t[" + instance.getUsage() + "]"); if (!instance.isDeprecated()) {
System.out.println("\t[" + instance.getUsage() + "]");
}
} }
System.out.println("\t[-help [cmd]]\n"); System.out.println("\t[-help [cmd]]\n");
System.out.println(fs); System.out.println(fs);
System.out.println(df);
System.out.println(du);
System.out.println(dus);
System.out.println(mv); System.out.println(mv);
System.out.println(cp); System.out.println(cp);
System.out.println(put); System.out.println(put);
@ -768,9 +618,11 @@ public class FsShell extends Configured implements Tool {
System.out.println(moveToLocal); System.out.println(moveToLocal);
for (String thisCmdName : commandFactory.getNames()) { for (String thisCmdName : commandFactory.getNames()) {
printHelp(commandFactory.getInstance(thisCmdName)); instance = commandFactory.getInstance(thisCmdName);
if (!instance.isDeprecated()) {
printHelp(instance);
}
} }
System.out.println(help); System.out.println(help);
} }
} }
@ -791,34 +643,6 @@ public class FsShell extends Configured implements Tool {
} }
} }
/**
* Apply operation specified by 'cmd' on all parameters
* starting from argv[startindex].
*/
private int doall(String cmd, String argv[], int startindex) {
int exitCode = 0;
int i = startindex;
//
// for each source file, issue the command
//
for (; i < argv.length; i++) {
try {
//
// issue the command to the fs
//
if ("-df".equals(cmd)) {
df(argv[i]);
}
} catch (IOException e) {
LOG.debug("Error", e);
exitCode = -1;
displayError(cmd, e);
}
}
return exitCode;
}
/** /**
* Displays format of commands. * Displays format of commands.
* *
@ -838,12 +662,6 @@ public class FsShell extends Configured implements Tool {
} else if ("-D".equals(cmd)) { } else if ("-D".equals(cmd)) {
System.err.println("Usage: java FsShell" + System.err.println("Usage: java FsShell" +
" [-D <[property=value>]"); " [-D <[property=value>]");
} else if ("-du".equals(cmd) || "-dus".equals(cmd)) {
System.err.println("Usage: java FsShell" +
" [" + cmd + " <path>]");
} else if ("-df".equals(cmd) ) {
System.err.println("Usage: java FsShell" +
" [" + cmd + " [<path>]]");
} else if ("-mv".equals(cmd) || "-cp".equals(cmd)) { } else if ("-mv".equals(cmd) || "-cp".equals(cmd)) {
System.err.println("Usage: java FsShell" + System.err.println("Usage: java FsShell" +
" [" + cmd + " <src> <dst>]"); " [" + cmd + " <src> <dst>]");
@ -860,9 +678,6 @@ public class FsShell extends Configured implements Tool {
" [" + cmd + " [-crc] <src> <localdst>]"); " [" + cmd + " [-crc] <src> <localdst>]");
} else { } else {
System.err.println("Usage: java FsShell"); System.err.println("Usage: java FsShell");
System.err.println(" [-df [<path>]]");
System.err.println(" [-du [-s] [-h] <path>]");
System.err.println(" [-dus <path>]");
System.err.println(" [-mv <src> <dst>]"); System.err.println(" [-mv <src> <dst>]");
System.err.println(" [-cp <src> <dst>]"); System.err.println(" [-cp <src> <dst>]");
System.err.println(" [-put <localsrc> ... <dst>]"); System.err.println(" [-put <localsrc> ... <dst>]");
@ -872,8 +687,10 @@ public class FsShell extends Configured implements Tool {
System.err.println(" [" + COPYTOLOCAL_SHORT_USAGE + "]"); System.err.println(" [" + COPYTOLOCAL_SHORT_USAGE + "]");
System.err.println(" [-moveToLocal [-crc] <src> <localdst>]"); System.err.println(" [-moveToLocal [-crc] <src> <localdst>]");
for (String name : commandFactory.getNames()) { for (String name : commandFactory.getNames()) {
instance = commandFactory.getInstance(name); instance = commandFactory.getInstance(name);
System.err.println(" [" + instance.getUsage() + "]"); if (!instance.isDeprecated()) {
System.err.println(" [" + instance.getUsage() + "]");
}
} }
System.err.println(" [-help [cmd]]"); System.err.println(" [-help [cmd]]");
System.err.println(); System.err.println();
@ -967,16 +784,6 @@ public class FsShell extends Configured implements Tool {
exitCode = rename(argv, getConf()); exitCode = rename(argv, getConf());
} else if ("-cp".equals(cmd)) { } else if ("-cp".equals(cmd)) {
exitCode = copy(argv, getConf()); exitCode = copy(argv, getConf());
} else if ("-df".equals(cmd)) {
if (argv.length-1 > 0) {
exitCode = doall(cmd, argv, i);
} else {
df(null);
}
} else if ("-du".equals(cmd)) {
du(argv, i);
} else if ("-dus".equals(cmd)) {
dus(argv, i);
} else if ("-help".equals(cmd)) { } else if ("-help".equals(cmd)) {
if (i < argv.length) { if (i < argv.length) {
printHelp(argv[i]); printHelp(argv[i]);
@ -1043,17 +850,4 @@ public class FsShell extends Configured implements Tool {
} }
System.exit(res); System.exit(res);
} }
/**
* Utility class for a line of du output
*/
private static class UsagePair {
public String path;
public long bytes;
public UsagePair(String path, long bytes) {
this.path = path;
this.bytes = bytes;
}
}
} }

View File

@ -139,6 +139,10 @@ abstract public class Command extends Configured {
public int run(String...argv) { public int run(String...argv) {
LinkedList<String> args = new LinkedList<String>(Arrays.asList(argv)); LinkedList<String> args = new LinkedList<String>(Arrays.asList(argv));
try { try {
if (isDeprecated()) {
displayWarning(
"DEPRECATED: Please use '"+ getReplacementCommand() + "' instead.");
}
processOptions(args); processOptions(args);
processArguments(expandArguments(args)); processArguments(expandArguments(args));
} catch (IOException e) { } catch (IOException e) {
@ -358,7 +362,7 @@ abstract public class Command extends Configured {
*/ */
public String getUsage() { public String getUsage() {
String cmd = "-" + getCommandName(); String cmd = "-" + getCommandName();
String usage = getCommandField("USAGE"); String usage = isDeprecated() ? "" : getCommandField("USAGE");
return usage.isEmpty() ? cmd : cmd + " " + usage; return usage.isEmpty() ? cmd : cmd + " " + usage;
} }
@ -367,7 +371,25 @@ abstract public class Command extends Configured {
* @return text of the usage * @return text of the usage
*/ */
public String getDescription() { public String getDescription() {
return getCommandField("DESCRIPTION"); return isDeprecated()
? "(DEPRECATED) Same as '" + getReplacementCommand() + "'"
: getCommandField("DESCRIPTION");
}
/**
* Is the command deprecated?
* @return boolean
*/
public final boolean isDeprecated() {
return (getReplacementCommand() != null);
}
/**
* The replacement for a deprecated command
* @return null if not deprecated, else alternative command
*/
public String getReplacementCommand() {
return null;
} }
/** /**

View File

@ -43,12 +43,13 @@ class Delete extends FsCommand {
/** remove non-directory paths */ /** remove non-directory paths */
public static class Rm extends FsCommand { public static class Rm extends FsCommand {
public static final String NAME = "rm"; public static final String NAME = "rm";
public static final String USAGE = "[-skipTrash] <src> ..."; public static final String USAGE = "[-r|-R] [-skipTrash] <src> ...";
public static final String DESCRIPTION = public static final String DESCRIPTION =
"Delete all files that match the specified file pattern.\n" + "Delete all files that match the specified file pattern.\n" +
"Equivalent to the Unix command \"rm <src>\"\n" + "Equivalent to the Unix command \"rm <src>\"\n" +
"-skipTrash option bypasses trash, if enabled, and immediately\n" + "-skipTrash option bypasses trash, if enabled, and immediately\n" +
"deletes <src>"; "deletes <src>\n" +
" -[rR] Recursively deletes directories";
private boolean skipTrash = false; private boolean skipTrash = false;
private boolean deleteDirs = false; private boolean deleteDirs = false;
@ -56,9 +57,9 @@ class Delete extends FsCommand {
@Override @Override
protected void processOptions(LinkedList<String> args) throws IOException { protected void processOptions(LinkedList<String> args) throws IOException {
CommandFormat cf = new CommandFormat( CommandFormat cf = new CommandFormat(
null, 1, Integer.MAX_VALUE, "R", "skipTrash"); null, 1, Integer.MAX_VALUE, "r", "R", "skipTrash");
cf.parse(args); cf.parse(args);
deleteDirs = cf.getOpt("R"); deleteDirs = cf.getOpt("r") || cf.getOpt("R");
skipTrash = cf.getOpt("skipTrash"); skipTrash = cf.getOpt("skipTrash");
} }
@ -95,17 +96,16 @@ class Delete extends FsCommand {
/** remove any path */ /** remove any path */
static class Rmr extends Rm { static class Rmr extends Rm {
public static final String NAME = "rmr"; public static final String NAME = "rmr";
public static final String USAGE = Rm.USAGE;
public static final String DESCRIPTION =
"Remove all directories which match the specified file\n" +
"pattern. Equivalent to the Unix command \"rm -rf <src>\"\n" +
"-skipTrash option bypasses trash, if enabled, and immediately\n" +
"deletes <src>";
protected void processOptions(LinkedList<String> args) throws IOException { protected void processOptions(LinkedList<String> args) throws IOException {
args.addFirst("-R"); args.addFirst("-r");
super.processOptions(args); super.processOptions(args);
} }
@Override
public String getReplacementCommand() {
return "rm -r";
}
} }
/** empty the trash */ /** empty the trash */

View File

@ -48,6 +48,7 @@ abstract public class FsCommand extends Command {
factory.registerCommands(Delete.class); factory.registerCommands(Delete.class);
factory.registerCommands(Display.class); factory.registerCommands(Display.class);
factory.registerCommands(FsShellPermissions.class); factory.registerCommands(FsShellPermissions.class);
factory.registerCommands(FsUsage.class);
factory.registerCommands(Ls.class); factory.registerCommands(Ls.class);
factory.registerCommands(Mkdir.class); factory.registerCommands(Mkdir.class);
factory.registerCommands(SetReplication.class); factory.registerCommands(SetReplication.class);

View File

@ -0,0 +1,263 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.fs.shell;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.fs.FsStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.util.StringUtils;
/** Base class for commands related to viewing filesystem usage, such as
* du and df
*/
@InterfaceAudience.Private
@InterfaceStability.Evolving
class FsUsage extends FsCommand {
public static void registerCommands(CommandFactory factory) {
factory.addClass(Df.class, "-df");
factory.addClass(Du.class, "-du");
factory.addClass(Dus.class, "-dus");
}
protected boolean humanReadable = false;
protected TableBuilder usagesTable;
protected String formatSize(long size) {
return humanReadable
? StringUtils.humanReadableInt(size)
: String.valueOf(size);
}
/** Show the size of a partition in the filesystem */
public static class Df extends FsUsage {
public static final String NAME = "df";
public static final String USAGE = "[<path> ...]";
public static final String DESCRIPTION =
"Shows the capacity, free and used space of the filesystem.\n"+
"If the filesystem has multiple partitions, and no path to a\n" +
"particular partition is specified, then the status of the root\n" +
"partitions will be shown.";
@Override
protected void processOptions(LinkedList<String> args)
throws IOException {
CommandFormat cf = new CommandFormat(null, 0, Integer.MAX_VALUE, "h");
cf.parse(args);
humanReadable = cf.getOpt("h");
if (args.isEmpty()) args.add(Path.SEPARATOR);
}
@Override
protected void processArguments(LinkedList<PathData> args)
throws IOException {
usagesTable = new TableBuilder(
"Filesystem", "Size", "Used", "Available", "Use%");
usagesTable.setRightAlign(1, 2, 3, 4);
super.processArguments(args);
if (!usagesTable.isEmpty()) {
usagesTable.printToStream(out);
}
}
@Override
protected void processPath(PathData item) throws IOException {
FsStatus fsStats = item.fs.getStatus(item.path);
long size = fsStats.getCapacity();
long used = fsStats.getUsed();
long free = fsStats.getRemaining();
usagesTable.addRow(
item.fs.getUri(),
formatSize(size),
formatSize(used),
formatSize(free),
StringUtils.formatPercent((double)used/(double)size, 0)
);
}
}
/** show disk usage */
public static class Du extends FsUsage {
public static final String NAME = "du";
public static final String USAGE = "[-s] [-h] <path> ...";
public static final String DESCRIPTION =
"Show the amount of space, in bytes, used by the files that\n" +
"match the specified file pattern. The following flags are optional:\n" +
" -s Rather than showing the size of each individual file that\n" +
" matches the pattern, shows the total (summary) size.\n" +
" -h Formats the sizes of files in a human-readable fashion\n" +
" rather than a number of bytes.\n\n" +
"Note that, even without the -s option, this only shows size summaries\n" +
"one level deep into a directory.\n" +
"The output is in the form \n" +
"\tsize\tname(full path)\n";
protected boolean summary = false;
@Override
protected void processOptions(LinkedList<String> args) throws IOException {
CommandFormat cf = new CommandFormat(null, 0, Integer.MAX_VALUE, "h", "s");
cf.parse(args);
humanReadable = cf.getOpt("h");
summary = cf.getOpt("s");
if (args.isEmpty()) args.add(Path.CUR_DIR);
}
@Override
protected void processPathArgument(PathData item) throws IOException {
usagesTable = new TableBuilder(2);
// go one level deep on dirs from cmdline unless in summary mode
if (!summary && item.stat.isDirectory()) {
recursePath(item);
} else {
super.processPathArgument(item);
}
usagesTable.printToStream(out);
}
@Override
protected void processPath(PathData item) throws IOException {
long length;
if (item.stat.isDirectory()) {
length = item.fs.getContentSummary(item.path).getLength();
} else {
length = item.stat.getLen();
}
usagesTable.addRow(formatSize(length), item);
}
}
/** show disk usage summary */
public static class Dus extends Du {
public static final String NAME = "dus";
@Override
protected void processOptions(LinkedList<String> args) throws IOException {
args.addFirst("-s");
super.processOptions(args);
}
@Override
public String getReplacementCommand() {
return "du -s";
}
}
/**
* Creates a table of aligned values based on the maximum width of each
* column as a string
*/
private static class TableBuilder {
protected boolean hasHeader = false;
protected List<String[]> rows;
protected int[] widths;
protected boolean[] rightAlign;
/**
* Create a table w/o headers
* @param columns number of columns
*/
public TableBuilder(int columns) {
rows = new ArrayList<String[]>();
widths = new int[columns];
rightAlign = new boolean[columns];
}
/**
* Create a table with headers
* @param headers list of headers
*/
public TableBuilder(Object ... headers) {
this(headers.length);
this.addRow(headers);
hasHeader = true;
}
/**
* Change the default left-align of columns
* @param indexes of columns to right align
*/
public void setRightAlign(int ... indexes) {
for (int i : indexes) rightAlign[i] = true;
}
/**
* Add a row of objects to the table
* @param objects the values
*/
public void addRow(Object ... objects) {
String[] row = new String[widths.length];
for (int col=0; col < widths.length; col++) {
row[col] = String.valueOf(objects[col]);
widths[col] = Math.max(widths[col], row[col].length());
}
rows.add(row);
}
/**
* Render the table to a stream
* @param out PrintStream for output
*/
public void printToStream(PrintStream out) {
if (isEmpty()) return;
StringBuilder fmt = new StringBuilder();
for (int i=0; i < widths.length; i++) {
if (fmt.length() != 0) fmt.append(" ");
if (rightAlign[i]) {
fmt.append("%"+widths[i]+"s");
} else if (i != widths.length-1) {
fmt.append("%-"+widths[i]+"s");
} else {
// prevent trailing spaces if the final column is left-aligned
fmt.append("%s");
}
}
for (Object[] row : rows) {
out.println(String.format(fmt.toString(), row));
}
}
/**
* Number of rows excluding header
* @return rows
*/
public int size() {
return rows.size() - (hasHeader ? 1 : 0);
}
/**
* Does table have any rows
* @return boolean
*/
public boolean isEmpty() {
return size() == 0;
}
}
}

View File

@ -41,7 +41,7 @@ class Ls extends FsCommand {
} }
public static final String NAME = "ls"; public static final String NAME = "ls";
public static final String USAGE = "[<path> ...]"; public static final String USAGE = "[-R] [<path> ...]";
public static final String DESCRIPTION = public static final String DESCRIPTION =
"List the contents that match the specified file pattern. If\n" + "List the contents that match the specified file pattern. If\n" +
"path is not specified, the contents of /user/<currentUser>\n" + "path is not specified, the contents of /user/<currentUser>\n" +
@ -50,7 +50,8 @@ class Ls extends FsCommand {
"and file entries are of the form \n" + "and file entries are of the form \n" +
"\tfileName(full path) <r n> size \n" + "\tfileName(full path) <r n> size \n" +
"where n is the number of replicas specified for the file \n" + "where n is the number of replicas specified for the file \n" +
"and size is the size of the file, in bytes."; "and size is the size of the file, in bytes.\n" +
" -R Recursively list the contents of directories";
protected static final SimpleDateFormat dateFormat = protected static final SimpleDateFormat dateFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm"); new SimpleDateFormat("yyyy-MM-dd HH:mm");
@ -135,12 +136,6 @@ class Ls extends FsCommand {
*/ */
public static class Lsr extends Ls { public static class Lsr extends Ls {
public static final String NAME = "lsr"; public static final String NAME = "lsr";
public static final String USAGE = Ls.USAGE;
public static final String DESCRIPTION =
"Recursively list the contents that match the specified\n" +
"file pattern. Behaves very similarly to hadoop fs -ls,\n" +
"except that the data is shown for all the entries in the\n" +
"subtree.";
@Override @Override
protected void processOptions(LinkedList<String> args) protected void processOptions(LinkedList<String> args)
@ -148,5 +143,10 @@ class Ls extends FsCommand {
args.addFirst("-R"); args.addFirst("-R");
super.processOptions(args); super.processOptions(args);
} }
@Override
public String getReplacementCommand() {
return "ls -R";
}
} }
} }

View File

@ -93,7 +93,7 @@ class Stat extends FsCommand {
: (stat.isFile() ? "regular file" : "symlink")); : (stat.isFile() ? "regular file" : "symlink"));
break; break;
case 'n': case 'n':
buf.append(item); buf.append(item.path.getName());
break; break;
case 'o': case 'o':
buf.append(stat.getBlockSize()); buf.append(stat.getBlockSize());

View File

@ -54,7 +54,7 @@
<comparators> <comparators>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
<expected-output>^-ls \[&lt;path&gt; \.\.\.\]:( |\t)*List the contents that match the specified file pattern. If( )*</expected-output> <expected-output>^-ls \[-R\] \[&lt;path&gt; \.\.\.\]:( |\t)*List the contents that match the specified file pattern. If( )*</expected-output>
</comparator> </comparator>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
@ -101,19 +101,7 @@
<comparators> <comparators>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
<expected-output>^-lsr \[&lt;path&gt; \.\.\.\]:( |\t)*Recursively list the contents that match the specified( )*</expected-output> <expected-output>^-lsr:\s+\(DEPRECATED\) Same as 'ls -R'</expected-output>
</comparator>
<comparator>
<type>RegexpComparator</type>
<expected-output>^( |\t)*file pattern.( |\t)*Behaves very similarly to hadoop fs -ls,( )*</expected-output>
</comparator>
<comparator>
<type>RegexpComparator</type>
<expected-output>^( |\t)*except that the data is shown for all the entries in the( )*</expected-output>
</comparator>
<comparator>
<type>RegexpComparator</type>
<expected-output>^( |\t)*subtree.( )*</expected-output>
</comparator> </comparator>
</comparators> </comparators>
</test> </test>
@ -152,7 +140,7 @@
<comparators> <comparators>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
<expected-output>^-du \[-s\] \[-h\] &lt;path&gt;:\s+Show the amount of space, in bytes, used by the files that\s*</expected-output> <expected-output>^-du \[-s\] \[-h\] &lt;path&gt; \.\.\.:\s+Show the amount of space, in bytes, used by the files that\s*</expected-output>
</comparator> </comparator>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
@ -203,11 +191,7 @@
<comparators> <comparators>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
<expected-output>^-dus &lt;path&gt;:( |\t)*Show the amount of space, in bytes, used by the files that( )*</expected-output> <expected-output>^-dus:\s+\(DEPRECATED\) Same as 'du -s'</expected-output>
</comparator>
<comparator>
<type>RegexpComparator</type>
<expected-output>^( |\t)*match the specified file pattern. This is equivalent to -du -s above.</expected-output>
</comparator> </comparator>
</comparators> </comparators>
</test> </test>
@ -299,7 +283,7 @@
<comparators> <comparators>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
<expected-output>^-rm \[-skipTrash\] &lt;src&gt; \.\.\.:( |\t)*Delete all files that match the specified file pattern.( )*</expected-output> <expected-output>^-rm \[-r\|-R\] \[-skipTrash\] &lt;src&gt; \.\.\.:( |\t)*Delete all files that match the specified file pattern.( )*</expected-output>
</comparator> </comparator>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
@ -326,19 +310,7 @@
<comparators> <comparators>
<comparator> <comparator>
<type>RegexpComparator</type> <type>RegexpComparator</type>
<expected-output>^-rmr \[-skipTrash\] &lt;src&gt; \.\.\.:( |\t)*Remove all directories which match the specified file( )*</expected-output> <expected-output>^-rmr:\s+\(DEPRECATED\) Same as 'rm -r'</expected-output>
</comparator>
<comparator>
<type>RegexpComparator</type>
<expected-output>^( |\t)*pattern. Equivalent to the Unix command "rm -rf &lt;src&gt;"( )*</expected-output>
</comparator>
<comparator>
<type>RegexpComparator</type>
<expected-output>^( |\t)*-skipTrash option bypasses trash, if enabled, and immediately( )*</expected-output>
</comparator>
<comparator>
<type>RegexpComparator</type>
<expected-output>^( |\t)*deletes &lt;src&gt;( )*</expected-output>
</comparator> </comparator>
</comparators> </comparators>
</test> </test>