前言

在hadoop的FsShell命令中,预计非常多人比較经常使用的就是hadoop fs -ls,-lsr,-cat等等这种与Linux系统中差点儿一致的文件系统相关的命令.可是细致想想,这里还是有一些些的不同的.首先,从规模的本身来看,单机版的文件系统,文件数目少,内容不多,而HDFS则是一个分布式系统,里面能容纳巨大数量的文件文件夹.因此在这个前提之下,你假设任意运行ls或lsr命令,有的时候会得到恐怖的数据条数的显示记录,有的时候我们不得不通过Ctrl+C的方式中止命令.所以对于未知文件夹的命令运行,能否够在ls命令中添加显示限制的參数呢,这样能够控制一下文件记录信息的数量.这就是本文的一个出发点.

Ls命令工作流程

要想加入參数,就要先理解眼下Ls命令工作的原理和过程.以下我从源码的层面进行简单的分析.首先这里有个结构关系:

Ls-->FsCommand-->Command

从左到右依次为孩子到父亲.所以Command类是最基础的类,命令行操作的运行入口就在这里.进入到Command.java方法中,你会看到有以下这种方法:

/**
* Invokes the command handler. The default behavior is to process options,
* expand arguments, and then process each argument.
* <pre>
* run
* |-> {@link #processOptions(LinkedList)}
* \-> {@link #processRawArguments(LinkedList)}
* |-> {@link #expandArguments(LinkedList)}
* | \-> {@link #expandArgument(String)}*
* \-> {@link #processArguments(LinkedList)}
* |-> {@link #processArgument(PathData)}*
* | |-> {@link #processPathArgument(PathData)}
* | \-> {@link #processPaths(PathData, PathData...)}
* | \-> {@link #processPath(PathData)}*
* \-> {@link #processNonexistentPath(PathData)}
* </pre>
* Most commands will chose to implement just
* {@link #processOptions(LinkedList)} and {@link #processPath(PathData)}
*
* @param argv the list of command line arguments
* @return the exit code for the command
* @throws IllegalArgumentException if called with invalid arguments
*/
public int run(String...argv) {
LinkedList<String> args = new LinkedList<String>(Arrays.asList(argv));
try {
if (isDeprecated()) {
displayWarning(
"DEPRECATED: Please use '"+ getReplacementCommand() + "' instead.");
}
processOptions(args);
processRawArguments(args);
} catch (IOException e) {
displayError(e);
} return (numErrors == 0) ? exitCode : exitCodeForError();
}

首先会进行參数的预处理,在这里会把參数中的一些參数给剥离出来,由于这是一个抽象方法,所以终于的实现类在Ls.java中,代码例如以下:

  @Override
protected void processOptions(LinkedList<String> args)
throws IOException {
CommandFormat cf = new CommandFormat(0, Integer.MAX_VALUE, "d", "h", "R");
cf.parse(args);
dirRecurse = !cf.getOpt("d");
setRecursive(cf.getOpt("R") && dirRecurse);
humanReadable = cf.getOpt("h");
if (args.isEmpty()) args.add(Path.CUR_DIR);
}

把这些參数逐一取出,然后这些參数会从args列表中被移除,最后就会剩下详细的目标浏览文件或文件夹的參数.以下就会进入到这种方法中:

  /**
* 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));
}

然后在expandArguments中会做一层从文件字符串到PathData详细对象的转化

 /**
* Expands a list of arguments into {@link PathData} objects. The default
* behavior is to call {@link #expandArgument(String)} on each element
* which by default globs the argument. The loop catches IOExceptions,
* increments the error count, and displays the exception.
* @param args strings to expand into {@link PathData} objects
* @return list of all {@link PathData} objects the arguments
* @throws IOException if anything goes wrong...
*/
protected LinkedList<PathData> expandArguments(LinkedList<String> args)
throws IOException {
LinkedList<PathData> expandedArgs = new LinkedList<PathData>();
for (String arg : args) {
try {
expandedArgs.addAll(expandArgument(arg));
} catch (IOException e) { // other exceptions are probably nasty
displayError(e);
}
}
return expandedArgs;
}
  /**
* Expand the given argument into a list of {@link PathData} objects.
* The default behavior is to expand globs. Commands may override to
* perform other expansions on an argument.
* @param arg string pattern to expand
* @return list of {@link PathData} objects
* @throws IOException if anything goes wrong...
*/
protected List<PathData> expandArgument(String arg) throws IOException {
PathData[] items = PathData.expandAsGlob(arg, getConf());
if (items.length == 0) {
// it's a glob that failed to match
throw new PathNotFoundException(arg);
}
return Arrays.asList(items);
}

