Ubuntu上设计Hexo主题

0 Hexo Theme

用多了hexo的默认主题,打算自己写一个Hexo主题,感觉很方便。
主要在theme文件夹中。
hexo文档格式:

1
2
3
4
5
6
7
8
├── _config.yml       // 总体的配置文件
├── node_modules // NodeJs 所依赖的包,后期也可以自己添加插件
├── package-lock.json // 支持 hexo 运行的 NodeJs 包
├── package.json // 自定义的 NodeJs 包
├── scaffolds // Hexo Markdown 加载时的关键字,如data,title等,它会在启动的时候默认加载
├── source // md 源文件目录
└── themes // 主题文件夹
└── landscape // 默认主题

我们编写 md 文档放在 『source』文件夹中,在运行『hexo generate』的时候,会根据『source』目录中的 md 文件自动生成一组 『html』格式的静态文件组,会在wiki目录下新建一个 『public』目录,存放在其中。

在『_config.yml』中,可以看到配置的默认主题『theme: landscape』,所对应的是 themes 目录下的 landscape 目录。

将theme:后面的内容修改为自己写的主题:lym.

在『themes』目录下,新建一个自己的主题『lym』,并且新建一些可以支持运行的文件与目录,按照如下目录格式创建,里面可以不写东西,下面写有注释的,就是我新建的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├── _config.yml                 
├── node_modules
├── package-lock.json
├── package.json
├── scaffolds
├── source
└── themes
├── landscape
└── lym // 自建的主题目录
├── _config.yml // 主题配置文件
├── layout // 主要构造 html 的模板
│ ├── index.ejs // 主页模板
│ ├── layout.ejs // 布局模板
│ └── post.ejs // md 编译成 html 后的文件模板
└── source // 静态资源文件目录
├── css // css 样式目录
└── js // JavaScript 脚本目录

1.EJS标签

EJS(Effective JavaScript)是一套简单的模板语言,帮你利用普通的 JavaScript 代码生成 HTML 页面。

1
2
3
4
5
6
7
8
9
10
<% '脚本' 标签,用于流程控制,无输出。
<%_ 删除其前面的空格符
<%= 输出数据到模板(输出是转义 HTML 标签)
<%- 输出非转义的数据到模板
<%# 注释标签,不执行、不输出内容
<%% 输出字符串 '<%'
%> 一般结束标签
-%> 删除紧随其后的换行符
_%> 将结束标签后面的空格符删除
<%- include("index.ejs") %> 引入其他模板
1
2
3
4
<% 
var test = "基本上,就用这两组标签,其他的也用不上。";
%>
<%- test %>

2.Hexo变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
site								总体变量,几乎都是从这里开始的
site.posts 所有文章
site.posts[0].path 文章路径,带日期的
site.posts[0].slug 文章路径,根据项目文件夹的路径来的
site.posts[0]._id 文章的唯一 id,后面会用于 active 对比
site.posts[0].title 文章的标题
site.posts[0].date 文章的时间
page.date 在直接访问文章路径下,文章的时间
page.title 在直接访问文章路径下,文章的标题
page._id 在直接访问文章路径下,文章的的唯一 id,后面会用于 active 对比
page.content 引入对应文章的正文
config.xxx 总体配置文件的引用 _config.yml
theme.xxx 主题配置文件 theme._config.yml
<%- body %> 同时引入 post.ejs 和 index.ejs
<%- css(path,...)%> 引入 css 文件
<%- js(path,...) %> 引入 js 文件

Ubuntu上Git使用教程

1 获取Git仓库

1.1 从工作目录中初始化新仓库

1.1.1 初始化

1
git init

初始化后会出现一个.git目录,所有git所需要的数据和资源都在这个目录中。

1.1.2 进行版本控制

1
2
3
git add *.c
git add README
git commit -m 'initial project version'

add命令开始跟踪,
commit命令开始提交

1.2 从现有的仓库中克隆

将项目的git仓库复制出来

1
git clone git://xxx [file-name]

2 文件管理

2.1 文件状态:

未跟踪:
没有上次更新时的快照,也不早当前的暂存区域。
当文件未被跟踪,git不会自动将其纳入跟踪范围,所以不用担心将临时文件归入版本管理。

已跟踪:
代表文件本来就被纳入了版本控制管理的文件,在上次快照中有记录。
已跟踪文件的状态有:未修改,已修改和已放入暂存区。

状态转化:
刚克隆某个仓库,所有文件都是已跟踪文件,状态为未修改。
编辑文件后,文件状态变为已修改。
将已修改的文件放到暂存区。
最后一次性提交暂存区的文件。

状态转化图:

