LittleQ

爱好:写代码

常用的操作:选择,插入,删除,前面已经讲了选择元素的相关操作,这里还是简单记一下。

元素操作

选择元素

假设有三个元素

1
2
3
<p>test1</p>
<p>test2</p>
<p>test3</p>

下面的操作都是基于这三个的元素的操作

选择第一个p元素

1
2
var p1 = body.sellect("p");
p1.style("color", "red");

全选

1
2
var p = body.sellectAll("p");
p.style("color", "red");

选择指定元素

  • 通过元素id属性
1
<p> id="id_2">Test 2</p>

然后使用下面的代码来改变第二个元素的颜色

1
2
var p2 = body.select("#id_2");
p2.style("color","red");
  • 通过元素class属性

假设有如下内容,把这个内容改一下:

1
2
3
<p class="c1" id="id_1">Apple</p>
<p class="c1" id="id_2">Pear</p>
<p class="c2" id="id_3">Banana</p>

可以使用下面的方法:

1
2
var p = body.selectAll(".c1");
p.style("color", "blue");

备注:关于select()selectAll()的参数,其实是符合CSS选择器的条件的,即用#表示id,用.表示class.这样可以把前两个内容设置成蓝色。

插入

和Python很像,有insert()以及append()方法:

1
2
append():	# 在选择集末尾插入元素
insert(): # 在选择集前面插入元素

例子:

1
2
body.append("p").text("append p element");	# 在最后插入一个<p>元素,并且设置文本内容
body.insert("p", "#id_2").text("insert p element"); # 在id为id_2的元素前面插入一个元素

结果如下:

1
2
3
4
5
Apple
insert p element
Pear
Banana
append p element

删除

这个简单,直接使用remove()即可,对于上面的代码:

1
2
var p = body.select("#id_1");
p.remove(); # 删除id=id_1的元素

简单图表

简单介绍一下图表,但是的先明白几个常见的术语:
要画图,必须要在一个用来画图的东西,通常我们用画布来画图,常见的画布又两种:SVGCanvas

  • SVG

SVG是指可缩放的矢量图形,主要用来描述二维矢量图形,定义图形也很简单,使用xml来定义图形。由于是用xml来定义图形,所以每个元素都可以添加JavaScript事件处理器。

  • Canvas

Canvas是通过JavaScript来绘制2D图形

**注意:**这里需要说明一下,D3提供的很多画图的函数都是SVG图形生成器,所以建议在学习D3.js的时候使用SVG画布,添加一个SVG也很简单:

1
2
3
4
5
6
7
var width = 300;  //画布的宽度
var height = 300; //画布的高度

var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度

下面就可以在这个svg上画各种图了,先上源码:

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
<html>
<head>
<meta charset="utf-8">
<title>HelloWorld</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 300;
var height = 300;

var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);

var dataSet = [250, 210, 170, 130, 90];
var rectHeight = 25;

svg.selectAll("rect")
.data(dataSet)
.enter()
.append("rect")
.attr("x", 20) // 设置举行左上角开始x位置
.attr("y", function(d, i) { // 设置举行左上角开始y位置
return i * rectHeight;
})
.attr("width", function(d) { // 设置每个举行的宽度(长度)
return d;
})
.attr("height", rectHeight - 2) // 行之间空出一条间隙
.attr("fill", "steelblue");
</script>
</body>
</html>

解释一下,<rect>为矩形,它有四个属性:

1
2
3
4
x:矩形左上角的 x 坐标
y:矩形左上角的 y 坐标
width:矩形的宽度
height:矩形的高度

其中x轴正方向为水平向右,y轴正方向为垂直向下:

1
var dataSet = [250, 210, 170, 130, 90];

用来存储几个长方形的长度,即width属性值,添加和dataSet数量相等的矩形,所使用的代码是:

1
2
3
4
svg.selectAll("rect")
.data(dataSet)
.enter()
.append("rect")

这段代码先记住,可以添加足够多的元素,后面细讲机制,然后就是前面讲过的给元素附属性值了,依次给每个举行设置那四个属性的值,dataSet里面的元素设置成举行的width属性值,最后设置一下颜色,效果如下:
svg图表

比例尺

上面的设置矩形的长度用的是一个数组,里面的值直接设置成了矩形的像素长度,但是这样比较死,有可能太大也有可能太小,所以使用比例尺是个不错的注意,只要设置一种放缩关系,只用设定上下限,中间的值会自动放缩会很方便。
简单来说就是一个函数x->y这样的一种映射关系,x为定义域,y为值域。