最后以最后的PathData列表的信息来到终于的processArgument方法

/**
* Processes the command's list of expanded arguments.
* {@link #processArgument(PathData)} will be invoked with each item
* in the list. The loop catches IOExceptions, increments the error
* count, and displays the exception.
* @param args a list of {@link PathData} to process
* @throws IOException if anything goes wrong...
*/
protected void processArguments(LinkedList<PathData> args)
throws IOException {
for (PathData arg : args) {
try {
processArgument(arg);
} catch (IOException e) {
displayError(e);
}
}
}

然后对每一个pathData信息运行处理操作

  /**
* Processes a {@link PathData} item, calling
* {@link #processPathArgument(PathData)} or
* {@link #processNonexistentPath(PathData)} on each item.
* @param item {@link PathData} item to process
* @throws IOException if anything goes wrong...
*/
protected void processArgument(PathData item) throws IOException {
if (item.exists) {
processPathArgument(item);
} else {
processNonexistentPath(item);
}
}

然后运行Ls.java中的processPathArgument方法

  @Override
protected void processPathArgument(PathData item) throws IOException {
// implicitly recurse once for cmdline directories
if (dirRecurse && item.stat.isDirectory()) {
recursePath(item);
} else {
super.processPathArgument(item);
}
}

在这里会进程是否为文件夹的推断,假设是文件夹则会进行递归推断一次,进行子文件夹文件的展示.我们直接看是单文件的处理,基础方法在Comman.java中定义.

  /**
* This is the last chance to modify an argument before going into the
* (possibly) recursive {@link #processPaths(PathData, PathData...)}
* -> {@link #processPath(PathData)} loop. Ex. ls and du use this to
* expand out directories.
* @param item a {@link PathData} representing a path which exists
* @throws IOException if anything goes wrong...
*/
protected void processPathArgument(PathData item) throws IOException {
// null indicates that the call is not via recursion, ie. there is
// no parent directory that was expanded
depth = 0;
processPaths(null, item);
}

然后processPaths又是在子类中详细实现

  @Override
protected void processPaths(PathData parent, PathData ... items)
throws IOException {
if (parent != null && !isRecursive() && items.length != 0) {
out.println("Found " + items.length + " items");
}
adjustColumnWidths(items);
super.processPaths(parent, items);
}

然后再次进行一个相似这种来回,运行processPaths方法

  /**
* Iterates over the given expanded paths and invokes
* {@link #processPath(PathData)} on each element. If "recursive" is true,
* will do a post-visit DFS on directories.
* @param parent if called via a recurse, will be the parent dir, else null
* @param items a list of {@link PathData} objects to process
* @throws IOException if anything goes wrong...
*/
protected void processPaths(PathData parent, PathData ... items)
throws IOException {
// TODO: this really should be iterative
for (PathData item : items) {
try {
processPath(item);
if (recursive && isPathRecursable(item)) {
recursePath(item);
}
postProcessPath(item);
} catch (IOException e) {
displayError(e);
}
}
}

最后展示的操作就是在这种方法中进行的

@Override
protected void processPath(PathData item) throws IOException {
FileStatus stat = item.stat;
String line = String.format(lineFormat,
(stat.isDirectory() ? "d" : "-"),
stat.getPermission() + (stat.getPermission().getAclBit() ? "+" : " "),
(stat.isFile() ? stat.getReplication() : "-"),
stat.getOwner(),
stat.getGroup(),
formatSize(stat.getLen()),
dateFormat.format(new Date(stat.getModificationTime())),
item
);
out.println(line);
}

