nettee 的 blog

Less is more than more.


  • 首页

  • 关于

  • 标签

  • 归档

Java 命令行交互输入库 JLine 入门

发表于 2018-09-03 |

我们都知道,软件的用户界面无非分为 GUI (图形用户界面)和 CLI (命令行用户界面)。对于我们经常使用 Linux 的人来说,命令行界面一定非常熟悉。无论是 Shell 里输入命令的界面,还是如 GDB 等软件的内部交互界面,都是命令行界面。而当我们开发自己的软件,要写认真写一个 CLI 的时候,却发现要手写做出一个好用的命令行界面其实非常困难。因为一个好的命令行界面,在输入/输出之外,还要支持一些常见的命令行功能。

对我而言,一个合格的命令行软件界面应该支持这三个功能:

  • 自动补全:当按下 TAB 键时,在当前光标处进行内容补全。根据上下文信息,补全可能是对命令的补全,也可能是对文件路径的补全。
  • 命令历史:当按上/下方向键时,可以显示上一条/下一条命令。
  • 行编辑 (line editing):可以使用 Emacs 快捷键进行行内的编辑功能,例如 Ctrl+A 移动光标至行首,Ctrl+E 移动光标至行尾。

熟悉 Linux 的人会发现,上面这三个功能都是 GNU Readline 的功能。我们不需要在软件中手写这几个功能,只要用这样一个库就可以了。实际上,GNU/Linux 中使用 GNU Readline 库的软件非常多,这使得 GNU Readline 同时也成为了一个事实上的命令行交互标准。GNU Readline 是 C 语言的库。我们用其他语言的时候,就要找对应功能的库(这往往是封装了底层的 GNU Readline 的库)。对 Java 语言来说,JLine 就是这样一个帮助你搭建一个命令行交互界面的库。

本文是想通过一个例子介绍 JLine3 的基本用法。JLine3 并没有一个 “Hello, world!” 的例子,它的 wiki 也写得非常简略。虽然有一个示例的程序 Example.java,但这个示例比较复杂,难以理解。希望本文的内容能对你理解 JLine3 的用法有所帮助。

基本框架

我们尝试为软件 Fog 设计一个命令行用户界面。用户可以输入四种命令:

1
2
3
4
CREATE [FILE_NAME]
OPEN [FILE_NAME] AS [FILE_VAR]
WRITE TIME|DATE|LOCATION TO [FILE_VAR]
CLOSE [FILE_VAR]
阅读全文 »

GDB 自动化调试

发表于 2018-06-05 |

我们通常都是在交互模式下使用 GDB 的,即手动输入各种 GDB 命令。其实 GDB 也支持执行预先写好的调试脚本,进行自动化的调试。调试脚本由一系列的 GDB 命令组成,GDB 会顺序执行调试脚本中的命令。

编写调试脚本时必须要处理好断点的问题。在交互模式下,程序执行至脚本时,GDB 会等待用户输入下一步的命令。如何在脚本中定义断点触发时进行的操作?这需要一种类似回调函数的机制。

GDB 中使用 Breakpoint Command Lists 的机制来实现这一点。用户可以定义,当程序停在某个 breakpoint (或 watchpoint, catchpoint) 时,执行由 command-list 定义的一系列命令。其语法为:

1
2
3
commands [list…]
… command-list …
end

例如,我想在每次进入 foo 函数且其参数 x > 0 时打印 x 的值:

1
2
3
4
5
6
break foo if x>0
commands
silent
printf "x is %d\n",x
continue
end

这里有几点要注意的:

  • Breakpoint command list 中的第一个命令通常是 silent。这会让断点触发是打印的消息尽量精简。如果 command … end 中没有 printf 之类的打印语句,断点触发时甚至不会产生任何输出。
  • Breakpoint command list 中的最后一个命令通常是 continue。这样程序不会在断点处停下,自动化调试脚本可以继续执行。

GDB 运行自动化调试脚本的方式为:

1
gdb [program] -batch -x [commands_file] > log

其中 -batch 参数将 GDB 运行为脚本模式(不进入交互环境),-x 参数 (也可以写为 -command) 指定调试脚本文件。

参考资料

  • GDB User Manual
  • GDB 自动化操作的技术

使用 Travis CI 实现 Github Pages + Hexo 博客的自动部署

发表于 2018-05-31 |

在使用 Hexo 写博客的时候,每次总是要使用 hexo deploy 将博客部署到 GitHub Pages,然后在把博客的源文件 push 到自己的 GitHub repo 里。我之前使用了 Git 分支的方法将博客源文件和博客部署文件放在一个 repo 里(这里)。但是有没有一种方法能让我每次只需要 push 一次呢?答案是有的,那就是使用 Travis CI 持续集成工具。