线性比例尺

这种比例尺比较常用,也比较简单,使用上只用设定上下限即可:

1
2
3
4
5
6
7
8
9
10
11
var dataSet = [1.2, 2.3, 0.9, 1.5, 3.3];
var min = d3.min(dataSet);
var max = d3.max(dataSet);

var linear = d3.scale.linear()
.domain([min, max])
.range([0, 300])

linear(0.9)
linear(2.3)
linear(3.3)

d3.scale.linear()返回线性比例尺,也可以当函数使用。

离散比例尺

有时候需要的并不一定是线性连续的映射关系,可能是把某一批转化成另一类元素,例如:

1
2
var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];

需要把对应的序号映射到颜色上去,用序数比例尺就更合适:

1
2
3
4
5
6
7
var ordinal = d3.scale.ordinal()
.domain(index)
.range(color);

ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black

所以上面讲的柱形图,我们可以利用比例尺改写一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var rectHeight = 25;   //每个矩形所占的像素高度(包括空白)

svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x",20)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return linear(d); //在这里用比例尺
})
.attr("height",rectHeight-2)
.attr("fill","steelblue");

对于做数据的来说,调度器这个东西应该不是陌生概念,数据流就是靠着调度器调度的,然后进行汇总,整合。但是随着调度器上的任务越来越多,任务之间的依赖变得很复杂,而且有时候有一些任务需要用到重要的模型,报表或者邮件日报中,这就要求用到的这些数据任务流需要进行一些优化,但是没有一个任务依赖图做全局参考,优化有时候不是那么好做,所以近期打算使用D3.js这个库给现有的调度器做一个任务依赖图,可以更好的把控数流。

准备工作

首先需要下载d3.js官网,下载d3.zip文件,解压之后把里面的d3.min.js拷贝到项目里面,我的项目目录路径为

1
2
3
4
5
6
➜  script git:(web-d3.js) ✗ tree
.
├── hello_world.html
├── js
│   └── d3.min.js
└── README.md