到这里整个ls调用的流程就基本结束了,预计有些读者要被这来回的方法绕晕了,只是没有关系,我们主要知道终于控制文件显示的方法在哪里,稍稍改改就能够达到我们的目的.

Ls限制显示參数的加入

如今我来教大家怎样新增ls命令參数.首先定义參数说明

public static final String NAME = "ls";
public static final String USAGE = "[-d] [-h] [-R] [-l] [<path> ...]";
public static final String DESCRIPTION =
"List the contents that match the specified file pattern. If " +
"path is not specified, the contents of /user/<currentUser> " +
@@ -53,7 +55,9 @@ public static void registerCommands(CommandFactory factory) {
"-d: Directories are listed as plain files.\n" +
"-h: Formats the sizes of files in a human-readable fashion " +
"rather than a number of bytes.\n" +=
"-R: Recursively list the contents of directories.\n" +
"-l: The limited number of files records's info which would be " +
"displayed, the max value is 1024.\n";

定义相关变量

   protected int maxRepl = 3, maxLen = 10, maxOwner = 0, maxGroup = 0;
protected int limitedDisplayedNum = 1024;
protected int displayedRecordNum = 0;
protected String lineFormat;
protected boolean dirRecurse; protected boolean limitedDisplay = false;
protected boolean humanReadable = false;

默认最大显示数目1024个.然后在參数解析的方法中进行新增參数的解析

   @Override
protected void processOptions(LinkedList<String> args)
throws IOException {
CommandFormat cf = new CommandFormat(0, Integer.MAX_VALUE, "d", "h", "R", "l");
cf.parse(args);
dirRecurse = !cf.getOpt("d");
setRecursive(cf.getOpt("R") && dirRecurse);
humanReadable = cf.getOpt("h");
limitedDisplay = cf.getOpt("l");
if (args.isEmpty()) args.add(Path.CUR_DIR);
}

然后是最核心的修改,processPaths方法

protected void processPaths(PathData parent, PathData ... items)
if (parent != null && !isRecursive() && items.length != 0) {
out.println("Found " + items.length " items");
} PathData[] newItems;
if (limitedDisplay) {
int length = items.length;
if (length > limitedDisplayedNum) {
length = limitedDisplayedNum;
out.println("Found " + items.length + " items"
+ ", more than the limited displayed num " + limitedDisplayedNum);
}
newItems = new PathData[length]; for (int i = 0; i < length; i++) {
newItems[i] = items[i];
}
items = null;
} else {
newItems = items;
} adjustColumnWidths(newItems);
super.processPaths(parent, newItems);
}

逻辑不难. 以下是測试的一个样例,我在測试的jar包中设置了默认限制数目1个,然后用ls命令分别測试带參数与不带參数的情况,測试截图例如以下:

此部分代码已经提交至开源社区,编号HADOOP-12641.链接在文章尾部列出.

相关链接

Issue链接:https://issues.apache.org/jira/browse/HADOOP-12641

github patch链接:https://github.com/linyiqun/open-source-patch/blob/master/hadoop/HADOOP-12641/HADOOP-12641.001.patch

