shell脚本分析
实例命令:hdfs dfs -ls
对应脚本文件:bin/hdfs
对应具体脚本内容:
COMMAND=$1
shift
elif [ "$COMMAND" = "dfs" ] ; then
CLASS=org.apache.hadoop.fs.FsShell
最终执行的命令:
exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS $CLASS "$@"
//其中,CLASS=org.apache.hadoop.fs.FsShell,$@=-ls
FsShell实现分析
当然是先看入口main
public static void main(String argv[]) throws Exception {
//new FsShell()
FsShell shell = newShellInstance();
//加载core-default.xml,core-site.xml
Configuration conf = new Configuration();
//不开启安静模式(安静模式下错误和提示信息不会被打印)
conf.setQuietMode(false);
shell.setConf(conf);
int res;
try {
//ToolRunner就是一个工具类,用于执行实现了接口`Tool`的类,FsShell是Tool的实现类
res = ToolRunner.run(shell, argv);
} finally {
shell.close();
}
System.exit(res);
}
ToolRunner类结合GenericOptionsParser类来解析命令行参数,
在运行上述ToolRunner.run(shell, argv)代码之后,经过一番解释之后,最后真正执行的仍然是类FsShell的run方法,而且对其参数进行了解析,run方法如下:
@Override
public int run(String argv[]) throws Exception {
// 初始化FsShll,包括注册命令类
init();
int exitCode = -1;
if (argv.length < 1) {
printUsage(System.err); //打印使用方法
} else {
String cmd = argv[0];//取到第一个参数,即 ls
Command instance = null;
try {
instance = commandFactory.getInstance(cmd); //实例化Command,即new Ls()
if (instance == null) {
throw new UnknownCommandException();
}
//调用LS的父类Command.run
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);
}
}
return exitCode;
}
(1)重点分析一:init()如下:
protected void init() throws IOException {
getConf().setQuietMode(true); //开启安静模式
if (commandFactory == null) {
//实例话CommandFactory
commandFactory = new CommandFactory(getConf());
//为commandFactory注册命令-对象:help,usage (实例化了commandClass,放入commandFactory的objectMap中)
commandFactory.addObject(new Help(), "-help");
commandFactory.addObject(new Usage(), "-usage");
// 注册命令-class,调用registerCommands方法(未实例化commandClass,延迟实例化,在真正使用的时候在通过反射进行实例化,放入commandFactory的classMap中)
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)) {
// 调用CommandFactory类的registerCommands方法
// 注意,这里传的参数是类FsCommand
factory.registerCommands(FsCommand.class);
}
}
CommandFactory类的registerCommands方法如下:
public void registerCommands(Class<?> registrarClass) {
try {
// 这里触发的是类FsCommand的registerCommands方法
registrarClass.getMethod(
"registerCommands", CommandFactory.class
).invoke(null, this);
} catch (Exception e) {
throw new RuntimeException(StringUtils.stringifyException(e));
}
}
接下来,看看类CommandFactory的registerCommands方法,代码如下:
public static void registerCommands(CommandFactory factory) {
factory.registerCommands(AclCommands.class);
factory.registerCommands(CopyCommands.class);
factory.registerCommands(Count.class);
factory.registerCommands(Delete.class);
factory.registerCommands(Display.class);
factory.registerCommands(Find.class);
factory.registerCommands(FsShellPermissions.class);
factory.registerCommands(FsUsage.class);
factory.registerCommands(Ls.class);
factory.registerCommands(Mkdir.class);
factory.registerCommands(MoveCommands.class);
factory.registerCommands(SetReplication.class);
factory.registerCommands(Stat.class);
factory.registerCommands(Tail.class);
factory.registerCommands(Test.class);
factory.registerCommands(Touch.class);
factory.registerCommands(Truncate.class);
factory.registerCommands(SnapshotCommands.class);
factory.registerCommands(XAttrCommands.class);
}
我们再来看看Ls类
class Ls extends FsCommand {
public static void registerCommands(CommandFactory factory) {
factory.addClass(Ls.class, "-ls");
factory.addClass(Lsr.class, "-lsr");
}
(2)重点分析二:instance.run
调用父类Command的run方法:
* 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)}
public int run(String...argv) {
//将String..