1
2
3
4
5
6
untracked(未跟踪) |						tracked(已跟踪)
untracked | unmodified(未修改) | modified(已修改) | staged(暂存区)
--------------------add file-------------------->
--add file->
--edit file-> --stage file->
<-remove file-- <-----------commit------------

检查当前文件状态:

1
git status

2.1.1跟踪新的文件:

1
git add README

当add未跟踪的文件,文件会变为已经被跟踪,而且进入了暂存状态。

2.1.2暂存已修改的文件

1
git add xxx

当文件已经跟踪,但是没有暂存,会将文件进入暂存状态。

2.1.3 忽略某些文件

一些文件不需要纳入文件管理,也不希望总是出现在未跟踪文件列表中。可以创建一个.gitignore文件,列出需要忽略的文件模式。

1
2
3
4
5
# commitments
*.a
!lib.a#除了xxx
/TODO #忽略TODO文件
build/ #忽略build目录下的所有文件

格式规范:
1。空行或者#都会被忽略
2。glob模式匹配
3。匹配模式最后/说明忽略目录
4。忽略指定模式以外的文件或者目录,可以加!

glob模式:shell使用的简化的正则表达式

    • 匹配零个或多个任意字符
  1. [abc] 匹配任何一个列在方括号中的字符
    如果方括号中使用短划线分隔两个字符,例如[0-9]
  2. (?) 只匹配一个任意字符

2.1.4 查看已暂存和未暂存的更新

1
2
git status
git diff

2.1.5 提交更新

在提交之前,查看文件的状态,是否已经暂存,然后提交。

1
2

git commit -m "xxx"

2.1.6 跳过暂存区域提交

通过暂存区域可以准备提交的细节,可以跳过暂存区域。

1
git commit -a

git自动把所有已经跟踪过的文件暂存起来一起提交,跳过了add步骤。

2.1.7 移除文件

从git中移除某个文件,需要将其从已跟踪清单中移除,也就是从暂存区移除。

1
git rm

从跟踪区域中移除,顺便会在工作目录中删除指定的文件。
如果删除之前已经修改过而且已经放到暂存区,必须使用-f。

如果,希望从git仓库即暂存区中移除,但是仍然希望保留在当前的工作目录中,

1
git rm --cached xxx

2.1.8 移动文件

1
git mv file_from file_to

2.2 查看提交历史

1
2
3
4
5
6
git log
-p -2
//展开显示每次提交的内容差异。-2表示仅显示最近的两次更新

-stat
//显示简要的更改行数统计

2.3 撤销操作

2.3.1 修改最后一次提交

1
2
3
4
5
git commit --amend 
//撤销刚才的提交操作
//使用当前的暂存区提交快照
//如果刚才的提交完没有任何改动,直接运行此命令,可以重新编辑提交说明。
//如果忘记暂存修改,补上暂存操作,然后再修改提交

2.3.2 取消已经暂存的文件

1
git reset HEAD <file>

将修改过后提交到暂存区的文件,撤销暂存,返回已修改但未暂存的状态

2.3.3 取消对文件的修改

1
git checkout -- <file-name>

取消修改,回到修改之前的版本

任何已经提交到git的都可以被恢复,

3 远程仓库

1
2
git remote [-v]
//-v verbose显示对应的克隆地址

列出每个远程库的简短名字。克隆完某个项目后,至少可以看到一个名为origin的远程库,git默认使用这个明治来标识你所克隆的原始仓库。

3.1 添加远程仓库

1
2
git remote add [short-name] [url]
//添加一个新的远程仓库,可以指定一个简单的名字,以便将来引用

3.2 从远程库抓取数据

1
git fetch [short-name]

fetch只是将远端的数据拉取到本地仓库。并不会自动合并到当前的工作分支。

还可以用git pull自动抓取数据,
然后将远端分支自动合并到本地仓库的当前分支。

3.3 推送数据到远程仓库

1
git push [remote-name] [branch-name]

只有在所克隆的服务器上有写权限,或者同一时刻没有其他人推数据,这条命令才可以完成。
如果在推送数据之前,别人推送了,必须先将更新抓取到本地,合并到自己的项目中,才能够再次推送。

3.4 查看远程仓库的信息

1
git remote show [remote-name]

4 打标签

1
2
3
4
5
6
git tag
//列出已有标签
git tag -l 'v1.4.2.*'
//特定搜索模式搜索标签
git tag -a v1.4 -m "my sersion 1.4"
//添加标签annotated

如果拥有私钥可以通过GPG来签署标签
这个有什么用处呢?

1
git tag -s v1.5 -m "my signed 1.5 tag"