Travis CI 最普通的用法是用来做自动测试。每次你 push 到 GitHub repo 的时候,Travis 会自动执行单元测试,这样你就可以知道自己的每个 commit 是否可以通过 build。在发起 pull request 的时候,Travis 也会自动执行待合并分支的单元测试。不过 Travis 能做的事情不止这些,它同样可以实现自动部署,就比如我想要的 GitHub Pages 自动部署。

Travis CI 会执行用户定义的 .travis.yml 脚本。我想在这个脚本中实现:使用 Hexo 生成博客内容,再将博客内容部署到 nettee.github.io 项目的 master 分支 上。

生成博客内容

1
2
3
4
5
6
7
8
9
10
11
12
13
language: node_js

node_js: stable

install:
- npm install

before_script:
- git clone https://github.com/nettee/hexo-theme-next themes/next

script:
- hexo clean
- hexo generate
阅读全文 »

OkHttp 的 Interceptors 与责任链模式

发表于 2018-05-01 |

OkHttp是目前Android最流行的HTTP网络库。从Android 4.4开始,标准库HttpURLConnection的底层实现开始使用OkHttp。OkHttp + Retrofit目前是Android网络请求的主流选择。

OkHttp的源码有很多可以学习的地方,这篇文章中介绍了OkHttp代码架构的进化过程。OkHttp当前的代码架构已经相当清晰。其中作为发送网络请求的核心的interceptors,是设计模式中责任链模式(Chain-of-responsibility pattern)的一个典型的应用。

OkHttp 的基本用法

OkHttp使用Request和Response类对网络请求的输入输出进行建模,使用Call对网络请求的行为进行建模。

1
2
3
4
5
6
7
8
9
10
11
OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
.url(url)
.header("Accept", "text/html")
.build();

Call call = client.newCall(request);

Response response = call.execute();
int responseCode = response.code();

Interceptors

Interceptor是OkHttp提供的一个强大的机制。用户使用Interceptor可以对网络请求(call)进行监控、重写或重试。用户通过实现Interceptor接口来创建一个interceptor。例如,下面是一个对request/response记录log的interceptor:

阅读全文 »

Hexo之坑:如何让Hexo不渲染某些文件

发表于 2018-04-05 |

最近在复习和整理一些计算机学科的知识,知识的提纲整理在了iknowledge项目中。我使用的是gitbook工具,可以生成静态网页的书籍格式,于是我就想把这个也放在我的博客上。

Hexo博客的基本内容是一些Markdown文件,放在source/_post文件夹下,每个文件对应一篇文章。除此之外,放在source文件夹下的所有开头不是下划线的文件,在hexo generate的时候,都会被拷贝到public文件夹下。但是,Hexo默认会渲染所有的HTML和Markdown文件,导致gitbook的相关网页显示出错。

怎么样避开这个坑呢?如果只有一个HTML文件的话,可以简单地在文件开头加上layout: false一行即可:

1
2
3
4
5
layout: false
---

<html>
...

然而gitbook生成的静态网页有十几个HTML文件,显然是不可能使用这种方法的。这时候需要使用skip_render配置。根据Hexo文档中的说明,通过在_config.yml配置文件中使用skip_render参数,可以跳过指定文件的渲染。使用方式如下:

1
skip_render: [games/**, depview/**, knowledge/**]

这里的路径匹配可以使用glob 表达式。

在设置了跳过渲染之后,最好使用hexo clean清除以前的编译结果,保证配置生效。

可以在这里看到我的gitbook书籍的效果。

Depview: Java程序调用依赖关系的分析与显示

发表于 2018-01-26 |

DepView

DepView这个工具来自程序分析课上的一个小作业,而它的灵感来自我平时读别人代码的时候。例如我前一阵子就拿到了师兄的项目源码,一共100个Java文件,11k行代码。如何理解一个项目的代码呢?首先我们想到通过读文档、注释来理解,但师兄的项目显然是没有文档也没有注释的。或者我也可以直接向师兄请教,但师兄可能已经记不清他几个月前写代码时的思路了。于是,在很多时候,我们理解一个项目的方式只能是通过直接阅读代码,正是所谓的”Read the fucking source code”。那么,能否有一个工具来辅助我理解代码呢?

理解一个项目的关键点

哪些关键信息可以帮助我们很好地理解代码呢?

第一种关键信息是代码的基本使用模式,或者叫做用例。通过用例我们可以很快知道哪些模块是对外的接口,这些模块应该是我们首先关注的。如果一个项目有测试代码,而且写得比较好的话,那么通过看测试用例就可以很快知道代码的使用模式。

第二种关键信息是模块间的协作关系。很多时候程序的逻辑分散在不同的模块中,很难梳理。如果能够知道模块间的相互关系,就可以很方便地梳理程序脉络。这可以用工具来完成。我希望有一个工具能够显示模块间的依赖关系,包括:找到程序入口(模块入口),以及找出紧密协作的模块。Depview实现的是调用依赖关系的分析与显示。因为调用依赖关系容易定义,而且可以比较好地体现上下层模块间的关系。

工作步骤

Depview的工作步骤可以分为分析和显示两个部分。它首先解析源代码生成抽象语法树,在抽象语法树上提取依赖关系,最后绘制依赖图。

Figure: overview

阅读全文 »

分布式系统复习

发表于 2017-12-25 |

分布式系统模型

什么是分布式系统 [01-8]

A distributed system is a collection of autonomous(自治的) computing elements that appears to its users as a single coherent(一致的) system.

  • 每个计算单元(机器/进程)可以独立地工作(但他们通过通信相互协作)
  • 用户(人或应用)认为他面对的是一个单一系统(分布式透明性)

为什么要分布式? [01-11]

  • Economic: 微处理器比大型机性价比高
  • Speed: 分布式系统整个计算能力比单个大型主机要强 ==> Performance
  • Inherent(固有的) distribution: 有些应用涉及到空间上分散的机器
  • Reliability: 如果其中一台机器崩溃,整体系统仍然能够运转 ==> Availability
  • Incremental growth: 计算能力可以逐渐有所增加 ==> Scalability

分布式系统的目标 [01-12]

(构建分布式系统的时候应该努力达成的重要目标)

“ATOS”

  • Making resources available 可用性
    • 用户易于访问, 易于共享
  • Transparency 透明性: Hide the fact that resources are distributed
  • Openness 开放性
  • Scalability 可扩展性
    • size scalability: 可以容易地添加用户/资源,而没有显著的性能损失
    • geographical scalability: 用户/资源可能距离很远,但没有显著的通信延迟
    • administrative scalability: An administratively scalable system is one that can still be easily managed even if it spans many independent administrative organizations. 即使跨越许多独立的行政组织,仍然可以轻松管理
    • 大部分系统可以做到第一点,但后两点很难做到
阅读全文 »

Hexo 新主题 NexT

发表于 2017-12-01 |

这个博客从建立之处一直使用yilia主题。当时觉得它非常漂亮,但是不知道从哪天开始,yilia主题的代码高亮莫名其妙变成了黑色背景,而且总是出现滚动条。这种情况对于强迫症的我来说是不能忍的。这次我终于换上了新主题NexT。

NexT比当时yilia给我的感觉还要清爽简洁,同样支持标签页面的查看。NexT还多了一个功能是文章的目录结构。不过我发现我过去一些文章的目录结构显示混乱。排查之后,是因为使用Markdown标题不规范。如果一篇文章里只用了<h1>和<h3>,没用<h2>,就会导致NexT显示目录结构混乱,看来我以后写Markdown必须按规范的来了。

编译Android源码时遇到的问题与解决方案

发表于 2017-09-07 |

因为科研需要,我最近在编译Android源码,其间遇到的一些问题记录下来,以备日后查阅。

Android 6

Installing OpenJDK 7

AOSP only accepts OpenJDK 7 as compiling Java version. No HotSpot (Oracle JDK).

http://forum.ubuntu.org.cn/viewtopic.php?f=48&t=477645

Install via PPA.

1
2
3
sudo add-apt-repository ppa:openjdk-r/ppa  
sudo apt-get update
sudo apt-get install openjdk-7-jre
阅读全文 »

将Java Web项目从Myeclipse迁移到Maven

发表于 2016-11-06 |

一年前和checky同学合作写了一个Java Web项目IFTTT-Web。当时这个项目是作为课程的编程作业,为了尽快完成项目,我们选择在MyEclipse中进行开发。课程结束之后代码就一直搁置,有一天我想在Eclipse中重新配置项目(MyEclipse是收费软件,我的电脑里已经不再安装),但没有成功。Java Web项目比较复杂,除了Java代码之外,还包括JSP页面、JavaScript脚本、CSS文件等各种webapp需要的文件,想把一个已有的项目重新在Eclipse里运行起来,确实比较困难。

我当时尝试了一种更为“原始”的做法:在命令行下手工编译Java源文件,将编译后的字节码和webapp文件拷贝到Tomcat的目录下。这种方法当然成功了,但部署需要的手工操作太多,显然不是长久之计。直到不久之前,我才了解到Maven这个工具在Java世界里的重要地位,并开始在我的项目里使用Maven。这次我尝试用Maven重新构建一年前的项目,终于成功,在这里记录一下全部的过程。

主要步骤

  1. 生成Maven webapp项目骨架
  2. 更改目录结构
  3. 添加依赖
  4. 使用jetty
阅读全文 »
123
nettee

nettee

持续学习中...

30 日志
29 标签
RSS
GitHub E-Mail 掘金 知乎
© 2019 nettee
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.3