d3.min.js文件放在项目目录下的的js文件夹,先来一个Hello World,新建一个hello_world.html`文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<head>
<meta charset="utf-8">
<title>HelloWorld</title>
</head>
<body>
<p>Hello World 1</p>
<p>Hello World 2</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
d3.select("body").selectAll("p").text("www.ourd3js.com");
</script>
</body>
</html>

打开浏览器就可以看到效果,所有的<p>标签里面的内容都被替换了。如果换成普通的js来实现也可以,不过得用一个循环,D3所做的事就是减轻你的工作量,以及使你的代码十分简单易懂。下面还有个简单的样例,也是类似余替代循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>
<head>
<meta charset="utf-8">
<title>HelloWorld</title>
</head>
<body>
<p>Hello World 1</p>
<p>Hello World 2</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
//选择<body>中所有的<p>,其文本内容为 www.ourd3js.com,选择集保存在变量 p 中
var p = d3.select("body")
.selectAll("p")
.text("www.ourd3js.com");

//修改段落的颜色和字体大小
p.style("color", "red")
.style("font-size", "72px");

</script>
</body>
</html>

上面的代码是先将选中的元素赋值给变量p,然后通过变量p来改变样式,这样可以使代码更整洁。

这里就展示除了D3.js的一个很重要的概念:选择集,像de.select()d3.sellectAll()选择元素返回的对象都是选择集.
除了选择集,d3.js还可以链式不停的调用函数d3.select().selectAll().text()链式语法。

选择 绑定数据

既然是操作数据,当然少不了选择需要操作的元素以及绑定对应的数据到对应的元素上。

选择元素

1
2
d3.select():	是选择所有指定元素的第一个
d3.selectAll(): 是选择指定元素的全部

上面的函数返回的结果都是返回集,常见用法有:

1
2
3
4
5
var body = d3.select("body"); //选择文档中的body元素
var p1 = body.select("p"); //选择body中的第一个p元素
var p = body.selectAll("p"); //选择body中的所有p元素
var svg = body.select("svg"); //选择body中的svg元素
var rects = svg.selectAll("rect"); //选择svg中所有的svg元素

绑定元素

选择元素最终也是为了操作元素,尤其是元素的值,即数据,绑定数据主要用下面两个方法:

1
2
datum():	绑定一个数据到选择集上
data(): 绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定

以具体的例子来看,data()datum()如何绑定数据:
首先在html代码里面写上几个供测试:

1
2
3
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
  • datum()

假设有我们有一个字符串,把这个字符串的内容绑定到这三个段落标签:

1
2
3
4
5
6
7
8
9
10
11
12
<script>
var str = "China";

var body = d3.select("body");
var p = body.selectAll("p");

p.datum(str);

p.text(function (d, i) {
return "第 " + i + " 个元素绑定的数据是 " + d;
});
</script>

运行这段代码的结果如下:

1
2
3
4
5
第 0 个元素绑定的数据是 China

第 1 个元素绑定的数据是 China

第 2 个元素绑定的数据是 China

**备注:**匿名函数function(d,i)的应用场景一般是选择集需要使用绑定的数据,两个参数的意思分别是:

  • d:代表数据,也就是绑定的数据

  • i:代表索引,代表数据的索引号,从0开始

  • data()

现在假设我们要把一个数组的元素依次赋值给前面的三个<p>,调用data()绑定数据,并替换成相应的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var dataset = ["I like dogs", "I like cats", "I like snakes"];

var body = d3.select("body");
var p = body.selectAll("p");

p.data(dataset)
.text(function (d, i) {
return d
});
</script>

运行结果为:

1
2
3
4
5
I like dogs

I like cats

I like snakes

这里的function(d, i)和上面的一样,d代表绑定的数据,i代表选择集元素序号,也是对选择集进行依次设置值

总结一下在工作中常用到的关于Python正则的一些用法,主要无非就是匹配提取置顶信息,或者替换指定信息,不过都是re模块的用法

查找匹配

查找和匹配主要就是re.search()以及re.match()

  • match:从字符串的开始处匹配,匹配成功会返回match object,如果匹配不上返回None
  • search:只要又字串符合就匹配成功,返回match object,如果没有一个子串满足匹配则返回None
  • findall: 如果能匹配,返回所有的匹配结果list

下面会以代码介绍一下如何使用以及提取出匹配到的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re

__author__ = 'anonymous'

if __name__ == '__main__':
str1 = 'hello world, test test Test'
if not re.match('world', str1):
print '1:not match'
print '2:', re.match('hello', str1).group()
print '3:', re.match('.*test', str1).group()
print '4:', re.match('.*test', str1, re.I).group()
print '5:', re.search('test', str1, re.I).group()
pass

输出结果如下:

1
2
3
4
5
1:not match
2: hello
3: hello world, test test
4: hello world, test test Test
5: test
  • 1, 2 说明match()只会从开头开始匹配,如果不是在开头则无法匹配
  • 2, 3 说明如果要匹配的内容不是在字符串的开头,那么需要加.*或者类似的通配符
  • 3, 4 说明re.I表示的是忽略大小写,
  • 4, 5 可以看到search()函数则是在整个字符串里面去查找给定的模式,没有位置限制,但是只会匹配第一个,就算后面还有也不会输出来,所以只输出了一个test,后面的Test也没有输出来。

findall

如果想获取指定内容应该怎么做呢?这里介绍一下正则里面的(),用括号匹配出来的内容可以通过groups()提取,看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re

__author__ = 'anonymous'

if __name__ == '__main__':
str1 = 'sfasfasfsfaname=hello phone=234123423 mail=23423@qq.com,hslfas7'

result = re.match(r'.*name=(\w*).*phone=(\d*) mail=(\d*@\w*.com).*', str1, re.I)

if result is not None:
print result.groups()
pass

看一下输出结果:

1
('hello', '234123423', '23423@qq.com')

可以看到,匹配结果被保存到了一个tuple里面,只要遍历这个tuple或者访问指定顺序的下标就可以获取相应的匹配结果了.
前面也说了,用search()匹配只会输出第一个匹配结果,而且上面的match()如果换成search(),调用groups()返回的结果和使用match()的结果是一样的,因为对于这个正则表达式来说,上面的匹配结果是第一个也是唯一一个。如果想返回字符串中所有出现的as应该怎么做呢?这个时候就需要用findall():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re

__author__ = 'anonymous'

if __name__ == '__main__':
str1 = 'sfasfasfsfaname=hello phone=234123423 mail=23423@qq.com,hslfas7'

result1 = re.findall(r'name=(\w*).*phone=(\d*) mail=(\d*@\w*.com)', str1, re.I)
result2 = re.findall(r'as', str1, re.I)
print result1
print result2
pass

程序的输出结果为

1
2
[('hello', '234123423', '23423@qq.com')]
['as', 'as', 'as']

可以看到findall()函数返回的是一个list,返回所有匹配的结果,如果正则里面有()提取内容,这写内容也会出现在里面,单独保存在一个tuple中.

正则分割

在Python中,如果想对一个字符串进行分割的话,只需要调用str的split方法就可以实现,但是这个split只能根据某个字符来进行分割的操作,如果要同时指定多个字符来进行分割的话,它就无法实现了。
好在re模块也提供了split这个方法来对字符串进行分割,而且这个方法更加强大,可以同时根据多个字符进行分割的操作,下面来看分别看一下str的split和re的split有什么不同的地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re

__author__ = 'anonymous'

if __name__ == '__main__':
str1 = 'helloword,i;am\nalex'
str2 = str1.split(',')
print str2

str3 = re.split('[,|;|\n]', str1)
print str3

看看输出结果:

1
2
['helloword', 'i;am\nalex']
['helloword', 'i', 'am', 'alex']

这里就可以看到差别是啥了,正则分割确实功能更加强大也更加灵活,可以用正则模式分割一个字符串

正则替换

讲到正则分割,当然少不了正则replace(),同理字符串也有替换方法,但是只能替换指定的内容,如果想达到模糊替换,只能用正则,不过正则里面的替换方法不叫replace,叫sub().

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re

__author__ = 'anonymous'

if __name__ == '__main__':
str1 = 'Hello 111 is 222'
str2 = re.sub(r'\d+', '333', str1)
print str2

输出结果为:

1
Hello 333 is 333

正则查询表

平时记住一些常见的的正则用法,有些不太常用需要自己网上去查,这里整理记在一个表里,免得以后上网查:

正则速查表

字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,n匹配字符n\n匹配一个换行符。串行\\匹配\\(则匹配(
^ 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配\n\r之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配\n\r之前的位置。
  • |匹配前面的子表达式零次或多次。例如,zo*能匹配z以及zoo*等价于{0,}
  • |匹配前面的子表达式一次或多次。例如,zo+能匹配zo以及zoo,但不能匹配z+等价于{1,}
    ? |匹配前面的子表达式零次或一次。例如,do(es)?可以匹配doesdoes中的do?等价于{0,1}
    {n} |n是一个非负整数。匹配确定的n次。例如,o{2}不能匹配Bob中的o,但是能匹配food中的两个o
    {n,} |n是一个非负整数。至少匹配n次。例如,o{2,}不能匹配Bob中的o,但能匹配foooood中的所有o。o{1,}等价于o+o{0,}则等价于o*
    {n,m} |mn均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,o{1,3}将匹配fooooood中的前三个o。o{0,1}等价于o?。请注意在逗号和两个数之间不能有空格。
    ? |当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串ooooo+?将匹配单个o,而o+将匹配所有o
    . |匹配除\n之外的任何单个字符。要匹配包括\n在内的任何字符,请使用像(.|\n)的模式。
    (pattern) |匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用\(\)
    (?:pattern) |匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符(|)来组合一个模式的各个部分是很有用。例如industr(?:y|ies)就是一个比industry|industries更简略的表达式。
    (?=pattern) |正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,Windows(?=95|98|NT|2000)能匹配Windows2000中的Windows,但不能匹配Windows3.1中的Windows。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
    (?!pattern) |正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如Windows(?!95&#124;98&#124;NT&#124;2000)能匹配Windows3.1中的Windows,但不能匹配Windows2000中的Windows。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
    (?<=pattern) |反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,(?<=95|98|NT|2000)Windows能匹配2000Windows中的Windows,但不能匹配3.1Windows中的Windows
    (?<!pattern) |反向否定预查,与正向否定预查类拟,只是方向相反。例如(?<!95|98|NT|2000)Windows能匹配3.1Windows中的Windows,但不能匹配2000Windows中的Windows
    x|y |匹配x或y。例如,z&#124;food能匹配zfood(z&#124;f)ood则匹配zoodfood

[xyz] |字符集合。匹配所包含的任意一个字符。例如,[abc]可以匹配plain中的a
[^xyz] |负值字符集合。匹配未包含的任意字符。例如,[^abc]可以匹配plain中的p
[a-z] |字符范围。匹配指定范围内的任意字符。例如,[a-z]可以匹配az范围内的任意小写字母字符。
[^a-z] |负值字符范围。匹配任何不在指定范围内的任意字符。例如,[^a-z]可以匹配任何不在az范围内的任意字符。
\b |匹配一个单词边界,也就是指单词和空格间的位置。例如,er\b可以匹配never中的er,但不能匹配verb中的er
\B |匹配非单词边界。er\B能匹配verb中的er,但不能匹配never中的er
\cx |匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的c字符。
\d |匹配一个数字字符。等价于[0-9]
\D |匹配一个非数字字符。等价于[^0-9]
\f |匹配一个换页符。等价于\x0c\cL
\n |匹配一个换行符。等价于\x0a\cJ
\r |匹配一个回车符。等价于\x0d\cM
\s |匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]
\S |匹配任何非空白字符。等价于[^ \f\n\r\t\v]
\t |匹配一个制表符。等价于\x09\cI
\v |匹配一个垂直制表符。等价于\x0b\cK
\w |匹配包括下划线的任何单词字符。等价于[A-Za-z0-9_]
\W |匹配任何非单词字符。等价于[^A-Za-z0-9_]
\xn |匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,\x41匹配A\x041则等价于\x04&1。正则表达式中可以使用ASCII编码。
\num |匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,(.)\1匹配两个连续的相同字符。
\n |标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
\nm |标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若nm``均为八进制数字(0-7),则\nm将匹配八进制转义值nm。 \nml |如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。 \un |匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)`。