通过git show可以显示GPG签名

后期加标签

1
2
3
4
5
git log --pretty=online
//显示之前的操作

git tag -a v1.2 9fceb02
//在打标签的时候跟上校验

分享标签,默认不会将标签传送到远端服务器,必须通过显式命令。

1
2
3
4
git push origin [tagname]

git push origin --tags
//将所有本地标签添加上去

git 快捷操作命名别名:

1
2
git config --global alias.ci commit
//输入git commit只需要git ci,用于提高效率

5 分支

任何版本控制系统都支持分支。
意味着从开发的主线上分离开,在不影响主线的同时继续工作。
在其他版本控制中,需要创建一个源代码目录的完整副本。

git可以快速切换分支与合并。

修补仓库中的程序的步骤:
1.返回到原先的分支
2.新建一个新的分支,修复问题
3.回到原先的分支进行合并,然后推送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

git checkout -b new-branch
//新建并切换到new-branch分支
//以上的命令等效于
git branch new-branch
git checkout new-branch

//修改文件之后,提交文件到暂存区
giit commit -a -m'commit new-brach'

//需要修补主分支的问题
//切换到主分支
git checkout master

//创建新的修补分支
git checkout -b fix-branch

//修改文件,之后提交
git commit -a -m 'fix branch'

//修补完成后,回到主分支并且合并,然后发布
git checkout master
git merge fix-branch

//fix-branch合并后可以删掉
git branch -d fix-branch

//返回到new-branch分支,继续进行原有工作,然后提交
git checkout new-branch
//修改文件
git commit -a -m 'finish the new-branch'

//将新分支和修改分支合并
git checkout master
git merge new-branch

//删除new-branch分支
git branch -d new-branch

在实际情况下,会遇到冲突的分支合并
如果在不同的分支中都修改了同一个文件的同一个部分,git就无法将两者合并到一起。这时候就会产生冲突。
通过git status显示。可以选择不同分支的一个或者亲自整合到一起。
整合完成之后,运行git add可以标记为解决状态。
运行git mergetool可以调用一个可视化的合并工具解决冲突。
git commit提交文件。

分支的管理:

1
2
3
4
5
6
7
8
git branch
//显示所有分支
git branch --merged
//显示未合并分支
git branch -d [branch-name]
//删除已合并分支
git branch -D [branch-name]
//删除未合并分支

远程分支:
远程分支是对远程仓库的分支的索引。是无法移动的本地分支,只有在git进行网络交互的时候才会更新。
远程分支的表示方式:
远程仓库名/分支名
例如:origin/master
origin仓库的master分支

//同步远程服务器上的数据到本地
git fetch origin

submodule:
在某个仓库中有引用submodule时,clone不会下载submodule,我们需要进入工程然后

1
2
3
git submodule init
git submodule update
//这样就可以下载submodule了

C语言解决数独

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
input:
0 2 0 0 9 0 8 0 6
0 0 1 0 0 0 2 0 0
5 0 0 0 1 0 0 3 0
0 0 3 0 0 1 0 2 0
2 5 0 0 6 0 9 0 0
0 0 7 0 0 8 0 5 0
6 0 0 0 5 0 0 9 0
0 0 5 0 0 0 3 0 0
0 3 0 0 7 0 5 0 4

ans:
3 2 4 7 9 5 8 1 6
8 9 1 6 3 4 2 7 5
5 7 6 8 1 2 4 3 9
9 6 3 5 4 1 7 2 8
2 5 8 3 6 7 9 4 1
4 1 7 9 2 8 6 5 3
6 8 2 4 5 3 1 9 7
7 4 5 1 8 9 3 6 2
1 3 9 2 7 6 5 8 4
repeat times: 6
Hello world!

源程序:https://github.com/Emir-Liu/MyProjectSudoku

Ubuntu上的C语言socket客户端教程

0.简介

socket时网络中2个主机之间进行任何类型的网络通信的虚拟口,任何网络通信都是通过套接字来进行。
按照功能分为:
socket客户端:连接到远程系统来获取数据的应用程序
socket服务器:使用socket接受传入连接,并提供数据的应用程序。

1 建立socket客户端

其中包含以下的内容:
1.创建一个socket
2.连接到远程服务器
3.发送数据
4.接受数据
5.关闭socket
在本部分中,我们编写socket客户端,www.baidu.com作为socket服务器。
更具体的说:
www.baidu.com是http服务器,网络浏览器是http客户端。

1.1 创建一个socket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#include <stdio.h>
#include <sys/socket.h>