Hadoop Ls命令添加显示条数限制參数的更多相关文章

  1. 研究下JavaScript中的Rest參数和參数默认值

    研究下JavaScript中的Rest參数和參数默认值 本文将讨论使 JavaScript 函数更有表现力的两个特性:Rest 參数和參数默认值. Rest 參数 通常,我们须要创建一个可变參数的函数 ...

  2. struts2 全局拦截器,显示请求方法和參数

    后台系统中应该须要一个功能那就是将每一个请求的url地址和请求的參数log出来,方便系统调试和bug追踪,使用struts2时能够使用struts2的全局拦截器实现此功能: import java.u ...

  3. Python学习之旅:使用Python实现Linux中的ls命令

    一.写在前面 前几天在微信上看到这样一篇文章,链接为:https://mp.weixin.qq.com/s/rl6Sgv3uk_IpoFAx6cWa8w,在这篇文章中,有这样一段话,吸引了我的注意: ...

  4. Linux学习历程——Centos 7 ls命令

    一.命令介绍 ls命令用于显示目录中的信息. ----------------------------------------------------------------------------- ...

  5. linux命令详解之ls命令

    ls命令概述 ls命令用于显示文件目录列表,和Windows系统下DOS命令dir类似.当执行ls命令时,默认显示的只有非隐藏文件的文件名.以文件名进行排序及文件名代表的颜色显示.当不加参数时,默认列 ...

  6. Linux命令详解之–ls命令

    今天开始为大家介绍下Linux中常用的命令,首先给大家介绍下Linux中使用频率最高的命令--ls命令. 更多Linux命令详情请看:Linux命令速查手册 linux ls命令用于显示指定工作目录下 ...

  7. 关于 终端 ls 命令 不能区分文件和目录的问题

    默认的,使用ls命令来显示目录内容的时候,“终端”对于目录.可执行文件等特殊类型的文件并没有使用颜色来显示,只有使用“ls -G”时,才能显示颜色,这可真是不方便.有没有方法可以默认显示颜色呢?方法当 ...

  8. ls命令的20个实用范例

    contents ls -l -h -lhS -l --block-size=M -a -d */ -g -G -n --color=never -i -p -r -R -t ls ~ ls --ve ...

  9. 【转】ls 命令的 20 个实用范例

    Linux中一个基本命令是ls.没有这个命令,我们会在浏览目录条目时会遇到困难.这个命令必须被每个学习Linux的人知道. ls是什么 ls命令用于列出文件和目录.默认上,他会列出当前目录的内容.带上 ...

随机推荐

  1. python--类中的对象方法、类方法、静态方法的区别

    1.对象方法:顾名思义,是对类实例化后的对象有效的,由对象调用 2.类方法:第一个参数是cls(当前类),是对当前类做的额外的处理,类方法需要用类去调用,而不是实例对象调用 3.静态方法:没有参数!没 ...

  2. 2018牛客网暑假ACM多校训练赛(第五场)F take 树状数组,期望

    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round5-F.html 题目传送门 - https://www.no ...

  3. 043 关于 loadrunner 返回 http 500 错误解决思路

    这个支持项目中,总是出现500的问题,后来发现是redis总是崩掉. 后来,在压力测试,500的问题,又开始不断出现,虽然不是我自己处理的,但是没少吃苦头. 看了这篇文章,感觉说的还是很有道理,如何来 ...

  4. 048 SparkSQL自定义UDAF函数

    一:程序 1.需求 实现一个求平均值的UDAF. 这里保留Double格式化,在完成求平均值后与系统的AVG进行对比,观察正确性. 2.SparkSQLUDFDemo程序 package com.sc ...

  5. Http和RPC区别

    本文转自:https://blog.csdn.net/mou_it/article/details/79873612 RPC(即Remote Procedure Call,远程过程调用)和HTTP(H ...

  6. day 49-css补充(终结)[浮动和定位]

    老师的笔记: 前情回顾: day49 混乱即阶梯. 1. 前情回顾 HTML HTTP和HTML 文档机构 <!Doctype html> HTML head <meta> & ...

  7. 爬虫2 urllib用法

    from urllib import request,parse # 1. 解析数据 # 解析一条 # response = request.urlopen(url='http://httpbin.o ...

  8. Masquerade strikes back Gym - 101911D (数学)

    Quite often the jury of Saratov SU use the problem "Masquerade" in different practice sess ...

  9. 解决Windows 系统下Chrome中有多个音频界面时 无法静音单个Tab界面的问题

    Open the browser and type this address into the URL bar: chrome://flags In the Search flags box at t ...

  10. 【可靠性】Mysql 5.7 降低了半同步复制-数据丢失的风险

    如果你的生产线开启了半同步复制,那么对数据的一致性会要求较高,但在MySQL5.5/5.6里,会存在数据不一致的风险.有这么一个场景,客户端提交了一个事务,master把binlog发送给slave, ...