在使用Ubuntu安装软件之后,启动软件有时候会出现软件一直在闪,但是最后无法进入软件启动界面,这种时候,需要在终端中运行,这个时候一般会报错:

1
error while loading shared libraries: libgstreamer-0.10.so.0: cannot open shared object file: No such file or directory

借助apt-file命令可以查找缺少包所依赖的linux文件, 然后用apt-get install安装所对应的文件

我的系统是Ubuntu 16.04,下面的操作都是在此系统上进行的操作:

  • 安装apt-file:
1
2
sudo apt install apt-file -y
sudo apt-file update
  • 查找缺失库

最开始的报错信息里面有关键字libgstreamer-0.10.so.0,这个时候就需要查找libgstreamer-0.10.so.0所对应的库:

1
2
3
➜  Blog git:(master) ✗ apt-file search libgstreamer-0.10.so.0
libgstreamer0.10-0: /usr/lib/x86_64-linux-gnu/libgstreamer-0.10.so.0
libgstreamer0.10-0: /usr/lib/x86_64-linux-gnu/libgstreamer-0.10.so.0.30.0

通过上面的命令查到对应的库是libgstreamer0.10-0,所以直接安装缺失的库就行:

1
sudo apt install libgstreamer0.10-0 -y

然后再重新运行程序,正常情况下应该没啥问题了,如果提示缺失其他的文件,同样的方式,直到不再报错为止.

有时候在项目里面不小心把某个文件删除了,并且代码也推送到远端了,或者我们删除了一个我们认为没啥用的文件,但是过了很长一段时间之后我们又想把这个文件找回来,当然笨一点的办法也有,就是去git的Web端找到删除那个文件的提交版本号,然后通过改动历史把那个文件内容复制,然后在本地新建那个文件,然后把内容复制过来,添加到项目中,自然这个文件也回来了,但是这个方法比较麻烦,下面介绍一下恢复一个被删掉的文件的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
git log --graph

* commit e4985f8fdac2b14468c0348e88180b5348417d04
| Author: san.zhang <san.zhang@gmail.com>
| Date: Fri Dec 18 18:03:44 2015 +0800
|
| xxxxx bugfix2
|
* commit 5bb4f8e49f26e27991d9ab84812cd6eb5a0a3dd6
| Author: san.zhang <san.zhang@gmail.com>
| Date: Fri Dec 18 17:27:21 2015 +0800
|
| xxxxx,buggix2,删除配置文件
|
* commit d835c652209ad430decdef28498d03379b06ae06
| Author: san.zhang <san.zhang@gmail.com>
| Date: Fri Dec 18 16:44:04 2015 +0800
|
| commit 1

commit5bb4f8e49f26e27991d9ab84812cd6eb5a0a3dd6这次提交删错了文件,具体日志为:

1
2
3
4
5
* commit 5bb4f8e49f26e27991d9ab84812cd6eb5a0a3dd6
| Author: san.zhang <san.zhang@gmail.com>
| Date: Fri Dec 18 17:27:21 2015 +0800
|
| xxxxx,buggix2,删除配置文件

这次提交版本号为:5bb4f8e,这个commit之前的commit为d835c65,也就是说在5bb4f8e这个版本中其实是没有这个文件的,在d835c65这个版本中,这个文件的状态为被删之前的样子,我们只要能把文件恢复到这个版本就可以了,怎么做呢?
假设被删的文件叫test.conf,恢复test.conf有两个办法:

1
2
git checkout "5bb4f8e~1" test.conf
git checkout d835c65 test.conf

其实这两个命令是一样的,5bb4f8e~1就是指这个commit的上一次,同理~2指的是前两次

本地改了一个代码,结果忘了推送到远端,晚上下班之后又改动了代码推送到了远端,结果第二天来公司一pull代码,果断悲剧了,冲突了。其实解冲突到没什么,但是其实我是想以远端的代码为准,完全丢弃我本地的代码更改,但是此时已经位于合并分支上,也没办法

1
git stash	# 将更改的代码压栈

如果你已经处在合并代码的分支了,不合并完代码commit是没办法使用压栈命令把代码还原到更改之前的,当然暴力的办法当然是直接把项目删了,重新clone到本地,但是这个方法显然不是很好,如果项目特别大就很慢了:

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  data git:(init) ✗ git revert 5d49773
error: revert is not possible because you have unmerged files.
提示:请在工作区改正文件,然后酌情使用 'git add/rm <文件>' 命令标记
提示:解决方案并提交。
fatal: 还原失败
➜ data git:(init) ✗ git stash
shell/ordercenter/order_snap/order_common_scene_snap.sh: needs merge
shell/ordercenter/order_snap/order_common_scene_snap.sh: needs merge
shell/ordercenter/order_snap/order_common_scene_snap.sh: unmerged (0abc52f84a64ad5d5e8157303b0b50cd095e4319)
shell/ordercenter/order_snap/order_common_scene_snap.sh: unmerged (eb8bd3095d572d0c6bde26eec1b7de0c3359ac78)
shell/ordercenter/order_snap/order_common_scene_snap.sh: unmerged (dce00518150b3a26f5fb04315cd7b3fedf1c2396)
fatal: git-write-tree: error building trees
无法保存当前索引状态

网上查阅了一下,有个命令可以将代码还原到指定历史:

1
2
3
4
5
6
➜  data git:(init) ✗ git fetch --all
正在获取 origin
➜ data git:(init) ✗ git reset --hard origin/init
HEAD 现在位于 612af0e update
➜ data git:(init) git pull
Already up-to-date.

**备注:**git fetch 只是下载远程的库的内容,不做任何的合并 git reset 把HEAD指向刚刚下载的最新的版本

由于经常在两台电脑上同步博客,一般博客文件夹下面的文件我会单独同步到Git上的一个项目。但是碰上重装系统了,虽然博客的内容可以直接从Github上同步下来,但是主题没了,所以研究了一下主题同步的方法。

系统环境

1
2
3
4
5
6
7
8
9
10
11
12
➜  Blog git:(master) ✗ hexo -v
hexo: 3.2.0
hexo-cli: 1.0.1
os: Linux 4.4.0-28-generic linux x64
http_parser: 2.5.0
node: 4.1.1
v8: 4.5.103.33
uv: 1.7.4
zlib: 1.2.8
ares: 1.10.1-DEV
modules: 46
openssl: 1.0.2d

我用的主题是jacmam,顺便说一下,Hexo的主题列表地址为Hexo主题列表

主题同步

这里采取的是fork + subtree来实现同步,下面都是以jacman主题为例

  • fork目标主题

jacman的git地址为:https://github.com/wuchong/jacman.git,然后我fork这个主题,于是我的项目的地址为:https://github.com/sjq597/jacman,然后我先把我本地的主题删掉:

  • 主题集成同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  Blog git:(master) ✗ sudo rm -r themes/jacman
➜ Blog git:(master) ✗ git remote add -f jacman https://github.com/sjq597/jacman
更新 jacman 中
warning: no common commits
remote: Counting objects: 1292, done.
remote: Total 1292 (delta 0), reused 0 (delta 0), pack-reused 1292
接收对象中: 100% (1292/1292), 2.81 MiB | 311.00 KiB/s, 完成.
处理 delta 中: 100% (665/665), 完成.
来自 https://github.com/sjq597/jacman
* [新分支] closeaside -> jacman/closeaside
* [新分支] gh-pages -> jacman/gh-pages
* [新分支] master -> jacman/master
* [新分支] site -> jacman/site
* [新标签] v0.9.0 -> v0.9.0
➜ Blog git:(master) ✗ git subtree add --prefix=themes/jacman jacman master --squash
git fetch jacman master
来自 https://github.com/sjq597/jacman
* branch master -> FETCH_HEAD
Added dir 'themes/jacman'
➜ Blog git:(master) ✗ git fetch jacman master
来自 https://github.com/sjq597/jacman
* branch master -> FETCH_HEAD

这样就把jacman作为了我的博客Blog项目的一个子项目了,子项目可以单独更新以及推送,同理,如果父项目中更新了子项目,那么这个更新也会推送到父项目,下面有一些常用的命令:

1
2
3
git commit -a -m 'update some'
git subtree push --prefix=themes/jacman/ jacman master
git push origin master # 顺便主项目也 push 了

或者单独推送子项目:

1
git subtree push -P themes/jacman/ jacman master

命令参考:

1
2
3
4
5
6
git subtree add -P <prefix> <commit>
git subtree add -P <prefix> <repository> <ref>
git subtree pull -P <prefix> <repository> <ref>
git subtree push -P <prefix> <repository> <ref>
git subtree merge -P <prefix> <commit>
git subtree split -P <prefix> [OPTIONS] [<commit>]

有时需要用到外部传入的参数,虽然简单的使用*arg数组可以获取到每个参数的值,但是有一个局限性就是参数必须按顺序传入,不能多也不能少。
但是考虑到有时候我们需要制定特定的参数的值,参数的个数,顺序都是不定的,这个时候单纯的靠*args显然无法满足我们的需求,这个时候就轮到argparse模块上场了,基本上一般的参数解析都可以胜任,下面看看怎么用:

参数的几种解析方式

主要有四种方式,简单介绍一下,先介绍一下最简单的按顺序解析不带key指定参数形式:

不带key按顺序解析

这种方式可以获取脚本运行时后面的所有参数,但是顺序由输入的顺序决定的,
先看看测试代码,假设文件名叫args.py:

1
2
3
4
5
6
7
8
9
#! /usr/bin/python
# coding=utf-8

import sys

if __name__ == '__main__':
for arg in sys.argv:
print arg
pass

来一段测试代码:

1
2
3
4
5
6
$ python args.py 12 34 56 78                                                                                                
args.py
12
34
56
78

第一个参数就是python脚本的名字,后面四个就是四个参数了.但这个有时候我们希望我们传入参数的顺序可以是随意的,只要我们指定每个参数的key,程序就能解析到对应的参数值,那应该怎么做呢?下面介绍一下带key的参数解析。

不带-参数

新建一个脚本arg_demo.py,然后输入以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse

__author__ = 'anonymous'

if __name__ == '__main__':
parse = argparse.ArgumentParser()
parse.add_argument('a', help='必填参数')
args = parse.parse_args()

print args.a
pass

终端运行:

1
2
3
4
5
6
7
8
9
➜  parameter git:(python-note) ✗ python arg_demo.py                    
usage: arg_demo.py [-h] a
arg_demo.py: error: too few arguments
➜ parameter git:(python-note) ✗ python arg_demo.py 我是参数
我是参数
➜ parameter git:(python-note) ✗ python arg_demo.py 我是参数1 我是参数2
usage: arg_demo.py [-h] a
arg_demo.py: error: unrecognized arguments: 我是参数2
➜ parameter git:(python-note) ✗

可以看出不带-的参数在调用脚本时必须输入值,并且输入的顺序必须和程序定义的一直,而且个数也得一致

-的参数

同样的文件,改动以下代码

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse

__author__ = 'anonymous'

if __name__ == '__main__':
parse = argparse.ArgumentParser()
parse.add_argument('-a')
args = parse.parse_args()

print args.a

同样,我们在终端中输入不同值运行一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  parameter git:(python-note) ✗ python arg_demo.py                          
None
➜ parameter git:(python-note) ✗ python arg_demo.py 我是参数
usage: arg_demo.py [-h] [-a A]
arg_demo.py: error: unrecognized arguments: 我是参数
➜ parameter git:(python-note) ✗ python arg_demo.py -a 我是参数
我是参数
➜ parameter git:(python-note) ✗ python arg_demo.py -a我是参数
我是参数
➜ parameter git:(python-note) ✗ python arg_demo.py -a 我是参数1 -a 我是参数2
我是参数2
➜ parameter git:(python-note) ✗ python arg_demo.py -a 我是参数2 -a 我是参数1
我是参数1

可以看出,带-的参数,可以输入也可以不输入,但是不能输入的时候不指定key,并且输入的key可以和参数分开或者连在一起,多次对一个key输入值,后面的会覆盖前面的输入。

--参数

-参数还可以指定shorname,即--shortname这种格式,表示变量的别名,改动一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse

__author__ = 'anonymous'

if __name__ == '__main__':
parse = argparse.ArgumentParser()
parse.add_argument('-a', '--another_name', default='我是默认参数')
args = parse.parse_args()

print args.another_name

同理在终端中运行:

1
2
3
4
➜  parameter git:(python-note) ✗ python arg_demo.py                          
我是默认参数
➜ parameter git:(python-note) ✗ python arg_demo.py -a输入参数
输入参数

可以看到如果不输入就输入默认值,输入了我们引用别名another_name也可以输出-a的值

解析参数的其他属性

argparse还有很多可选参数,用来设置我们解析参数的具体操作,例如:

  • dest
    这个参数表示绑定参数在程序中对应的变量名称

    1
    add_argument("a",dest='code_name')

    表示a参数直接绑定到程序中的变量值code_name

  • default
    为参数提供默认值,如果没有输入这个参数,就用默认值代替,注意不带-的参数不能制定默认值,因为不带-必须输入参数值,也就没有不输入而采用默认值的场景

  • help
    参数的帮助文档,一般用来告诉用户这个参数是什么意思,起提示和指导作用

  • type
    为参数指定一个类型,一般不指定的时候,默认会把输入的参数解析成字符串类型

    1
    add_argument("c", type=int)
  • action
    指对参数的具体操作

参数 解释
store 默认就是这个模式,存储值到制定变量
store_const 存储值在参数的const部分指定,多用于实现非布尔的命令行flag
store_true/store_falst 布尔开关,store_true默认为False,输入则为True,store_false则相反
append 存储值到列表,该参数可以重复使用
append_const 存储值到列表,存储值在参数的const部分指定
count 统计参数简写输入的个数
version 输出版本信息然后退出
  • const
    配合action="store_const|append_const"使用,默认值

  • choices
    输入值的范围

    1
    add_argument("--gb", choices=['A', 'B', 'C', 0])
  • required
    默认为False,若为True,则必须输入该参数.

经常需要把脚本放到后台执行,但是出了问题也不知道错误出在哪,最好是把任务在前端执行输出到终端的标准输出以及标准错误输出都输出到文件,等程序出错了可以知道问题出在哪:

1
bash test.sh > data.log 2>&1

之前一直使用ss代理上google,但是最近不知道怎么回事儿,本地运行

1
sslocal -c /etc/config.json

报错:

1
2
3
4
INFO: loading config from doc/config.json
2016-06-26 12:34:53 INFO loading libcrypto from libcrypto.so.1.0.0
2016-06-26 12:34:53 INFO starting local at 127.0.0.1:1080
2016-06-26 12:34:53 ERROR [Errno 98] Address already in use

主要是找不到占用这个端口的进程,也不知道怎么把占用端口的进程杀掉,网上查了相关解决方法,总算找到解决方案:
首先得找到是哪个进程占用了这个端口,即找出进程的pid:

1
2
3
➜  ~ netstat -anp | grep 1080             
(并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户)
tcp 0 0 0.0.0.0:1080 0.0.0.0:* LISTEN 1955/EmbedThunderMa

看到没,那个1955就是罪魁祸首,于是再通过pid查看具体是哪个进程:

1
2
3
➜  ~ ps -ef | grep 1955
anonymo+ 1955 1871 0 09:47 ? 00:00:18 /opt/xware-desktop/xware/lib/EmbedThunderManager *********
anonymo+ 5621 2847 0 10:41 pts/0 00:00:00 grep --color=auto 1955

终于找到原因,原来是前一阵子为了下东西,装了一个Linux版的迅雷,虽然最后还是没法下载,但是这东西有个服务开机自动启动,占用了我的1080端口,导致我想用没法用
果断卸载了坑爹的迅雷,然后把这个进程杀了

1
sudo kill -9 1955

然后果然1080端口可以用了。

0%