int main()
{
int socket_desc;
socket_desc = socket(AF_INET,SOCK_STREAM,0);

if(socket_desc == -1)
{
printf("could not create socket");
}
return 0;
}

socket函数创建了一个socket,并且返回了一个socket描述符,可以在其他的函数中别使用,socket函数具体有以下的属性:

1
2
3
4
5
int socket(int domain,int type,int protocol)
int domain: AF_UNIX(本机通信),AF_INET(TCP/IP-IPv4),AF_INET6(TCP/IP-IPv6)
int type: SOCK_STREAM(TCP),SOCK_DGRAM(UDP),SOCK_RAW
int protocol =0确定套接字需要的协议簇和类型
返回值:成功返回套接字,失败返回-1,错误代码写入“errno”

1.2将socket连接到服务器

通过socket连接到远程服务器,需要ip地址和端口号进行连接

1.2.1 创建sockaddr_in结构

1
struct sockaddr_in server;

sockaddr_in 结构如下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// IPv4 AF_INET sockets:
struct sockaddr_in {
short sin_family; // e.g. AF_INET, AF_INET6
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, below
char sin_zero[8]; // zero this if you want to
};

struct in_addr {
unsigned long s_addr; // load with inet_pton()
};

struct sockaddr {
unsigned short sa_family; //地址族,AF_xxx
char sa_data [14]; // 14个字节的协议地址
};

函数inet_addr是将ip地址转换为长格式的函数:

1
server.sin_addr.s_addr = inet_addr("183.232.231.172");

这里时baidu的ip地址。下面有如何找出给定域名的ip地址。

1.2.2 连接

“连接”的概念适用于SOCK_STREAM / TCP类型的套接字。连接是指可靠的数据“流”,以便可以有多个这样的流,每个流具有自己的通信。可以将其视为不受其他数据干扰的管道。

其他套接字,例如UDP,ICMP,ARP都没有“连接”的概念。这些是基于非连接的通信。这意味着您一直在发送或接收来自任何人和每个人的数据包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<stdio.h>
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr

int main(int argc , char *argv[])
{
int socket_desc;
struct sockaddr_in server;

//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}

server.sin_addr.s_addr = inet_addr("183.232.231.172");
server.sin_family = AF_INET;
server.sin_port = htons( 80 );

//Connect to remote server
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0)
{
puts("connect error");
return 1;
}

puts("Connected");
return 0;
}

1.3 通过socket发送数据

通过send函数发送数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<stdio.h>
#include<string.h> //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr

int main(int argc , char *argv[])
{
int socket_desc;
struct sockaddr_in server;
char *message;

//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}

server.sin_addr.s_addr = inet_addr("183.232.231.172");
server.sin_family = AF_INET;
server.sin_port = htons( 80 );

//Connect to remote server
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0)
{
puts("connect error");
return 1;
}

puts("Connected\n");

//Send some data
message = "GET / HTTP/1.1\r\n\r\n";
if( send(socket_desc , message , strlen(message) , 0) < 0)
{
puts("Send failed");
return 1;
}
puts("Data Send\n");

return 0;
}

备注:也可以通过write来将数据发送到socket

1.4 通过socket接受数据

函数recv用于在socket上接受数据,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<stdio.h>
#include<string.h> //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr

int main(int argc , char *argv[])
{
int socket_desc;
struct sockaddr_in server;
char *message , server_reply[2000];

//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}

server.sin_addr.s_addr = inet_addr("183.232.231.172");
server.sin_family = AF_INET;
server.sin_port = htons( 80 );

//Connect to remote server
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0)
{
puts("connect error");
return 1;
}

puts("Connected\n");

//Send some data
message = "GET / HTTP/1.1\r\n\r\n";
if( send(socket_desc , message , strlen(message) , 0) < 0)
{
puts("Send failed");
return 1;
}
puts("Data Send\n");

//Receive a reply from the server
if( recv(socket_desc, server_reply , 2000 , 0) < 0)
{
puts("recv failed");
}
puts("Reply received\n");
puts(server_reply);

return 0;
}

baidu返回了html。

备注:我们也可以通过read读取socket的数据。

1.5 关闭socket

用close函数,需要unistd.h头文件

1
close(socket_desc);

以上,socket客户端已经基本完成。

1.6 补充:获取主机的IP地址

连接到到远程主机,需要IP地址。用gethostbyname函数,域名作为参数,返回hostent类型的结构。

1
2
3
4
5
6
7
8
9
/* 单个主机的数据库描述*/
struct hostent
{
char *h_name; /* 主机的正式名称*/
char **h_aliases; /* 别名列表*/
int h_addrtype; /* 主机地址类型*/
int h_length; /* 地址的长度*/
char **h_addr_list; /* 服务器的地址列表*/
};

在 h_addr_list中有IP地址。通过以下的代码来使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h> //printf
#include <string.h> //strcpy
#include <sys/socket.h>
#include <netdb.h> //hostent
#include <arpa/inet.h>

int main(int argc , char *argv[])
{
char *hostname = "www.baidu.com";
char ip[100];
struct hostent *he;
struct in_addr **addr_list;
int i;

if ( (he = gethostbyname( hostname ) ) == NULL)
{
//gethostbyname failed
herror("gethostbyname");
return 1;
}

//Cast the h_addr_list to in_addr , since h_addr_list also has the ip address in long format only
addr_list = (struct in_addr **) he->h_addr_list;

for(i = 0; addr_list[i] != NULL; i++)
{
//Return the first one;
strcpy(ip , inet_ntoa(*addr_list[i]) );
}

printf("%s resolved to : %s\n", hostname , ip);
return 0;
}

Ubuntu上gtk+使用

0简介

GTK(GIMP Toolkit)是一套跨多种平台的图形工具包,按LGPL许可协议发布的。虽然最初是为GIMP写的,但早已发展为一个功能强大、设计灵活的通用图形库。特别是被GNOME选中使得GTK+广为流传,成为Linux下开发图形界面的应用程序的主流开发工具之一,当然GTK+并不要求必须在Linux上,事实上,目前GTK+已经有了成功的windows版本。 [1]
GTK+虽然是用C语言写的,但是您可以使用你熟悉的语言来使用GTK+,因为GTK+已经被绑定到几乎所有流行的语言上,如:C++, Guile, Perl, Python, TOM, Ada95, Objective C, Free Pascal, Eiffel等。

1.gtk+的安装

1
sudo apt-get install libgtk2.0-dev

C语言编写解释器

相关资料:
基于栈的指令集和基于寄存器的指令集的对比:
https://www.cnblogs.com/wade-luffy/p/6058089.html

用C写了一个解释器,发现Java等高级语言通过虚拟机解释运行程序的过程和汇编语言编译为机器语言有异曲同工之妙。

将代码解释为的字节码,和汇编语言很类似。

C语言编译原理

gcc

1
gcc [options] [filenames]

其中options就是编译器所需要的参数,filenames给出相关的文件名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-E
//只激活预处理,生成.i文件
//预处理:将头文件插入代码中,生成.i文件
-S
//只激活预处理和编译,生成汇编文件.s
//编译:将预处理后的文件转换为为汇编语言,检查代码规范性,是否有语法错误等。检查完毕后,将代码翻译为汇编语言,生成.s文件。
//gcc -S -O3 -fno-asynchronous-unwind-tables main.c这一句里面的参数不明白
-c
//只激活预处理,编译,和汇编,生成.o文件
//汇编:将汇编语言转换为为机器语言,并将指令打包为可重定位目标程序的二进制格式,生成.o文件
无参数
//激活预处理,编译,汇编和连接,生成执行文件
//链接:将调用的函数的目标文件与主程序整合到一起,将文件合并后生成一个可执行的目标文件

gcc -E main.c -o main.i
gcc -S main.i -o main.s
gcc -c main.s -o hello.o
gcc main.o -o main.out

书单

作者:
Neil Gaiman 尼尔 盖曼
Fairy tales are more than true: not because they tell us that dragons exist, but because they tell us that dragons can be beaten.

作品:
1.黑客:计算机革命的英雄

Ubuntu下建立shell程序

最近写了一个shell,了解了系统内核的基本内容。以后补上说明。

shell内置命令与外部命令的区别

内部命令实际上是shell程序的一部分,其中包含的是一些比较简单的linux系统命令,这些命令由shell程序识别并在shell程序内部完成运行,通常在linux系统加载运行时shell就被加载并驻留在系统内存中。内部命令是写在bashy源码里面的,其执行速度比外部命令快,因为解析内部命令shell不需要创建子进程。比如:exit,history,cd,echo等。

外部命令是linux系统中的实用程序部分,因为实用程序的功能通常都比较强大,所以其包含的程序量也会很大,在系统加载时并不随系统一起被加载到内存中,而是在需要时才将其调用内存。通常外部命令的实体并不包含在shell中,但是其命令执行过程是由shell程序控制的。shell程序管理外部命令执行的路径查找、加载存放,并控制命令的执行。外部命令是在bash之外额外安装的,通常放在/bin,/usr/bin,/sbin,/usr/sbin……等等。可通过“echo $PATH”命令查看外部命令的存储路径,比如:ls、vi等。

用type命令可以分辨内部命令与外部命令: