年度归档:2017年

第3章 Python基础

3.1 Python简介

前面我们介绍了Linux操作系统、MySQL数据库。Linux作为大数据平台的基石,其重要性,在这里就不说了;MySQL数据库或推广为其他数据库(实际上其他数据库,如Oracle、DB2、SQL server等)作为存储和管理数据系统,在企业中已运行几十年(当然还将将继续使用下去,在事物处理方面尤其独特优势)积累大量各行各业的数据,这些数据是大数据重要来源。平台有了,数据有了,接下来要做的就是处理和分析这些数据,而Python恰恰是解决这类问题的高手之一,可以毫不夸张的说,Python将把你带进一个数据处理、数据分析的五彩缤纷世界!Python将助你达到一个新境界!
3.1.1 Python概述
Python是做啥用的? 为啥要学Python?在数据处理、数据分析、机器学习等方面除了Python还有其他一个工具,如R,MATLAB等,我们为啥选择Python?1、它开源,2,它易学,3,它强大,4,它与时俱进,它和大数据计算平台SPark的结合,可为强强联合,优势互补、相得益彰!Spark后面我们会重点介绍。
3.1.2 Python简介
Python是一种动态的高级解释性编程语言,简单易学,可读性高,功能强大,目前已广泛用于学校、企业、金融机构等。Python支持面向对象、模块和函数式编程,可以在windows、linux、Unix等操作系统上运行。它有如下主要特征:
 开源:
由荷兰人Guido van Rossum在1989开发的,1991发行第一个正式版本,Python开发的里程碑:
1991年发行Python0.9.0;
1994年发行Python1.0;
2000年发行Python2.0;
2008年发行Python2.6;
2010年发行Python2.7;
2008年发行Python3.0;
2010年发行Python3.3;
2014年发行Python3.4.
从2008年开发有两版本在同时使用,而且这两个版本的代码不是100%兼容的,目前大部分实际使用的一般是Python2.6或Python2.7版本编写。
 跨平台;
Python支持常用的操作系统,如Windows、Linux、Unix和Mac OS,既可在集群,也可运行在一些小设备上。
 面向对象
Python支持面向对象、命令式、函数或过程式编程。
 动态性
Python与JavaScript、PHP、Perl等语言类似,无需预先声明,直接赋值即可。
 缩进感知
Python和大部分编程语言不同,它没有分号、begin、end等标记,使用缩进标记代码块、逻辑块,代替圆括号、方括号或分号等。
 多用途
目前Python已广泛应用于web开发、数据库、图像处理、自然语言处理、网络、操作系统扩展等大型应用程序,也用于高级数据分析、图形处理等领域。
3.1.3 Python重要库


3.1.4 安装配置
上一节介绍的这些库可以用python的安装管理包Anaconda轻松搞定,Anaconda有Linux、windows平台的,这里以2系列的包为例,其它版本安装类似。在Linux下安装,先下载管理包Anaconda2-4.0.0-Linux-x86_64.sh,这是2系列的安装包,3系列的包类似为Anaconda3-4.3.1-Linux-x86_64,然后在Linux命令行下运行:bash Anaconda2-4.0.0-Linux-x86_64.sh,然后按缺省步骤即可,如果要安装其他库,如scipy,只要运行conda install scipy即可。
Python安装完成后,在命令运行python后,启动交互式Python解释器:

其中>>>为提示符,说明已进入python环境,用户可在后面输入python命令、变量等。
Python解释器通过一次运行一条语句。

>>> 2+3
5
>>> print "hello world!"
hello world!
>>> a=10
>>> a
10

退出Python解释器,在提示符下输入quit() 或 Ctrl-D即可。

当然我们也可用IPython,IPython是一种加强的交互式Python解释器,它更加方便、功能更强大、更神奇,它已经成为Python科学计算界的标准配置。启动Ipython,只要在命令行输入ipython。

在IPython我们可以进行计算、编程、运行脚本(用%run 脚本)、copy脚本(%paste)、测试脚本、可视化数据等,甚至可以运行shell命令,当然Tab补全功能、回看历史命令等它都有,方便快捷,功能强大,退出IPython解释器,在命令行输入quit或exit或Ctrl+D。后面我们以IPython为主。

3.2 常量与变量

3.2.1 语言特点
Python语言一个最大特点就是代码缩进与冒号,这是其他任何语言所没有的,这个约束既带来了Python程序的高可读性,也带来了一些其他声音。
Python通过空格或制表符来表示代码之间的层次关系或逻辑关系,而且通过空格缩进是其语法之一,必须严格遵守,否则可能报错或逻辑错误。它不像其它语言如Java、C++、SQL等,这些语句通过花括号或BEGIN...END等关键字来界定代码间的层次关系、开始结束等。
下面我们通过一个实例来进一步了解Python这个特点。

In [1]: a=10

In [2]: if a==10:
...: print("a is ",a) ###缩进4格(IPython在冒号后自动缩4个空格)
...: else:
...: print("a is ",a) ###缩进4格
...: a=a+10 ###缩进4格
...: print("a is ",a)
...:###以下为运行结果
('a is ', 10)
('a is ', 10)
########################################
a=10
a=input()
if a==10:
print("a is ",a) ###缩进4格(IPython在冒号后自动缩4个空格)
else:
print("a is ",a) ###缩进4格
a=a+10 ###缩进4格
print("a is ",a)

【几点说明】
1、冒号(:) 表示一段缩进代码的开始,其后的所有代码都必须缩进相同的量(否则将报错),直到代码块结束。
2、缩进量建议用4个空格,这也是IPython在冒号后自动缩进的空格数,不建议使用Tab制表符。
3、代码缩进量不同,可能结果也会不同。
4、通过冒号和缩进,也同时可少写很多关键字(如then,if,end if之类)。

如果我们把a=a+10语句放在与if语句同一级,运算结果将不同,具体请看下例:

In [8]: a=10

In [9]: if a==10:
...: print("a is ",a)
...: else:
...: print("a is ",a)
...: a=a+10 ###该句于if语句处于相一级,影响a的结果
...: print("a is ",a)
...: ###运行结果
('a is ', 10)
('a is ', 20)
以上代码我们也可以保存为py文件,然后在IPython直接执行运行该文件,非常方便。
In [11]: !cat mytest.py
a=10

if a==10:
print("a is ",a)
else:
print("a is ",a)
a=a+10
print("a is ",a)

In [12]: %run mytest.py ##运行该脚本或import mytest
('a is ', 10)
('a is ', 10)

为了使该脚本有更好的移植性,可在第一行加上一句#!/usr/bin/python
运行.py文件时,python自动创建相应的.pyc文件,如下图,.pyc文件包含目标代码(编译后的代码),它是一种python专用的语言,以计算机能够高效运行的方式表示python源代码。这种代码无法阅读,故我们可以不管这个文件。
python程序是使用名为虚拟机的特殊软件运行的。这个软件模拟计算机,是专为运行在python上而设计的,这让很多.pyc文件无需做任何修改就能在不同的计算机上系统上运行。

类似于shell脚本中的一行。然后,我们可以在命令行运行该该脚本,无需在显式使用Python或IPython解释器。如下例:

feigu@slave001:~$ cat mytest.py
#!/usr/bin/python

a=10

if a==10:
print("a is ",a)
else:
print("a is ",a)
a=a+10
print("a is ",a)
feigu@slave001:~$ python mytest.py ###在命令行运行下,通过python运行该脚本
('a is ', 10)
('a is ', 10)

3.2.2 注释
备注或注释,采用井号(#),注释多行,可以在每行前加#,或前后分别用三个单引号’’’,
如果注释中含中文,一般需要在python文件的最前面加上如下注释:
#-*- coding: utf-8 -*-
python3系列无需这句,因python3缺省字符集就是utf8。
说起这个注释符(#),我们应该不陌生了,shell中的注释也是#,SQL中也可使用。

#!/usr/bin/python
#-*- coding: utf-8 -*-

a=10
###if 判断语句
if a==10:
print("a is ",a)
else:
print("a is ",a)
a=a+10
print("a is ",a)

3.2.3模块导入
模块导入跟我们有啥关系?为什么要进行模块导入?学过Java的朋友,可能不会感到陌生,但对没有接触过Java,Python等新手来说,有这样的疑问,正常,能提出这样的疑问朋友,不错。
模块可用看成是一些对象或函数的集合。前面我们介绍了Python很强大,它确实很强大,他有成千上万的模块可用,不过绝大部分模块都处于“休眠状态”,如果我们要使用这些模块,需要通过import 模块或from 模块 from 函数等方式将它们"激活"。
用import 导入模块,引用模块中的函数时,还需要使用模块.函数的方式,如果你直接用其函数,不行带上模块这个“累赘”,可用采用from 模块 import 函数的方式,以下通过实例进一步说明如何使用以下两种导入方式的异同。
 import 模块
 from 模块 import 函数

In [1]: 1+2
Out[1]: 3

In [2]: max(1,2) ##这些常用函数启动python时就已“激活”
Out[2]: 2

In [3]: sqrt(4) ###使用这个平方根函数,报错,它所在模块没导入
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 sqrt(4)

NameError: name 'sqrt' is not defined

In [4]: import math ###平方根函数在math模块中,导入该模块

In [5]: math.sqrt(4) ###模块.函数,可用使用
Out[5]: 2.0

In [6]: sqrt(4) ###想直接用函数,报错
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 sqrt(4)

NameError: name 'sqrt' is not defined

In [7]: from math import * ###把模块及函数都导入

In [8]: sqrt(4) ###这下简单多了
Out[8]: 2.0

3.2.4变量与常量
在讲shell时,我们介绍了变量,shell中的变量拿来就可用,无需声明它是哪种数据类型,非常简单,Python也“继承”了这个优点,无需声明它是哪种数据类型,不像Java、c++等用一个变量还有先说明它哪种数据类型,否则还不让你用。好东西大家喜欢,scala也沿用了这一优点。
不声明数据类型,并不意味着Python就不需要数据类型,只是它更智能一点,它根据其值来推导。
变量的赋值可以一次一个变量、一次多个变量。

In [9]: a=2
In [10]: type(a)
Out[10]: int
In [11]: b="python"
In [12]: type(b)
Out[12]: str
In [13]: a+b
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 a+b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
In [14]c=range(1,11)
In [15] d=xrange(1,11)
for i in d:
....:print i
###或[i for i in d]

【说明】
xrange 用法与 range 完全相同,所不同的是生成的不是一个list对象,而是一个生成器。要生成很大的数字序列的时候,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间。
python虽然无需声明变量类型,但它还是区分数据类型的,而且变量的引用无需像shell中变量一样要加上$,它可直接引用。
常量比较好理解,就是一些不变的量,如1,a等。
3.2.5变量如何赋值
变量赋值,通过赋值运算符=,把右边的值赋给左边的变量,具体如下:

应用变量时,需要初始化,否则将报错。

In [18]: v1=2+v0
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 v1=2+v0
NameError: name 'v0' is not defined

In [19]: v0=10 ###初始化
In [20]: v1=2+v0

In [21]: v1
Out[21]: 12

3.2.6多重赋值
python中,可以同时各多个变量赋值:

In [22]: x=10;y=20 ##一次给多个变量赋值
In [23]: a1,a2,a3=1,2,'python' ##一次给多个变量赋值
In [24]: a1,a2,a3
Out[24]: (1, 2, 'python')

3.3 控制语句

控制语句包括条件判断语句、循环语句,控制语句根据条件表达式控制程序的流转。
本章主要介绍
 if条件判断语句
 for循环语句
 while循环语句
 contnue及break语句
3.3.1if判断语句
if语句是一种最常用的条件判断语句,用于判断一个条件,如果成立,则执行紧跟其后的代码块,其语法格式为:
if (表达式):
代码块

if a>0:
print 'a 是正数'

if a>0 and b>0:
print 'a和b都是正数'
if语句也可以有多个分支,如if ...else,if ...elif ... else
a=10
if a>0:
print "a 为整数"
elif a==0:
print "a 为零"
else:
print "a 为负数"

3.3.2 代码块和缩进
对于Python而言代码缩进是一种语法,Python没有像其他语言一样采用{}或者begin...end分隔代码块,而是采用代码缩进和冒号来区分代码之间的层次。缩进的空白数量是可变的,但是在一个模块中所有代码块语句必须包含相同的缩进空白数量,这个必须严格执行,否则将作为语法错误。

有时候代码采用合理的缩进但是缩进的情况不同,代码的执行结果也不同。有相同的缩进的代码表示这些代码属于同一代码块。例如:

# -*- coding: UTF-8 -*-
if 1>0:
print("Hello girl!")
else:
print("Hello boy!")
print("end")
print("=========华丽的分割线===========")
if True:
print("Hello girl!")
else:
print("Hello boy!")
print("end")
运行结果如下:
Hello girl!
end
=========华丽的分割线===========
Hello girl!

【思考】
以上两个print("end")的区别。

3.3.3for循环语句
for循环语句可用于遍历一个集合(如列表、元组等)或迭代器,依次访问集合或迭代器中的每项或每个元素。其语法格式为:
for 变量 in 集合:
代码块

a=range(10) #a=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a=range(10)
for i in a:
i+=1
print i ###输出结果为10

当然for 代码块中增加一些控制循环的关键字如continue或break。continue使for循环提前进入下一个循环或下一个迭代(跳过代码块的剩余部分,直接进入下一轮循环),而break是立即跳出for循环。下面通过实例来进一步说明它们的使用。

In [105]: b=[10,20,a,30,a]
In [106]: for i in b:
.....: if i is a:
.....: continue
.....: print i*2
.....:
20
40
60

如果我们把上面的continue关键字换成break,看一下结果:

In [108]: for i in b:
.....: if i is a: ###遍历到第一个a,立即结束for循环语句
.....: break
.....: print i*2
.....:
20
40

3.3.4 while循环语句
while循环定义了一个条件和代码块,只要条件满足,则代码块将一直被执行。其语法格式为:
while 条件:
代码块
下面通过实例来具体说明while的使用。

In [115]: x=10
In [116]: while x左数
Out[20]: 'n'
In [21]: "python"[-1]
Out[21]: 'n'
In [22]: "python"[-3] ##从右往左算第3个元素
Out[22]: 'h'

3.4 序列的基本操作

元组、字符串、列表统称为序列,它们除都有索引外,还有一些共性的操作,如索引、切片、相加、乘法、属员等操作,此外,还有计算序列的长度、最大与最小元素等。
3.4.1 索引
序列中的每个元素都分配一个序号,第一个元素的序号(或编号)为0,第二个元素的序号为1,以此类推,这些序号或编号就是索引。序列的元素可以通过其索引来访问。

In [16]: a="python"
In [17]: a[0]
Out[17]: 'p'
In [18]: a[1]
Out[18]: 'y'
In [19]: a[5]
Out[19]: 'n'

索引由左到右,索引由0开始,然后依次加1;索引也可以从右到左,索引从-1开始、依次减1。

In [20]: a[-1] ##负号表示从右->左数
Out[20]: 'n'
In [21]: "python"[-1]
Out[21]: 'n'
In [22]: "python"[-3] ##从右往左算第3个元素
Out[22]: 'h'

3.4.2 分片
可以通过索引来访问一个元素,是否可以通过索引一次访问多个元素呢?可以的,通过分片操作即可,分片三要素:
1、是索引运算符[]
2、分片的开始与结束位置,即start:stop,stop不在分片范围内。
3、开始与结束通过冒号(:)来分割,start索引所在元素包括在内,但stop索引所在元素不在分片范围内容。start或stop可以省略。具体请看以下实例

In [24]: b=[1,2,3,4,5]
In [25]: b[0:3] ##注意索引3对应元素不在分片范围内。
Out[25]: [1, 2, 3]

In [26]: b[2:] ##查看第3个开始及以后所有元素
Out[26]: [3, 4, 5]

In [27]: b[:] ##表示所有索引
Out[27]: [1, 2, 3, 4, 5]
In [28]: b[-1:]
Out[28]: [5]

In [29]: b[-3:]
Out[29]: [3, 4, 5]

如果只想选择偶数或奇数或选择其他步长,能否实现呢?如果能,又该如何实现呢?请看下例:

In [30]: b[::2] ###第二个冒号后的表示步长
Out[30]: [1, 3, 5]

In [31]: b[0:3:2] ###分片并取步长
Out[31]: [1, 3]

步长不能为零,但是否可以负,大家可尝试一下。
3.4.3 相加
我们知道数字可以相加,字符串也可相加,但数字和字符不能相加。序列是否可以相加呢?如果能相加,又有何限制呢?
可以相加,相加的条件和通常的数字或字符串相加一样,不同类型的不能相加。如元组不能和列表、字符串相加,列表不能和元组、字符串相加,尽管他们都是序列。只要两种相同类型的才能相加。

In [10]: b1=[6,7,8,9,10]

In [11]: b+b1
Out[11]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In [12]: b2=['a','b']

In [13]: b+b2
Out[13]: [1, 2, 3, 4, 5, 'a', 'b']

In [14]: a+b ##不同类型不能相加,a是字符串,b是列表。
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 a+b

TypeError: cannot concatenate 'str' and 'list' objects

3.4.4 乘法
这里的乘法指用一个数字n乘以一个序列生成一个新的序列,新的序列重复原来序列的n次。以下通过实例进一步说明。

In [19]: a*2
Out[19]: 'pythonpython'

In [20]: b*2
Out[20]: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

In [21]: c=(1,2,3)

In [22]: c*3
Out[22]: (1, 2, 3, 1, 2, 3, 1, 2, 3)

3.4.5 最大(小)值及长度
如果我们想知道一个序列的最大或最小值或这个序列的长度,这个长度指序列中元素的个数,尤其这个序列比较长或元素很多时,我们该如何求你?是否有现成的函数?我们知道MySQL里有现成或内置的函数。
Python提供了这方面的函数,如max、min、len等,这些函数是Python的内置函数。或Python自带的函数,我们直接使用即可。以下通过实例来进一步说明这些函数的使用。

In [23]: a
Out[23]: 'python'

In [24]: b
Out[24]: [1, 2, 3, 4, 5]

In [25]: max(b)
Out[25]: 5

In [26]: max(a)
Out[26]: 'y'

In [27]: len(a)
Out[27]: 6

In [28]: len(b)
Out[28]: 5

In [29]: e=[1,2,4,[3,4]] ##这里[3,4]是e的一个元素,故元素个数为4

In [30]: len(e)
Out[30]: 4

3.4.6 排序
对序列进行排序也是一件很平常的事,给序列排序可以sorted(序列)函数,缺省为升序,返回结果为新的有序列表。使用sorted函数不改变原序列的次序。

In [18]: sorted((1,4,5))
Out[18]: [1, 4, 5]

In [19]: sorted("spark")
Out[19]: ['a', 'k', 'p', 'r', 's']

In [20]: sorted((1,5,4,2))
Out[20]: [1, 2, 4, 5]

In [21]: sorted([4,7,2,5])
Out[21]: [2, 4, 5, 7]

In [23]: sorted([4,7,2,5],reverse=True) ##把reverse置为True将使用降序
Out[23]: [7, 5, 4, 2]

In [32]: p1=[7,4,9,6]
In [33]: sorted(p1) ##生成新的列表
Out[33]: [4, 6, 7, 9]

In [34]: p1 ###不会改变原序列的元素的次序
Out[34]: [7, 4, 9, 6]

sorted函数更详细使用可以通过help(sorted)来查询。
3.4.7 元组
上节介绍了序列一些共性方面的内容,如索引、分片等。元组(tuples)是Python中一种常见的数据结构,且不能修改或称为不可变序列,故除了序列常用操作外,没有太多其他操作。以下主要介绍元组的几种创建方法。

In [33]: t1=1,2,4,8 ##用逗号分割一些值,自动创建元组
In [34]: t1
Out[34]: (1, 2, 4, 8)

In [35]: t2="python","spark" ##用逗号分割一些值,自动创建元组
In [36]: t2
Out[36]: ('python', 'spark')

In [37]: t3=() ##元组可以没有值
In [38]: t3
Out[38]: ()

In [39]: t4=(1)
In [40]: t4 ##从结果来看,(1)不是元组,元组应该是使用圆括号
Out[40]: 1

In [41]: t5=(1,) ##如果元组只有一个元素,需要添加一个逗号
In [42]: t5
Out[42]: (1,)

In [43]: t6=tuple([1,2,3,4]) ###tuple函数(序列)生成元组
In [44]: t6
Out[44]: (1, 2, 3, 4)

In [45]: t7=tuple("SparkSQL")
In [46]: t7
Out[46]: ('S', 'p', 'a', 'r', 'k', 'S', 'Q', 'L')

In [47]: t8=tuple(2,3,4) ###tuple的参数要是序列,显然2,3,4没被认为是序列
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 t8=tuple(2,3,4)

TypeError: tuple() takes at most 1 argument (3 given)

In [48]: t9=tuple((2,3,4))
In [49]: t9
Out[49]: (2, 3, 4)

3.4.8 列表
列表为可修改序列,或称为可变序列,故列表上的操作相对比较丰富。这也是列表区别于元组、字符串的主要特点。以下对各种操作进行介绍。
3.4.8.1 列表的创建
列表通常用方括号"[]"或list(序列)函数进行定义,以下通过实例来说明。

In [7]: l1=[2,5,6,9]
In [8]: l1
Out[8]: [2, 5, 6, 9]

In [9]: l2=list("sparksql") ##
In [10]: l2
Out[10]: ['s', 'p', 'a', 'r', 'k', 's', 'q', 'l']

In [11]: l3=list((2,3,6,9))
In [12]: l3
Out[12]: [2, 3, 6, 9]

In [13]: l4=list([3,4,5])
In [14]: l4
Out[14]: [3, 4, 5]

3.4.8.2 添加或移除元素
既然列表是可变序列,这就意味着我们可以对其增删改,修改列表的方法比较多,甚至同一一种操作就有几种实现方法,各种实现方法的场景不同,效率也可能不同。
通过append方法,可以将元素添加到末尾,而用insert可以插入到指定位置,insert比较灵活但计算量比较大。

In [1]: lista=['python','R','hadoop','spark']

In [2]: lista.append('sparkml') ###在末尾添加一个元素

In [3]: lista
Out[3]: ['python', 'R', 'hadoop', 'spark', 'sparkml']

In [4]: lista.insert(3,'hdfs') ##在索引为3的位置上插入hdfs

In [5]: lista
Out[5]: ['python', 'R', 'hadoop', 'hdfs', 'spark', 'sparkml']
insert的逆运算为pop,它可以在指定位置移除元素,remove是按值删除数据,它仅删除第一个匹配的元素。
In [6]: listb=[2,3,1,2,7,5]

In [7]: listb.pop(2) ###这个2是指索引
Out[7]: 1

In [8]: listb
Out[8]: [2, 3, 2, 7, 5]

In [9]: listb.remove(2) ##这个2不是索引,是指元素2,只删除第一个2

In [10]: listb
Out[10]: [3, 2, 7, 5]

判断一个元素是否在列表中,可以用in这个关键字或运算符。

In [11]: 'hdfs'in lista
Out[11]: True

In [12]: 'sparkR'in lista
Out[12]: False

按索引查询比按值查询快很多,在列表中如此,但在后续我们将介绍的字典中,按值查询效率将比列表高很多,字典后面将介绍。
3.4.8.4 排序
对列表进行排序可以sort方法或sorted方法,sort实现就地排序,即改变原列表元素的次序,不生成新的列表;使用sorted排序,原列表元素次序不变,而是生成新的列表。此外sorted可用于可迭代序列,可迭代序列这个概念后面将介绍。

In [19]: listd=[3,5,2,1]
In [20]: listd.sort()
In [21]: listd ##列表的次序发生改变

Out[21]: [1, 2, 3, 5]

In [24]: liste=['red','blue','yellow','gree']
In [25]: listf=sorted(liste)

In [26]: listf
Out[26]: ['blue', 'gree', 'red', 'yellow']

In [27]: liste ##原列表次序未变
Out[27]: ['red', 'blue', 'yellow', 'gree']

数据库排序时通常指明用哪个字段或降序还是升序,在列表中是否可以呢?如果能,如何实现?当然可以,这就是接下来介绍的内容。sort或sorted两个重要参数key和reverse,key就就相当于指定排序的字段,这里key参数是函数,这个函数作用于列表中每个元素,reverse参数为False或True,False为默认的升序,True为降序。以下为具体使用实例。

In [1]: liste=['red','blue','yellow','gree']
In [2]: liste.sort(key=len,reverse=True) ##根据元素长度进行排序
In [3]: liste
Out[3]: ['yellow', 'blue', 'gree', 'red']

In [5]: sorted(liste,key=len) ##排序方式缺省为升序
Out[5]: ['red', 'blue', 'gree', 'yellow']

3.4.9 字符串
字符串是除数字外最重要的数据类型,字符串无处不在。字符串是一种聚合数据结构,我们可以使用索引和切片的方法。

3.4.9.1 字符串索引
字符串索引从0开始(这和我们使用尺子刻度从0开始一样),依次为1,2,,,到len(str)-1等等,当然也可以从右到左,此时,索引从-1开始,-2,-3,,,到-len(str)。如下图:

字符串负索引:

n [23]: s="Python"

In [24]: s[0]
Out[24]: 'P'

In [25]: s[5]
Out[25]: 'n'

In [26]: s[-1]
Out[26]: 'n'

In [27]: s[:2] ##字符串切片
Out[27]: 'Py'

In [28]: s[2:5] ##字符串切片
Out[28]: 'tho'

3.4.9.2 字符串函数
字符串的方法比较多,这里介绍几种常用方法,此外,简单介绍字符串格式化问题。
lower、upper实现字符大小写的转换;replace替换字符;strip去除两侧空格;split将字符串分割成列;字符串的正则表达式(re)。以下通过实例具体说明这些方法或函数的使用。

In [6]: 'Python'.lower
Out[6]:

In [7]: 'Python'.lower()
Out[7]: 'python'

In [8]: 'Python'.upper()
Out[8]: 'PYTHON'

In [9]: 'Python|MySQL|Hadoop|Spark'.split('|')
Out[9]: ['Python', 'MySQL', 'Hadoop', 'Spark']

In [10]: ' Scala '.strip()
Out[10]: 'Scala'

In [11]: 'Python'.replace('P','IP')
Out[11]: 'IPython'

3.4.10 字典
我们知道汉语字典、新华字典、英汉字典等,用这些字典可以根据一个字来查找有关这个字的使用方法,如读音、词语、成语、详细含义等等。这个可通过目录或索引来查找内容有点不同。
这里介绍的Python字典,本质上有点像上面讲的一些字典,就是通过关键字来查询其对应值,相当于Java中的HashMap。
字典(dict)是由多个键(key)和对应的值(value)构成的键-值对组成(键-值对又称为项),每个键和它对应的值之间用冒号(:)隔开,项之间用逗号(,)隔开,而整个字典放在在一个大括号{}里,键是唯一的,但值可以不唯一。字典格式,如:d={'liuhai':26 ,'gaofeng':30}
3.4.10.1 创建字典
字典的创建有多种方法,以下介绍几种常用方法:
1、使用大括号并用冒号分隔键和值,键-值对用逗号分隔。
2、使用dict()函数
3、使用已有的序列。
以下通过一些实例来说明如何创建字典:

In [1]: d1={'a':10 ,'b':20,'c':10}

In [2]: d1
Out[2]: {'a': 10, 'b': 20, 'c': 10}

In [3]: d2={} ##创建空字典

In [4]: d2
Out[4]: {}

In [5]: item=[('zhang',20),('liu',30)]

In [6]: d3=dict(item) ###使用dict函数创建字典

In [7]: d3
Out[7]: {'liu': 30, 'zhang': 20}

In [8]: item1=['red','blue','yellow']

In [9]: item2=[100,200,300]
In [10]: d4=dict(zip(item1,item2))

In [11]: d4
Out[11]: {'blue': 200, 'red': 100, 'yellow': 300}

3.4.10.2 字典的基本操作
 len(d) 返回字典d中键-值对或项的个数;
 d[k] 返回键k对应的值v;
 d.get(k,default_value) 返回字典d中键k对应的值v,没有对应的键k,将返回缺省值(缺省值可自定义);
 k in d 判断k是否在字典d中
以下通过一些具体实例来说明;

In [13]: d4
Out[13]: {'blue': 200, 'red': 100, 'yellow': 300}

In [14]: len(d4)
Out[14]: 3

In [15]: d4['red']
Out[15]: 100

In [16]: d4['gree'] ##如果没有对应的键,则报错
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
in ()
----> 1 d4['gree']

KeyError: 'gree'

In [17]: d4.get('red')
Out[17]: 100

In [18]: d4.get('gree',0) ##如果没有对应的键,则不报错,返回缺省值
Out[18]: 0
In [34]: 'red'in d4 ##键red是否在字典d4中,在则返回True
Out[34]: True

In [35]: 'gree' not in d4
Out[35]: True

 d[k]=v 把键k对应的值修改为v
 del d[k] 删除键为k所在的项;
 d.pop(k) 删除键为k所在的项;
 d.keys() 返回字典d所有的键;
 d.values() 返回字典d所有的值。
以下通过一些具体实例来说明;

In [19]: d4['yellow']=200 ##把键为yellow的值修改为200

In [20]: d4
Out[20]: {'blue': 200, 'red': 100, 'yellow': 200}

In [21]: del d4['yellow'] ##删除键为yellow的项或一个键-值对

In [22]: d4
Out[22]: {'blue': 200, 'red': 100}

In [23]: d4.pop('blue')
Out[23]: 200

In [24]: d4
Out[24]: {'red': 100}
In [26]: d5
Out[26]: {'blue': 200, 'red': 100, 'yellow': 200}

In [27]: d5.keys()
Out[27]: ['blue', 'yellow', 'red']

In [28]: d5.values()
Out[28]: [200, 200, 100]

3.4.11 集合
集合(set)是由唯一元素组成的无序集,集合的创建方式常用的有两种,set函数或用大括号。

In [30]: set([2,3,8,2,6,1]) ##set有去重的功能
Out[30]: {1, 2, 3, 6, 8}

In [31]: {3,3,3,8,6,6}
Out[31]: {3, 6, 8}

集合的基本操作
a&b a与b的交集
a|b a与b的并集
a-b a与b的差
a.issubset(b) 判断a是否为b的子集
a.issuperset(b) 判断a是否为b的超集
以下为具体实例

In [36]: a={1,2,3,4}

In [37]: b={3,4,5,6,7,9}

In [38]: a&b
Out[38]: {3, 4}

In [39]: a|b
Out[39]: {1, 2, 3, 4, 5, 6, 7, 9}

In [40]: a-b
Out[40]: {1, 2}

In [41]: c=a|b

In [42]: c
Out[42]: {1, 2, 3, 4, 5, 6, 7, 9}

In [43]: a.issubset(c)
Out[43]: True

In [44]: c.issuperset(a)
Out[44]: True

3.5 pandas简介

3.5.1 pandas简介
“Pandas经过几个版本的更新,目前已经成为数据清洗、处理和分析的不二选择。”
前面我们介绍了NumPy,它提供了数据处理功能,但需要写很多命令,是否有更加便捷、直观、有效的方法?Pandas就是为此而诞生的。Pandas提供了众多更高级、更直观的数据处理功能,尤其是它的DataFrame数据结构,将给你全新的体验,可以用处理数据库表或电子表格的方式来处理分析数据。
Pandas基于NumPy构建的,它提供的结构或工具,让以NumPy为中心的数据处理、数据分析变得更加简单和高效。
Pandas中两个最常用的对象是Series和DataFrame。使用pandas前,需导入以下内容:

In [1]: import numpy as np
In [2]: from pandas import Series,DataFrame
In [3]: import pandas as pd

3.5.2 pandas数据结构
Pandas主要采用Series和DataFrame两种数据结构。Series是一种类似一维数据的数据结构,由数据(values)及索引(indexs)组成,而DataFrame是一个表格型的数据结构,它有一组有序列,每列的数据可以为不同类型(NumPy数据组中数据要求为相同类型),它既有行索引,也有列索引。
3.5.3 Series
上章节我们介绍了多维数组(ndarray),当然,它也包括一维数组,Series类似一维数组,为啥还要介绍Series呢?或Series有哪些特点?
Series一个最大特点就是可以使用标签索引,序列及ndarray也有索引,但都是位置索引或整数索引,这种索引有很多局限性,如根据某个有意义标签找对应值?切片时采用类似[2:3]的方法,只能取索引为2这个元素等等,无法精确定位。
Series的标签索引(它位置索引自然保留)使用起来就方便多了,而且定位也更精确,不会产生歧义。举例说明。

In [1]: import numpy as np
In [2]: from pandas import Series,DataFrame
In [3]: import pandas as pd

In [4]: s1=Series([1,3,6,-1,2,8])

In [5]: s1
Out[5]:
0 1
1 3
2 6
3 -1
4 2
5 8
dtype: int64

In [6]: s1.values
Out[6]: array([ 1, 3, 6, -1, 2, 8])

In [7]: s1.index
Out[7]: RangeIndex(start=0, stop=6, step=1)
###创建Series时,自定义索引或称为标签索引
In [8]: s2=Series([1,3,6,-1,2,8],index=['a','c','d','e','b','g'])

In [9]: s2
Out[9]:
a 1
c 3
d 6
e -1
b 2
g 8
dtype: int64

In [10]: s2['a'] ###根据标签索引找对应值
Out[10]: 1
In [11]: s2[['a','e']] ###根据标签索引找对应值
Out[11]:
a 1
e -1
dtype: int64

当然,Series除了标签索引外,还有其它很多优点,如运算的简洁:

In [15]: s2[s2>1]
Out[15]:
c 3
d 6
b 2
g 8
dtype: int64

In [16]: s2*10
Out[16]:
a 10
c 30
d 60
e -10
b 20
g 80
dtype: int64

3.5.4 DataFrame
DataFrame除了索引有位置索引也有标签索引,而且其数据组织方式与MySQL的表极为相似,除了形式相似,很多操作也类似,这就给我们操作DataFrame带来极大方便。这些是DataFrame特色的一小部分,它还有比数据库表更强大的功能,如强大统计、可视化等等。
DataFrame几要素:index、columns、values等,columns就像数据库表的列表,index是索引,当然values就是值了。

In [18]:
####自动生成一个3行4列的DataFrame,并定义其索引(如果不指定,缺省为整数索引)####及列名
d1=DataFrame(np.arange(12).reshape((3,4)),index=['a','b','c'],columns=['a1','a2','a3','a4'])

In [19]: d1
Out[19]:
a1 a2 a3 a4
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11

In [20]: d1.index ##显示索引
Out[20]: Index([u'a', u'b', u'c'], dtype='object')

In [21]: d1.columns ##显示列名
Out[21]: Index([u'a1', u'a2', u'a3', u'a4'], dtype='object')

In [22]: d1.values ##显示值
Out[22]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])

3.5.4.1 生成DataFrame
生成DataFrame有很多,比较常用的有导入等长列表、字典、numpy数组、数据文件等。

In [33]: data={'name':['zhanghua','liuting','gaofei','hedong'],'age':[40,45,50,46],'addr':['jianxi','pudong','beijing','xian']}

In [34]: d2=DataFrame(data)

In [35]: d2
Out[35]:
addr age name
0 jianxi 40 zhanghua
1 pudong 45 liuting
2 beijing 50 gaofei
3 xian 46 hedong

In [36]: d3=DataFrame(data,columns=['name','age','addr'],index=['a','b','c','d'])

In [37]: d3
Out[37]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian

3.5.4.2 获取数据
获取DataFrame结构中数据可以采用obj[]操作、查询、obj.ix[]等命令。

In [8]: data={'name':['zhanghua','liuting','gaofei','hedong'],'age':[40,45,50,46],'addr':['jianxi','pudong','beijing','xian']}
###把字典数据转换为DataFrame,并指定索引
In [9]: d3=DataFrame(data,columns=['name','age','addr'],index=['a','b','c','d'])
In [10]: d3
Out[10]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian

In [11]: d3[['name','age']] ##选择列
Out[11]:
name age
a zhanghua 40
b liuting 45
c gaofei 50
d hedong 46

In [12]: d3['a':'c'] ##选择行
Out[12]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing

In [13]: d3[1:3] ##选择行(利用位置索引)
Out[13]:
name age addr
b liuting 45 pudong
c gaofei 50 beijing
In [14]: d3[d3['age']>40] ###使用过滤条件
Out[14]:
name age addr
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
obj.ix[indexs,[columns]]可以根据列或索引同时进行过滤,具体请看下例:
In [16]: d3.ix[['a','c'],['name','age']]
Out[16]:
name age
a zhanghua 40
c gaofei 50

In [17]: d3.ix['a':'c',['name','age']]
Out[17]:
name age
a zhanghua 40
b liuting 45
c gaofei 50

In [18]: d3.ix[0:3,['name','age']]
Out[18]:
name age
a zhanghua 40
b liuting 45
c gaofei 50

pandas除了可通过ix确定行列位置外,还可以通loc、iloc来定位或查找需要的数据,这三者主要区别可参考如下示例

import pandas as pd    
data = [[1,2,3],[4,5,6]]    
index = [0,1]    
columns=['a','b','c']    
df = pd.DataFrame(data=data, index=index, columns=columns)    
 
#1. loc——通过行标签索引行数据  
df.loc[1]    
'''''''  
a    4  
b    5  
c    6  
'
''    
 
#1.2 loc[‘d’]表示索引的是第’d’行(index 是字符)  
import pandas as pd    
data = [[1,2,3],[4,5,6]]    
index = ['d','e']    
columns=['a','b','c']    
df = pd.DataFrame(data=data, index=index, columns=columns)    
df.loc['d']    
'''''''  
a    1  
b    2  
c    3  
'
''    
 
#1.3 如果想索引列数据,像这样做会报错  
print df.loc['a']    
'''''''  
KeyError: '
the label [a] is not in the [index]'  
'
''    
 
#1.4 loc可以获取多行数据  
print df.loc['d':]    
'''''''  
   a  b  c  
d  1  2  3  
e  4  5  6  
'
''    
 
#1.5 loc扩展——索引某行某列  
 
print df.loc['d',['b','c']]    
'''''''  
b    2  
c    3  
'
''    
 
#1.6 loc扩展——索引某列  
 
print df.loc[:,['c']]    
'''''''  
   c  
d  3  
e  6  
'
''    
 
##当然获取某列数据最直接的方式是df.[列标签],但是当列标签未知时可以通过这种方式##获取列数据。  
 
#需要注意的是,dataframe的索引[1:3]是包含1,2,3的,与平时的不同。  
 
#2. iloc——通过行号获取行数据  
 
#2.1 想要获取哪一行就输入该行数字  
 
df.iloc[1]    
'''''''  
a    4  
b    5  
c    6  
'
''    
 
#2.2 通过行标签索引会报错  
 
print df.iloc['a']    
'''''''  
TypeError: cannot do label indexing on <class '
pandas.core.index.Index'> with these indexers [a] of <type 'str'>  
'
''    
 
#2.3 同样通过行号可以索引多行  
 
df.iloc[0:]    
'''''''  
   a  b  c  
d  1  2  3  
e  4  5  6  
'
''    
 
#2.4 iloc索引列数据  
 
df.iloc[:,[1]]    
'''''''  
   b  
d  2  
e  5  
'
''    
 
#3. ix——结合前两种的混合索引  
 
#3.1 通过行号索引  
 
df.ix[1]    
'''''''  
a    4  
b    5  
c    6  
'
''    
 
#3.2 通过行标签索引  
 
df.ix['e']    
'''''''  
a    4  
b    5  
c    6  
'
''

3.5.4.3 修改数据
我们可以像操作数据库表一样操作DataFrame,删除数据,插入数据、修改字段名、索引名、修改数据等,以下通过一些实例来说明。

In [9]: data={'name':['zhanghua','liuting','gaofei','hedong'],'age':[40,45,50,46],'addr':['jianxi','pudong','beijing','xian']}

In [10]: d3=DataFrame(data,columns=['name','age','addr'],index=['a','b','c','d'])

In [11]: d3
Out[11]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
In [12]: d3.drop('d',axis=0) ###删除行,如果欲删除列,使axis=1即可
Out[12]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
In [13]: d3 ###从副本中删除,原数据没有被删除
Out[13]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
###添加一行,注意需要ignore_index=True,否则会报错
In [14]: d3.append({'name':'wangkuan','age':38,'addr':'henan'},ignore_index=True)
Out[14]:
name age addr
0 zhanghua 40 jianxi
1 liuting 45 pudong
2 gaofei 50 beijing
3 hedong 46 xian
4 wangkuan 38 henan

In [15]: d3 ###原数据未变
Out[15]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
###添加一行,并创建一个新DataFrame
In [16]: d4=d3.append({'name':'wangkuan','age':38,'addr':'henan'},ignore_index=True)
In [17]: d4
Out[17]:
name age addr
0 zhanghua 40 jianxi
1 liuting 45 pudong
2 gaofei 50 beijing
3 hedong 46 xian
4 wangkuan 38 henan

In [18]: d4.index=['a','b','c','d','e'] ###修改d4的索引

In [19]: d4
Out[19]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
e wangkuan 38 henan
In [20]: d4.ix['e','age']=39 ###修改索引为e列名为age的值

In [21]: d4
Out[21]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
e wangkuan 39 henan

3.5.4.4 汇总统计
Pandas有一组常用的统计方法,可以根据不同轴方向进行统计,当然也可按不同的列或行进行统计,非常方便。
常用的统计方法有:


(表7-1 Pandas统计方法)

以下通过实例来说明这些方法的使用

from pandas import DataFrame
import numpy as np
import pandas as pd
inputfile = '/home/hadoop/data/stud_score.csv'
data = pd.read_csv(inputfile)
#其他参数,
###header=None 表示无标题,此时缺省列名为整数;如果设为0,表示第0行为标题
###names,encoding,skiprows等
#读取excel文件,可用read_excel
In [7]: df=DataFrame(data)

In [8]: df.head(3) ###显示前3行
Out[8]:
stud_code sub_code sub_nmae sub_tech sub_score stat_date
0 2.015101e+09 10101.0 数学分析 NaN 90.0 NaN
1 2.015101e+09 10102.0 高等代数 NaN 88.0 NaN
2 2.015101e+09 10103.0 大学物理 NaN 67.0 NaN

In [9]: df.count()
Out[9]:
stud_code 121
sub_code 121
sub_nmae 121
sub_tech 0
sub_score 121
stat_date 0
dtype: int64

In [10]: df['sub_score'].describe() ##汇总学生各科成绩
Out[10]:
count 121.000000
mean 78.561983
std 12.338215
min 48.000000
25% 69.000000
50% 80.000000
75% 89.000000
max 98.000000
Name: sub_score, dtype: float64

In [11]: df['sub_score'].std() ##求学生成绩的标准差
Out[11]: 12.338214729032906

注:DataFrame数据结构的函数或方法有很多,大家可以通过df.[Tab键]方式查看,具体命令的使用方法,如df.count(),可以在Ipython命令行下输入:?df.count() 查看具体使用,退出帮助界面,按q即可。

3.6 函数

3.6.1函数的定义
说到函数大家应该不陌生,函数从初中就开始了,函数的作用我们也大都有所了解,利用函数极大提高了我们工作的效率,一个个函数就像软件行业的一块块芯片,插上接口就可使用。所以函数很好的解决程序的移植性、重用性、复制性、易用性和可靠性等等。
Python的函数同样有这些特性,Python有很多内置函数,如max、min、abs等,甚至还有很多内置机器算法或模型、机器学习库等,如sklearn库中的支持向量机、keras中的神经网络等等,这里就不展开说了,这章主要介绍自定义函数的创建及使用。
 Python函数定义的格式:
def 函数名(参数1,参数2,...):
函数体
return 表达式或变量
【说明】
1、参数可分为位置参数、关键参数或默认参数或可选参数;
2、返回值可以是表达式、变量,甚至没有return语句,此时返回None。
 Python函数的调用:
函数名(参数1,参数2,....)
下面是定义、调用函数的一些实例

In [1]: def myfun(x,y): ###定义函数
...: return x+y
...:

In [2]: myfun(2,6) ###调用函数
Out[2]: 8
In [5]: def myfun01(x,y,z=1): ##定义一个含缺省参数(z)的函数
...: if z==1:
...: return x+y
...: else:
...: return (x+y)*z
...:

In [6]: myfun01(2,6) ###调用函数时,没有输入z参数,则取其缺省值
Out[6]: 8

In [7]: myfun01(2,6,10) ###调用函数时,输入z参数,则取其输入值
Out[7]: 80

3.6.2 函数的返回值
我们在介绍Shell的函数时,发现其局限很多,通过return只能返回[0,255]间的某个整数,如果要返回其它值,需要通过echo的方式,然后再通过运行该函数来获取返回值,大大限制了函数的表现力。Python的函数返回值,是否也是这样呢?
Python的函数强大多了,也方便多了!从输入参数可以看出它不同一般的很多优点,其返回值同样有很多不一样的地方。
不但可以返回多个值,还可有多种形式,举例如下:

In [8]: def myfun02():
...: a=1
...: b=3
...: c=8
...: return a,b,c
...: myfun02()
...:
Out[8]: (1, 3, 8) ###返回多个值,而且很方便

In [9]: x,y,z=myfun02()

In [10]: print x,y,z ###轻松返回多个值,而且轻松获取返回值
1 3 8

3.6.3 变量的作用域
前面我们提到模块及函数,一个模块中可能有很多函数和变量,当然在一个函数中也可能涉及很多变量或嵌套函数等等问题,如此,变量的活动范围(或作用范围)就是一个重要问题。改变变量作用范围的有def、class、lambda等定义函数或类的语句,循环语句不影响变量的作用域。
我们先看一个示例:

#testlocalvar.py
import math
def dist(x,y):
a,b=0,0
d2=(x-a)**2+(y-b)**2
d=math.sqrt(d2)
return d

dist(3,4) ###调用函数
#print(a) ###应用局部变量a将报错

局部变量只是在其所属的函数内部使用,在函数外,不能访问函数的局部变量,函数结束时,其局部变量将被自动删除。
在一个模块中除了函数内部的变量外,一般也有函数之外的变量,这部分变量可以被其他函数访问,但能否被被其他函数修改呢?我们来看一个示例:

#testglobalvar.py
name='python'

def select_tool():
print('select the tool is:'+name)

def select_other(a):
name=a

select_other('java')
def select_tool()

运行结果是:
select the tool is:python
而不是:
select the tool is:java
上例中之所以结果非我们期待的java,究其原因,就是函数select_other中name是一个局部变量,该局部变量,执行函数select_other()立即被删除,如要出现我们期望的结果,该如何设计?
方法之一,可以在select_other函数中声明name为全局变量,如下例:

#testglobalvar.py
name='python'

def select_tool():
print('select the tool is:'+name)

def select_other(a):
global name
name=a

select_other('java')
def select_tool()

运行结果是:
select the tool is:java
3.6.4 函数也是对象
在Python中,有一句话非常有代表性:“万物皆对象”,一个字符是对象,一个数字也是对象,更不用说数据结构、模块、函数了,何以见得?既然是对象,那应该就有属性或方法,难道一个字符也有属性和方法?下面看几个实例。

In [18]: a='b'

In [19]: a. ###a.然后按Tab键,就可以看到a具有的方法或属性
a.capitalize a.endswith a.isalnum a.istitle a.lstrip a.rjust a.splitlines a.translate
a.center a.expandtabs a.isalpha a.isupper a.partition a.rpartition a.startswith a.upper
a.count a.find a.isdigit a.join a.replace a.rsplit a.strip a.zfill
a.decode a.format a.islower a.ljust a.rfind a.rstrip a.swapcase
a.encode a.index a.isspace a.lower a.rindex a.split a.title

In [19]: b=2

In [20]: b. ###b.然后按Tab键,就可以看到b具有的方法或属性
b.bit_length b.conjugate b.denominator b.imag b.numerator b.real

这是Python非常神奇、非常强大的特点之一。后续大家将看到更多有趣的地方。
万物皆对象有啥好处?好处很多,第一,保证了python对象的一致性;第二,增强python的灵活性;第三,提供了使用的方便性(如通过.Tab就可方便查看对象的属性和方法等),等等。下面介绍一个实际应用,既然函数也是对象,当然就可以把它作为其它对象来对待,如字符或数字或参数之类。函数可以作为一个参数,传给另一个函数;函数也可作为函数的返回值。

In [11]: def sum1(x,y):
....: return x+y
....: def max1(a,f): ##f就是一个函数参数
....: return max(a,f) ##函数max作为返回值
....: max1(10,sum1(20,30))
....:
Out[11]: 50

它的好处,现在你可能感觉不到,不过scala已经在大面积使用这一方法。

3.7 数据可视化

无论是大数据、还是小数据、也不管通过统计还是挖掘或机器学习,人们最终想看到的数据,越直观越好,所以这个就涉及到一个数据的可视化问题,而python或pandas的数据可视化功能很强大,可画的种类多,也非常便捷,这是一般数据库软件和开发工具目前所欠缺的。以下我们通过两个实例来说明利用python的matplotlib或pandas实现数据的可视化。
下例利用matplotlib实现数据的可视化

In [1]: %paste
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif']=['SimHei'] ###显示中文
plt.rcParams['axes.unicode_minus']=False ##防止坐标轴上的-号变为方块
x = np.linspace(0, 10, 100)
y = np.sin(x)
y1 = np.cos(x)
##绘制一个图,长为10,宽为6(默认值是每个单位80像素)
plt.figure(figsize=(10,6))
###在图列中自动显示$间内容
plt.plot(x,y,label="$sin(x)$",color="red",linewidth=2)
plt.plot(x,y1,"b--",label="$cos(x^2)$") ###b(blue),--线形
plt.xlabel(u"X值") ##X坐标名称,u表示unicode编码
plt.ylabel(u"Y值")
plt.title(u"三角函数图像") ##t图名称
plt.ylim(-1.2,1.2) ##y上的max、min值
plt.legend() ##显示图例
plt.savefig('fig01.png') ##保持到当前目录
plt.show()

以下是运行结果:


(图 9-2 matplotlib数据可视化)

3.8 数据地图显示

下例通过matplotlib及mpl_toolkits.basemap实现地图数据的可视化。

# -*- coding: utf-8 -*-

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.sans-serif']=['SimHei']
#============================================# read data
names = []
pops = []
lats = []
lons = []
countries = []
for line in file("/home/hadoop/data/bigdata_map/china_city_jobs_stat.csv"):
info = line.split(',')
names.append(info[0])
pops.append(float(info[1]))
lat = float(info[2][:-1])
if info[2][-1] == 'S': lat = -lat
lats.append(lat)
lon = float(info[3][:-1])
if info[3][-1] == 'W': lon = -lon + 360.0
lons.append(lon)
country = info[4]
countries.append(country)

#============================================
#lat0 = 35;lon0 = 120;change = 25;
lat0 = 30;lon0 = 120;change = 26;
lllat=lat0-change; urlat=lat0+change;
lllon=lon0-change; urlon=lon0+change;

map = Basemap(ax=None,projection='stere',lon_0=(urlon + lllon) / 2,lat_0=(urlat + lllat) / 2,llcrnrlat=lllat, urcrnrlat=urlat,llcrnrlon=lllon,urcrnrlon=urlon,resolution='f')
# draw coastlines, country boundaries, fill continents.
map.drawcoastlines(linewidth=0.25)
map.drawcountries(linewidth=0.25)
# draw the edge of the map projection region (the projection limb)
map.drawmapboundary(fill_color='#689CD2')
# draw lat/lon grid lines every 30 degrees.
#map.drawmeridians(np.arange(0,360,30))
#map.drawparallels(np.arange(-90,90,30))
# Fill continent wit a different color
map.fillcontinents(color='green',lake_color='#689CD2',zorder=0)
# compute native map projection coordinates of lat/lon grid.
shapefilepath = '/home/hadoop/data/bigdata_map/map/map'
map.readshapefile(shapefilepath,'city') #添加街道数据
x, y = map(lons, lats)

max_pop = max(pops)
# Plot each city in a loop.
# Set some parameters
size_factor = 80.0
y_offset = 15.0
rotation = 30
for i,j,k,name in zip(x,y,pops,names):
size = size_factor*k/max_pop
cs = map.scatter(i,j,s=size,marker='o',color='yellow')
plt.text(i,j+y_offset,name,rotation=rotation,fontsize=1)

plt.title(u'中国大数据主要城市需求分布图(2017-03-17)')
plt.show()

运行结果如下图:


(图9-3 中国大数据需求分别图)

3.9 图像处理

3.9.1PIL常用操作
这节我们将介绍如何Python库PIL(Python Imaging Library,图像处理类库)提供的图像处理功能,以及大量有用的基本图像操作,比如图像缩放、裁剪、旋转、颜色转换等。
利用 PIL 中的函数,我们可以从大多数图像格式的文件中读取数据,然后写入最常见的图像格式文件中。PIL 中最重要的模块为 Image。要读取一幅图像,可以使用:

import PIL
from PIL importImage
im = Image.open('timg.png')
im.show() ###显示图片

【说明】如果ubuntu系统无法显示图像,看是否已安装imagemagick,如果没有,通过sudo apt-get install imagemagick来安装。
上述代码的返回值 im 是一个 PIL 图像对象
图像的颜色转换可以使用 convert() 方法来实现。要读取一幅图像,并将其转换成灰度图像,只需要加上 convert('L'),如下所示:

from PIL importImage
im1=Image.open('timg.png').convert('L')
im1.show()

使用 PIL 可以很方便地创建图像的缩略图。thumbnail() 方法接受一个元组参数(该参数指定生成缩略图的大小),然后将图像转换成符合元组参数指定大小的缩略图。例如,创建最长边为 128 像素的缩略图,可以使用下列命令:
im.format, im.size, im.mode ##查看图像信息
('JPEG', (128, 128), 'RGB')
im.thumbnail((128,128)) ###设置目前大小
im.save("tenna_bak.gif","GIF") ###保存图片,并转换格式
如何图片剪辑?我们可以通过crop方法,先设定剪辑范围,然后使用crop方法即可:

box=(100,100,130,130) ##设置图片剪辑区
region=im.crop(box) ### region是一个新的图像对象

如何选择图像?我们可以通过
box=(100,100,130,130) ##设置图片剪辑区
如何旋转一个图片?我们可以通过函数rotate

im3=im.rotate(45)
im3.show()

如何实现图像的模糊处理?可以使用ImageFilter方法,具体请看如下代码:

from PIL import ImageFilter
im4 = im.filter(ImageFilter.BLUR) ##模糊滤波,处理之后的图像会整体变得模糊
im4.show()

3.9.2随机生成验证码
PIL的ImageDraw提供了一系列绘图方法,以下利用该方法,随机生成字母验证码图

from PIL import Image,ImageDraw, ImageFont, ImageFilter

import random

# 随机字母:
def rndChar():
return chr(random.randint(65, 90))

# 随机颜色1:
def rndColor():
return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))

# 随机颜色2:
def rndColor2():
return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))

# 240 x 60:
width = 60 * 4
height = 60
image = Image.new('RGB', (width, height), (255, 255, 255))
# 创建Font对象:
font1= ImageFont.truetype('/home/hadoop/anaconda2/lib/python2.7/site-packages/matplotlib/mpl-data/fonts/ttf/Arial.ttf', 36)
# 创建Draw对象:
draw = ImageDraw.Draw(image)
# 填充每个像素:
for x in range(width):
for y in range(height):
draw.point((x, y), fill=rndColor())
# 输出文字:
for t in range(4):
draw.text((60 * t + 10, 10), rndChar(), font=font1, fill=rndColor2())
# 模糊:
image = image.filter(ImageFilter.BLUR)
#显示图形
image.show()

3.9.3图像数组表示
NumPy是非常有名的 Python 科学计算工具包,其中包含了大量有用的思想,比如数组对象(用来表示向量、矩阵、图像等)以及线性代数函数。NumPy 中的数组对象几乎贯穿用于本书的所有例子中 1 数组对象可以帮助你实现数组中重要的操作,比如矩阵乘积、转置、解方程系统、向量乘积和归一化,这为图像变形、对变化进行建模、图分类、图像聚类等提供了基础。PyLab 实际上包含 NumPy 的一些内容,如数组类型。
先前的例子中,当载入图像时,我们通过调用 array() 方法将图像转换成 NumPy 的数组对象,但当时并没有进行详细介绍。NumPy 中的数组对象是多维的,可以用来表示向量、矩阵和图像。一个数组对象很像一个列表(或者是列表的列表),但是数组中所有的元素必须具有相同的数据类型。除非创建数组对象时指定数据类型,否则数据类型会按照数据的类型自动确定。
对于图像数据,下面的例子阐述了这一点:

im = array(Image.open('empire.jpg'))
print im.shape, im.dtype

结果显示如下:

(220, 142, 3) uint8
im = array(Image.open('empire.jpg').convert('L'),'f')
print im.shape, im.dtype

结果显示如下:
(220, 142) float32
每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。关于更多数据类型选项,可以参考图书 [24]。注意,由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。
数组中的元素可以使用下标访问。位于坐标 i、j,以及颜色通道 k 的像素值可以像下面这样访问:
value = im[i,j,k]
多个数组元素可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问该数组的元素值。下面是有关灰度图像的一些例子:

im[i,:] = im[j,:] # 将第 j 行的数值赋值给第 i 行
im[:,i] = 100 # 将第 i 列的所有数值设为100
im[:100,:50].sum() # 计算前100 行、前 50 列所有数值的和
im[50:100,50:100] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)
im[i].mean() # 第 i 行所有数值的平均值
im[:,-1] # 最后一列
im[-2,:] (or im[-2]) # 倒数第二行

注意,示例仅仅使用一个下标访问数组。如果仅使用一个下标,则该下标为行下标。注意,在最后几个例子中,负数切片表示从最后一个元素逆向计数。我们将会频繁地使用切片技术访问像素值,这也是一个很重要的思想。

3.10 操作MySQL数据库

人工智能最重要的动力就是数据,没有充分数据的人工智能是毫无意义的,数据越大人工智能将越聪明,所以数据是非常重要的。因此整合各种数据的能力也是各种开发工具非常重视的,Python在这方面功能很强大。这一章我们将介绍如何利用Python存取数据库中的数据。
目前企业数据大都存储在数据库中,如MySQL、Oracle、DB2等关系型数据库,这些数据库目前使用非常广泛,由于其独特优势,未来还将大量使用;但随着数据量、数据种类的不断增长,目前非关系型数据库也越来越普及了,如MongoDB、Redis、HBase等等,这些数据库有时又称为NoSQL型数据库。所有这些数据是目前大数据的主要源头,因此,如何使用抽取、整合、处理及分析这些数据是非常重要。
这章主要内容:
 操作MySQL

Python使用MySQL非常简单,先导入python有关mysql的驱动模块,然后建立与数据库的连接即可。
以下通过实例来说明。

In [2]: import numpy as np
In [3]: import pandas as pd
In [4]: from pandas import DataFrame
In [5]: import MySQLdb

In [6]: conn= MySQLdb.connect(host='slave02',port=3306,user='feigu', passwd='feigu', db='testdb',charset='utf8')

In [7]: data= pd.read_sql('select * from stud_score', conn)
In [8]: df=DataFrame(data)

In [9]: df.count()

3.11 初学者最易犯的一些错误

Python 以其简单易懂的语法格式与其它语言形成鲜明对比,初学者遇到最多的问题就是不按照 Python 的规则来写,即便是有编程经验的程序员,也容易按照固有的思维和语法格式来写 Python 代码,以下小结一下初学者最易犯的几个错误,供大家参考。
注:以上代码都是基于 Python3 的,在 Python2 中即使是同样的代码出现的错误也不尽一样。
1、忘记冒号:
在 if、elif、else、for、while、class、def 语句后面忘记添加 “:”

spam=26
if spam > 20
print('Hello!')

报错:SyntaxError: invalid syntax
订正:

spam=26
if spam > 20:
print('Hello!')

2、误用 “=” 做等值比较
“=” 是赋值操作,而判断两个值是否相等是 “==”

if spam = 42:
print('Hello!')

报错:SyntaxError: invalid syntax

3、使用错误的缩进
Python用缩进区分代码块,常见的错误用法:

print('Hello!')
print('Happy new year!')

报错:IndentationError: unexpected indent
说明:同一个代码块中的每行代码都必须保持一致的缩进量

if spam == 42:
print('Hello!')
print('Howdy!')

报错:IndentationError: expected an indented block
说明:“:” 后面要使用缩进
4、变量没有定义

if age == 42:
print('Hello!')

报错:NameError: name 'age' is not defined

5、获取列表元素索引位置忘记调用 len 方法
通过索引位置获取元素的时候,忘记使用 len 函数获取列表的长度。

spam = ['cat', 'dog', 'mouse']
for i in range(spam):
print(spam[i])

报错:TypeError: 'list' object cannot be interpreted as an integer
订正:

spam = ['cat', 'dog', 'mouse']
for i in range(len(spam)):
print(spam[i])
cat
dog
mouse

可用更符合python风格的写法是用 enumerate

spam = ['cat', 'dog', 'mouse']
for i, item in enumerate(spam):
print(i, item)
0 cat
1 dog
2 mouse

6、修改字符串
字符串一个序列对象,支持用索引获取元素,但它和列表对象不同,字符串是不可变对象,不支持修改。

spam = 'I have a pet cat.'
spam[5] = 'r'
print(spam)

报错: in ()
1 spam = 'I have a pet cat.'
----> 2 spam[5] = 'r'
3 print(spam)

TypeError: 'str' object does not support item assignment
订正:

spam = 'I have a pet cat.'
spam = spam[:13] + 'r' + spam[14:]
print(spam)
I have a pet rat.

7、字符串与非字符串连接

num_eggs = 12
print('I have ' + num_eggs + ' eggs.')

报错:TypeError: must be str, not int
说明:字符串与非字符串连接时,必须把非字符串对象强制转换为字符串类型。

num_eggs = 12
print('I have ' + str(num_eggs) + ' eggs.')
I have 12 eggs.

或者使用字符串的格式化形式

num_eggs = 12
print('I have %s eggs.' % (num_eggs))
I have 12 eggs.

8、使用错误的索引位置

spam = ['cat', 'dog', 'mouse']
print(spam[3])

报错:
使用错误的索引位置1 spam = ['cat', 'dog', 'mouse']
----> 2 print(spam[3])
IndexError: list index out of range

说明:列表对象的索引是从0开始的,第3个元素应该是使用 spam[2] 访问。

9、字典中使用不存在的键

spam = {'cat': 'Zophie', 'dog': 'Basil', 'mouse': 'Whiskers'}
print('The name of my pet zebra is ' + spam['zebra'])

报错:KeyError: 'zebra'
说明:在字典对象中访问 key 可以使用 [],但是如果该 key 不存在,就会报错。
订正:正确的方式应该使用 get 方法,如果key 不存在,get 默认返回 None。

spam = {'cat': 'Zophie', 'dog': 'Basil', 'mouse': 'Whiskers'}
spam.get('zebra')

10、用关键字做变量名

class = 'algebra'

报错:SyntaxError: invalid syntax
说明:在 Python 中不允许使用关键字作为变量名。Python3 一共有33个关键字。关键字有:

import keyword
print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

11、函数中局部变量赋值前被使用

someVar = 42

def myFunction():
print(someVar)
someVar = 100

myFunction()

报错:UnboundLocalError: local variable 'someVar' referenced before assignment
说明:当函数中有一个与全局作用域中同名的变量时,它会按照 LEGB 的顺序查找该变量,如果在函数内部的局部作用域中也定义了一个同名的变量,那么就不再到外部作用域查找了。因此,在 myFunction 函数中 someVar 被定义了,所以 print(someVar) 就不再外面查找了,但是 print 的时候该变量还没赋值,所以出现了 UnboundLocalError

12、使用自增 “++” 自减 “--”

spam = 0
spam++

报错:SyntaxError: invalid syntax
说明:Python 中没有自增自减操作符,如果你是从C、Java转过来的话,你可要注意了。你可以使用 “+=” 来替代 “++”

spam = 0
spam += 1
print(spam)
1

13、错误地调用类中的方法

class Foo:
def method1():
print('m1')
def method2(self):
print("m2")

a = Foo()
a.method1()

报错:TypeError: method1() takes 0 positional arguments but 1 was given
说明:method1 是 Foo 类的一个成员方法,该方法不接受任何参数,调用 a.method1() 相当于调用 Foo.method1(a),但 method1 不接受任何参数,所以报错了。正确的调用方式应该是 Foo.method1()。

3.12 Python2.x与Python3.x版本的区别

3.12.1简介
Python3发布于2008年底,是一次重大的Python升级。Python3的有些改进不向Python2兼容,因此,Python2始终与Python3并行向前发展,不过,据Python官方说明,Python2.x将在2020年后停止维护,以后将以Python3.x为主。
3.12.2具体说明

print函数
在Python3中,print是函数式,而在Python2中,print是个语言结构,与if和for一样。以下几种情况,在Python2.7中是等价的。

print "ok"
print ("ok") #注意print后面有个空格
print("ok") #print()不能带有任何其它参数

对Python2.x中print函数,可以通过导入print_function ,即from __future__ import print_function,可以使Python2中print兼容Python3,如下例,在Python2中可运行如下语句:

from __future__ import print_function
print("fish", "panda", sep=',')

Unicode
Python 2 有 ASCII str() 类型,unicode() 是单独的,不是 byte 类型。
现在, 在 Python 3,我们最终有了 Unicode (utf-8) 字符串,以及一个字节类:byte 和 bytearrays。
由于 Python3.X 源码文件默认使用utf-8编码,这就使得以下代码是合法的。

中国='china'
print(中国)

在Python2.x中,需要添加u

a1="我喜欢Python"
a1
'\xe6\x88\x91\xe5\x96\x9c\xe6\xac\xa2Python'

a2=u"我喜欢Python"
a2
u'\u6211\u559c\u6b22Python'

在Python3.x

a1="我喜欢Python"
a1
'我喜欢Python'

除法运算
Python中的除法较其它语言显得非常高端,有套很复杂的规则。Python中的除法有两个运算符,/和//
首先来说/除法:
在python 2.x中/除法就跟我们熟悉的大多数语言,比如Java啊C啊差不多,整数相除的结果是一个整数,把小数部分完全忽略掉,浮点数除法会保留小数点的部分得到一个浮点数的结果。
在python 3.x中/除法不再这么做了,对于整数之间的相除,结果也会是浮点数。
在Python2.x中

1/2
结果为
0
1.0/2.0
结果为:
0.5

xrange
在 Python 2 中 xrange() 创建迭代对象的用法是非常流行的。比如: for 循环或者是列表/集合/字典推导式。
这个表现十分像生成器(比如。"惰性求值")。但是这个 xrange-iterable 是无穷的,意味着你可以无限遍历。
由于它的惰性求值,如果你不得仅仅不遍历它一次,xrange() 函数 比 range() 更快(比如 for 循环)。尽管如此,对比迭代一次,不建议你重复迭代多次,因为生成器每次都从头开始。
在 Python 3 中,range() 是像 xrange() 那样实现以至于一个专门的 xrange() 函数都不再存在(在 Python 3 中 xrange() 会抛出命名异常)。
不等运算符
Python 2.x中不等于有两种写法 != 和 <>
Python 3.x中去掉了<>, 只有!=一种写法,还好,我从来没有使用<>的习惯。
数据类型
1)Py3.X去除了long类型,现在只有一种整型——int,但它的行为就像2.X版本的long
2)新增了bytes类型,对应于2.X版本的八位串,定义一个bytes字面量的方法如下:

b = b'china'
type(b)
str

str对象和unicode对象可以使用.encode() (str -> unicode) or .decode() (unicode -> str)方法相互转化。

3.13 练习

一、编写程序计算1+2+3+....+100的结果。
二、对字符串'12345678',将逆序输出。
三、创建一个10*10的随机数组并查找最大最小值

第2章 MySQL入门

2.1 MySQL简介

2.1.1数据库泛述
为什么要学习数据库?
这个问题我觉得还是从反面来回答比较好,数据库出故障了,会发生啥呢?
学数据库与我找工作或找到更好工作有关系吗?有,关系还很大哦。当然,如果你去应聘不用电脑的职业除外。否则,很可能产生“一丑遮百俊”。
学了数据库有哪些好处?
其他好处不好说,但如果你学了熟悉数据库,对学习其他技术有非常大得帮助,尤其对学习大数据相关技术如Hive、HBase、SparkSQL、SparkRDD等等更是如此,数据库很多都是相通或相似的,学好一个学其他的就轻松多了。
有哪些数据库?
数据库种类很多,从大得方面来说,可分为关系型数据库和非关系型数据库,如MySQL、SQL Server、Oracle、DB2、Sybase等属于关系型数据库,近些年比较火的HBase、MongoDB、Redis等属于非关系型数据库,从存储方式方面来说,可分为行存储数据库、列存储数据库、键值数据库、NoSQL数据库等,当然各类关系型数据库或非关系型数据库自身都各有一些特点。这里就不展开说了。
如何学习数据库?
这个问题有点仁者见仁智者见智,一百人可能有一百个答案,不过我个人认为,数据库作为一个基础性非常强、使用非常广泛的系统,多花些时间进行一些有系统的学习是非常必要,好的基础将大大提升你的竞争力、拓展你的职业发展空间。
2.1.2 MySQL特点
为何选择MySQL? 它有哪些特点?
MySQL是由原MySQL AB公司自主研发的,现在已经被Sun公司收购,是目前IT行业最流行的开放源代码的数据库管理系统,同时它也是一个支持多线程高并发多用户的关系型数据库管理系统。支持Linux、Windows、MAC等多种操作系统,虽然功能上没有其他的大型数据库Oracle、DB2等那么齐全,但好用、易用、开源、可靠性等特点受到成千上万企业和用户的青睐重要原因。
向大家推荐几个学习MySQL的网站:
MySQL社区:http://www.mysqlpub.com/
MySQL菜鸟教程: http://www.runoob.com/mysql/mysql-tutorial.html
MySQL官网:http://www.mysql.com/
《深入浅出MySQL》唐汉名等著
2.1.3 数据库基础
数据库是一个长期存储在计算机内的、有组织、共享、统一的数据集合。作为关系型数据其关系可理解为数据库表,表是一系列二维数组的集合。
 表定义:表(Table),在关系型数据库中,表是一系列二维数组的集合,由纵向的列和横向的行组成,列又称为字段,行又称为记录。
表样例:stud_info(学生基本信息表)

 表要素有:
关系或表、列,字段行,记录
 数据类型:
字符串数据类型、整数型、日期/时间类型、浮点数据型等
 关键字,主键:
主键(Primary key),唯一标志表的每一条记录,可以定义表中一列或多列为主键, 主键列上不能有重复的值,也不能为空,如stud_info表中代码字段为该表的主键。
关系模式或表结构,格式为:表名称(属性1,属性2,…属性n)
2.1.4 数据库语言
我们一般通过数据库语言与数据库打交道,其中SQL是我们常用的,SQL的含义是结构化查询语言(Structured Query Language)。
SQL语言分类
 数据定义语言(DDL)
如:DROP,CREATE,ALTER等
 数据操作语言(DML)
如:INSERT,UPDATE,DELETE等
 数据查询语言(DQL)
SELECT等
 数据控制语言(DCL)
GRANT,REVOKE,COMMIT,ROLLBAK等。
为进一步理解SQL语句含义,下面以创建一张表的SQL语句为例,表名为t_student,具体SQL语句如下:

CREATE TABLE t_student
(
stud_id INT NOT NULL,
stud_name VARCHAR(40) NOT NULL,
stud_sex CHAR(1),
stud_birthday DATE,
PRIMARY KEY (stud_id)
);

这是一个典型的数据定义语言(DDL),该表共有4个字段,分别为stud_id,stud_name,stud_sex,stud_birthday,其中stud_id为主键。
目前这个表是空表,只有结构没有数据,我们可以用数据操作语句(DML)往表插入记录或数据。

INSERT INTO t_student(stud_id,stud_name,stud_sex,stud_birthday)
VALUES(1001001,'刘芳','F','1995-06-19');

以上记录是否成功插入到表中呢?我们可以通过数据查询语言(DQL)来验证一下:

select * from t_student;
+---------+-----------+----------+---------------+
| stud_id | stud_name | stud_sex | stud_birthday |
+---------+-----------+----------+---------------+
| 1001001 | 刘芳 | F | 1995-06-19

2.1.5 数据库系统架构


(图1-1 数据库系统架构)

2.1.6 Windows平台下安装配置
MySQL支持多平台,如常见的windows,linux等,这里我们以Windows下安装为主,然后简单说明在Linux平台上的安装。
这里介绍一个针对初学者的Windows版的MySQL安装程序(mysqlSetup.exe,文件下载链接:http://pan.baidu.com/s/1kVHK3eZ),文件大小为35M左右,版本为V5.0,依赖较少,安装方便,但基础功能都有。
以下为详细的安装步骤:
第1步:点击“mysqlSetup.exe”文件,弹出如下界面

第2步:选择安装类型,选择typical(典型安装)。

第3步:是否注册,选择skip sign-up(不注册)

第4步:开始配置服务器

第5步:在配置类型界面中,选择Detailed configuration(详细配置)

第6步:在服务器类型中,作为初学者,可以选择Developer Machine(作为开发机),占用系统资源较少,但基本功能都有。

第7步:字符集选择界面,第一个为西文编码,第二个是多字节的通用utf8编码,都不是我们通用的编码,这里选择第三个,然后在Character Set那里选择或填入“gbk”,当然也可以用“gb2312”,区别就是gbk的字库容量大,包括了gb2312的所有汉字。

第8步:在数据库用途界面,选择Mutifunctional Database(多功能数据库)。

第9步:进入服务器最多并发连接数界面,作为初学者,不涉及很复杂的业务逻辑,而且并发数也需要很多,可选择第一项OLAP或OLTP,并发连接数(concurrent connection)缺省为15,当然你也可以根据需要进行修改。

第10步:配置网络,使用缺省配置即可。

第11步:在Windows设置界面,记得在勾上“Inclode bin Directory in windows PATH”,这样自动把mysql命令所在目录放在Windows的环境变量Path中,接下来你便可在任何目录下启动mysql。


第12步,确认root用户密码

第13步,显示将执行的内容(无需选择),点击“Execute”

第14步安装结束

第15步登录MyQL系统。
 通过Windows命令行登录
第1步,从开始菜单选择“运行”,打开运行对话框,输入‘cmd’

第2步,按确定,打开DOS窗口。

第3步,在DOS窗口中,可以通过输入命令登录MySQL系统,命令格式为:
mysql -h hostname -u username -p
其中mysql为登录命令,
-h 后的参数为服务器名称或IP地址,如果为本地服务器,可为localhost 或127.0.0.1, -u后的参数为用户名称,还没有创建其他用户时,只有root用户。
-p 后的参数为用户密码。

第4步,退出mysql系统,只要在mysql>后,输入quit 然后回车即可。
 通过MySQL命令行登录
选择“开始”菜单,点击含“MySQL Command Line Client”字样的图标,进入登录界面,然后输入root用户密码即可登录。

这是在登录windows下MySQL系统的几种方式,下章我们将介绍如何登录远程服务器上的MySQL系统。

2.2 维护表结构

在关系型数据库中,表是是数据库中最重要、最基本的操作对象,是数据存储的基本单位,数据是按行存储的(非关系型数据库有些是按列存储的),同时通过表中的主键、外键、索引、非空等约束来保证数据的一致性和完整性。这一章主要介绍数据表的基本操作:如何创建表、查看表的结构、修改数据表、删除数据表等。
创建表的方式,可以命令行的方式,也可以通过客户端(navicat for mysql)或通过该客户端的建模方式创建,然后,把模型同步到数据库即可。
2.2.1 创建表
创建表的过程就是确定数据列的属性、制定数据完整性、一致性等约束的过程。而这些约束主要通过主键、外键、是否可空、索引等约束来实现的。
创建表的语法形式:
CREATE TABLE 表名称
(
字段名称1 数据类型 [列级的约束] [默认值],
字段名称2 数据类型 [列级的约束] [默认值],
--------
[表级的约束]
);
注:
(1)、表的名称不区分大小写,不能以SQL中的一些关键字为表名,如CREATE、ALTER、INSERT等;
(2)、列名间用逗号隔开。
下面以创建一个学生的基本信息表为例,说明如何创建一张表。
表的定义或结构如下:
学生信息(t_stud_info)表结构

注:
(1)、stud_code这个字段为主键,作为主键(PRIMARY KEY)的字段不能为空,为空的字段不能为主键,主键可以建的一个字段上,也可建的两个或两个以上的字段上,称为组合主键;
(2)、非空字段,是指这些字段的值,不能为空(即为null)。
创建表的SQL语句:
CREATE TABLE t_stud_info
(
stud_code varchar(20) NOT NULL,
stud_name varchar(100) NOT NULL,
stud_gend varchar(10) ,#没有说明NULL,说明是NULL
college_name varchar(300) NULL,
PRIMARY KEY (stud_code) #指明构成主键的字段
);#最后是分号
创建表的SQL以写好,如何执行,如何查看表结构?通过实例来说明:
 直接把创建表的SQL语句放在mysql命令行执行:

mysql> use testdb; ###把表创建在该数据库上
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> CREATE TABLE t_stud_info
-> (
-> stud_code varchar(20) NOT NULL,
-> stud_name varchar(100) NOT NULL,
-> stud_gend varchar(10) ,
-> college_name varchar(300) NULL,
-> PRIMARY KEY (stud_code)
-> );
Query OK, 0 rows affected (0.31 sec)
mysql> show tables; ###查看已创建的表
+------------------+
| Tables_in_testdb |
+------------------+
| stud_info |
| stud_info_copy |
| stud_score |
| stud_score_view |
| t_stud_info | ###表创建成功
+------------------+
5 rows in set (0.00 sec)

mysql> desc t_stud_info; ###查看表结构
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| stud_code | varchar(20) | NO | PRI | NULL | |
| stud_name | varchar(100) | NO | | NULL | |
| stud_gend | varchar(10) | YES | | NULL | |
| college_name | varchar(300) | YES | | NULL | |
+--------------+--------------+------+-----+---------+-------+
4 rows in set (0.17 sec)
mysql> show create table t_stud_info \G ###查看表的详细信息
*************************** 1. row ***************************
Table: t_stud_info
Create Table: CREATE TABLE <code>t_stud_info</code> (
<code>stud_code</code> varchar(20) NOT NULL,
<code>stud_name</code> varchar(100) NOT NULL,
<code>stud_gend</code> varchar(10) DEFAULT NULL,
<code>college_name</code> varchar(300) DEFAULT NULL,
PRIMARY KEY (<code>stud_code</code>)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ###表的存储引擎、字符集。

 通过执行脚本创建表
如果一次要创建几十张表,把创建表的语句直接放在mysql命令行运行就不方便了,此时我们可以把创建这些表的SQL语句保存本地当前目录下,名为t_stud_info.sql的文件中,,具体内容请参看以下system cat t_stud_info.sql部分,然后用source命令在mysql命令下执行这个文件即可。这个source命令有点像shell命令。

mysql>system cat t_stud_info.sql; ###查看sql文件内容
DROP TABLE IF EXISTS t_stud_info;
CREATE TABLE t_stud_info
(
stud_code varchar(20) NOT NULL,
stud_name varchar(100) NOT NULL,
stud_gend varchar(10) ,
college_name varchar(300) NULL,
PRIMARY KEY (stud_code)
);
mysql> source t_stud_info.sql;
Query OK, 0 rows affected (0.03 sec)
mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| stud_info |
| stud_score |
| t_stud_info | ###表创建成功
+------------------+

除了以上两种方法外,还有其他方法,如通过客户端创建、通过模型来创建等等,这里就不展开来说了。
2.2.2 修改表结构
表创建好以后,有时间我们可能根据新的需求,需要修改字段类型、字段名称、添加字段、新建其它约束如索引、是否可空等。当表中无数据时做这些修改比较方便,如果表已有数据可能就需要慎重,否则可能导致修改失败,此时建议备份原表数据,然后清空数据,再做修改,修改后根据新的规则把数据导入新表中。但添加字段、放大自动长度等与是否有数据无关。
2,2.2.1 修改字段类型
如果发现创建的表的某个字段长度太小,需要放大其长度,该如何修改呢?我们可以使用ALTER TABLE 语句来修改。
【注意】如果是正式环境的数据,记得先备份,后修改,有备无患。
修改表字段类型的语法格式:
ALTER TABLE <表名> MODIFY <字段名><字符类型>;
我们创建一张表test01 (a1 varchar(20),a2 int,a3 date),然后,修改a1字段的数据类型,由varchar(20)改为varchar(60)。具体操作如下:

mysql> CREATE TABLE test01 ###创建表test01
-> (
-> a1 varchar(20),
-> a2 int,
-> a3 date
-> );
Query OK, 0 rows affected (0.07 sec)

mysql> DESC test01; ###查看表结构
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a1 | varchar(20) | YES | | NULL | |
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)

mysql> ALTER TABLE test01 MODIFY a1 varchar(60); ###修改字段a1类型
Query OK, 0 rows affected (0.24 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> DESC test01;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a1 | varchar(60) | YES | | NULL | | ###修改成功
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

大家考虑一下,是否可以把INT修改为字符型,或把字符型修改整数型?如果要修改需要满足哪些条件?
除了可以修改字段类型,我们还可以修改表名称、字段名称、字段属性等。
2.2.2.2 修改字段名称
修改字段名称的语句与修改字段类型的不一样,其语法格式为:
ALTER TABLE <表名> CHANGE <旧字段名><新字段名><数据类型>;
现在我们把test01表中a1字段名称改为name,数据类型不变。

mysql> desc test01; ###查看表结构
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a1 | varchar(60) | YES | | NULL | |
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
mysql> ALTER TABLE test01 CHANGE a1 name varchar(60); ###把字段a1改为name
Query OK, 0 rows affected (0.38 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> desc test01;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name | varchar(60) | YES | | NULL | | ###修改成功
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

2.2.2.3 新增字段
表创建后,根据需要我们可以修改表名、修改表中字段名称、字段类型,当然也可添加字段,而且可以更加指定位置的添加,如果没有指定,缺省是添加到最后。添加字段的语法格式为:
ALTER TABLE <表名> ADD <新增字段名><字段类型> [字段约束条件][FIRST|AFTER 已有字段名];
我们还是以test01表为例,在name字段后,添加一个名为code的字段,数据类型为varchar(20),并且不能为空或not null。

mysql> ALTER TABLE test01 ADD code varchar(20) not null AFTER name;
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> desc test01;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name | varchar(60) | YES | | NULL | |
| code | varchar(20) | NO | | NULL | |
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

2.2.3 修改表的字符集
表的字符集或缺省字符集也能修改?能,这应该也是MySQL特色之一吧,一般数据库系统字符集与数据库绑定在一起,但MySQL把字符集粒度精确到了表甚至字段。虽然这个功能很强大,但也存在很大风险,特别是表中有数据时,可能导致字符类型不兼容问题,为降低风险,还是这句老话,先备份,后修改。
这里的修改一般指修改表的缺省字符集,常用的字符集有:UTF8、GBK、GB2312、latin1等,其中UTF-8用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码。UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。GBK是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大。
GBK、GB2312等与UTF8不兼容,需要通过Unicode编码才能相互转换:
GBK、GB2312--Unicode--UTF8
UTF8--Unicode--GBK、GB2312 。
字符集涉及面比较广,如服务器或数据库或表字符集、应用字符集(如连接字符集、文件字符集等)、客户端字符,一般这些环节的字符集需要一致或兼容,尤其对中文而言,否则可能导致乱码。如何解决乱码问题,后面我们也有介绍。
以下是修改表的字符集的一个简单实例:

mysql>show create table test01 ; ###查看表存储引擎、字符集等信息
+--------+---------------------------------------------------------------------| Table | Create Table
+--------+---------------------------------------------------------------------| test01 | CREATE TABLE <code>test01</code> (
<code>name</code> varchar(60) DEFAULT NULL,
<code>code</code> varchar(20) NOT NULL,
<code>a2</code> int(11) DEFAULT NULL,
<code>a3</code> date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 | ###缺省字符集为utf8
----------------------------------+
mysql>ALTER TABLE test01 CONVERT TO CHARACTER SET gbk; ###修改缺省字符集为gbk
Query OK, 0 rows affected (0.12 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> show create table test01 ; ###检查修改是否成功
+--------+---------------------------------------------------------------------| Table | Create Table
+--------+---------------------------------------------------------------------| test01 | CREATE TABLE <code>test01</code> (
<code>name</code> varchar(60) DEFAULT NULL,
<code>code</code> varchar(20) NOT NULL,
<code>a2</code> int(11) DEFAULT NULL,
<code>a3</code> date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk | ###修改成功

修改方式或命令还有很多,大家可以借助其强大的help进一步获取其他命令的使用方法,如可以通过help ALTER TABLE查询其他使用方法,这里就不在一一例举了。
2.2.4 插入数据
往表中插入数据,有多种方法,如通过SQL语句、客户端、数据备份工具等。
1、通过SQL命令:insert into table_name (列1,列2,..) Values(‘’,’’,’’,….);
用这种方法需要注意列与值的对应关系;如果不指定列名,则指所有列:
如insert into table_name values(‘’,’’,’’,…),values(),
2、通过客户端导入;
3、利用工具(load data file或mysqlimport等)借助数据文件来导入数据。
这里我们介绍第1种方法,其它2种后面讲数据备份时将介绍。
往表test01插入一条记录。

mysql> select * from test01; ###查看表记录信息
Empty set (0.01 sec)

mysql> INSERT INTO test01(name,code,a2,a3)
-> VALUES('mysql','001',10,'2016-10-30'); ###插入1条记录
Query OK, 1 row affected (0.07 sec)

mysql> select * from test01;
+-------+------+------+------------+
| name | code | a2 | a3 |
+-------+------+------+------------+
| mysql | 001 | 10 | 2016-10-30 | ###插入成功
+-------+------+------+------------+

这是往插入1条记录,如果想一次往表插入多条记录如何实现呢?我们只要在后面添加个values即可,如同时往表中插入2条记录:
insert into table_name values(‘’,’’,’’,…) ,(‘’,’’,’’,…)
注意记录间用逗号。
具体操作请看下例

mysql> INSERT INTO test01(name,code,a2,a3) ###同时插入2条记录
-> VALUES('linux','002',20,'2016-10-30'),
-> ('spark','003',30,'2016-10-30');
Query OK, 2 rows affected (0.06 sec)
Records: 2 Duplicates: 0 Warnings: 0

mysql> select * from test01; ###检查结果,插入成功
+-------+------+------+------------+
| name | code | a2 | a3 |
+-------+------+------+------------+
| mysql | 001 | 10 | 2016-10-30 |
| linux | 002 | 20 | 2016-10-30 |
| spark | 003 | 30 | 2016-10-30 |
+-------+------+------+------------+

2.3 增删改数据

存储数据时用来查询分析用的,所以查询分析数据是平时重要任务,但数据库中数据它不会自动生成,需要我们去维护,当然大部分是系统自动维护,不需要手工去操作,不过维护程序还是需要写的,这章我们介绍如何维护数据库数据。这里我们主要介绍如何新增数据、如何修改数据、如何删除数据等。
2.3.1 插入数据
插入数据语句的语法:
INSERT INTO表名[(列名1,……列名n)] values(值1,…..值n);
这个SQL语句一次往表中插入1条记录,如果一次要插入多条记录是否可以呢?可以,而且很方便,插入多条语句为:
INSERT INTO表名[(列名1,……列名n)] VALUES(值1,…..值n), (值1,…..值n),..;
下面我们还是通过一些实例来进步说明如何操作。

mysql> CREATE TABLE test03 (id INT NOT NULL,name VARCHAR(20),birthday DATE);
Query OK, 0 rows affected (0.17 sec)

mysql> DESC TEST03; ###查看表结构
ERROR 1146 (42S02): Table 'testdb.TEST03' doesn't exist
mysql> DESC test03;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
| name | varchar(20) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.04 sec)

mysql> INSERT INTO test03 VALUES(100,'
张华', '2000-01-01'); ##对所有字段插值
Query OK, 1 row affected (0.16 sec)

mysql> INSERT INTO test03(id,name) VALUES(200,'
刘婷'); ###选择字段插值
Query OK, 1 row affected (0.13 sec)

mysql> SELECT * FROM test03;
+-----+--------+------------+
| id | name | birthday |
+-----+--------+------------+
| 100 | 张华 | 2000-01-01 |
| 200 | 刘婷 | NULL |
+-----+--------+------------+
2 rows in set (0.00 sec)
mysql> INSERT INTO test03(id,name) VALUES(300,'
貂蝉'),(400,'吕布'); ##插入多条
Query OK, 2 rows affected (0.03 sec)
Records: 2 Duplicates: 0 Warnings: 0

2.3.2 修改数据
修改数据也是很常见的,不过修改数据前,记得备份数据。如何备份数据后面将介绍。
修改数据的一般语法:
UPDATE表名SET列名1=值1,….列名n=值n
[WHERE条件];
以下以修改id为200对应的name为例,假设发现id为200对应的name输错了,不是刘婷,而是刘涛。

mysql> UPDATE test03 SET name='刘涛' WHERE id=200; ###修改id对应name值
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> SELECT * FROM test03 WHERE id=200; ###查询验证结果
+-----+--------+----------+
| id | name | birthday |
+-----+--------+----------+
| 200 | 刘涛 | NULL | ###修改成功
+-----+--------+----------+
1 row in set (0.00 sec)

2.3.3 删除数据
删除数据时,我们可以选择删除几条,或满足某些条件的记录,当然也可以删除所有记录。在日常工作中,删除数据需要非常谨慎,务必养成一个良好习惯,先备份,后删除。对生产环境数据、或正式环境的数据,不建议物理删除,最好采用逻辑删除的方式(即修改对应记录的状态或有效时间等)。数据都有价值。
用DELETE删除数据的一般语法:
DELETE FROM表名 [WHERE条件];
DELETE 加上条件,就可以有条件删除记录;如果没有条件,将删除所有数据。删除所有数据也可用TRUNCATE命令(这种方式删除数据比较快),其命令格式为:
TRUNCATE [table] 表名;
使用DELETE FROM 表名或TRUNCATE [table] 表名命令删除全部记录时,有一种情况需要注意,如果一个表中有自增字段,这个自增字段将起始值恢复成1.如果你不想这样做的话,可以在DELETE语句中加上永真的WHERE,如WHERE 1或WHERE true。

mysql> SELECT * FROM test03;
+-----+--------+------------+
| id | name | birthday |
+-----+--------+------------+
| 100 | 张华 | 2000-01-01 |
| 200 | 刘涛 | NULL |
| 300 | 貂蝉 | NULL |
| 400 | 吕布 | NULL |
+-----+--------+------------+
4 rows in set (0.00 sec)

mysql> DELETE FROM test03 WHERE (id=300 or id=400);
Query OK, 2 rows affected (0.02 sec)

mysql> SELECT * FROM test03;
+-----+--------+------------+
| id | name | birthday |
+-----+--------+------------+
| 100 | 张华 | 2000-01-01 |
| 200 | 刘涛 | NULL |
+-----+--------+------------+
2 rows in set (0.00 sec)

mysql> DELETE FROM test03;
Query OK, 2 rows affected (0.01 sec)

mysql> SELECT * FROM test03;
Empty set (0.00 sec)

2.4 查询数据

数据库主要功能是存储数据,但存储数据不是最终目的,存储数据最终目的是为了展示和分析,如何分析展示数据库中数据,数据查询就是重要手段。MySQL提供了功能强大、又非常灵活、非常方便的语句实现这些操作。这一章将介绍使用SELECT语句实现简单查询、子查询、连接查询、分组查询及利用正则表达式查询等。
2.4.1 一般查询语句
最简单的是SELECT [列名]FROM [表名] WHERE [条件] 。然后你可以在后面加上像[LIMIT][ORDER BY][GROUP BY][HAVING]等。
[列名]: 可以多个字段(列间用逗号分隔),也可所以字段(一般用*表示所有字段)
[表名]: 可以是一个表名或视图名,也可以是多表或多视图(表间用逗号分隔)。
[条件]: 为可选项,如果选择该项,将限制行必须满足的查询条件。
[LIMIT]: 后跟[位置偏移量,] 行数(第1行的位置偏移量为0,第2行为1,以此类推。)
[ORDER BY]: 后跟字段,可一个或多个,根据这些字段进行分组。
[GROUP BY]: 后跟可一个或多个字段,根据这些字段进行排序,升序(ASC)或降序(DESC)。
其后也可跟WITH ROLLUP,增加一条合计记录。
[HAVING]: 一般与GROUP BY一起使用,用来显示满足条件的分组记录。

2.4.2 单表查询
单表查询就是从1张表中查询数据,后续将介绍多表查询。为查询表数据我们需要先做些准备工作。
2.4.2.1 准备工作
准备工作包括:1、定义表结构,创建表;2、查看分析数据文件;3.把数据导入到表中。
1.首先我们创建一个存储学生各科成绩的表(stud_score),表的定义如下:


(表6-1 学生成绩表 stud_score)
字段名称 字段描述 数据类型 主键 外键 非空 自增
stud_code 学生代码 VARCHAR(20) 是 否 是 否
sub_code 学科代码 VARCHAR(20) 是 否 是 否
sub_name 学科名称 VARCHAR(100) 否 否 否 否
sub_tech 学科老师代码 VARCHAR(20) 否 否 否 否
sub_score 学科成绩 SMALLINT(10) 否 否 否 否
stat_date 统计日期 DATE 否 否 否 否
转换为建表的SQL语句为:
DROP TABLE IF EXISTS stud_score;
CREATE TABLE stud_score (
stud_code varchar(20) NOT NULL,
sub_code varchar(20) NOT NULL,
sub_name varchar(100) default NULL,
sub_tech varchar(20) default NULL COMMENT '教师代码',
sub_score smallint(10) default NULL,
stat_date date default NULL,
PRIMARY KEY (stud_code,sub_code)
);
2、创建这个表以后,我们需要把一个包含该表数据的文件(在slave02节点的/tmp目录下,名称为stud_score.csv)导入该表,另该文件第1行为字段名称,需过滤掉。我们先操作,具体语法等我们后续会介绍。
查看该数据文件信息

hadoop@master:/tmp$ pwd
/tmp
hadoop@master:/tmp$ ls -l |grep 'stud*'
-rw-rw-r-- 1 feigu feigu 1508 Jul 6 15:47 stud_info.csv
-rw-rw-rw- 1 mysql mysql 7157 Jul 2 11:32 stud_score_0702.txt
-rw-rw-rw- 1 mysql mysql 7157 Jul 2 21:43 stud_score_0703.txt
-rw-rw-r-- 1 feigu feigu 4652 Jul 2 10:26 stud_score.csv
hadoop@master:/tmp$ head -2 stud_score.csv
stud_code,sub_code,sub_nmae,sub_tech,sub_score,stat_date
2015101000,10101,数学分析,,90,
hadoop@master:/tmp$ cat stud_score.csv |wc -l ###查看文件记录总数
122

3.把数据文件导入表中

mysql> DROP TABLE IF EXISTS stud_score; ###判断是否存在,存在则删除
Query OK, 0 rows affected (0.36 sec)

mysql> CREATE TABLE stud_score ( ###创建表
-> stud_code varchar(20) NOT NULL,
-> sub_code varchar(20) NOT NULL,
-> sub_name varchar(100) default NULL,
-> sub_tech varchar(20) default NULL COMMENT '教师代码',
-> sub_score smallint(10) default NULL,
-> stat_date date default NULL,
-> PRIMARY KEY (stud_code,sub_code)
-> );
Query OK, 0 rows affected (0.07 sec)
mysql> select * from stud_score; ###查看是否有数据
Empty set (0.00 sec)
mysql> load data infile '/tmp/stud_score.csv' into table stud_score fields terminated by "," ignore 1 lines; ###把数据导入到表中
Query OK, 121 rows affected, 121 warnings (0.25 sec)
Records: 121 Deleted: 0 Skipped: 0 Warnings: 121
mysql> select count(*) from stud_score; ###查看验证记录总数
+----------+
| count(*) |
+----------+
| 121 |
+----------+
1 row in set (0.07 sec)

2.4.2.2 查看指定行数据

mysql> SELECT * FROM stud_score where sub_name='高等代数'; ###查看指定学科
+------------+----------+--------------+----------+-----------+------------+
| stud_code | sub_code | sub_name | sub_tech | sub_score | stat_date |
+------------+----------+--------------+----------+-----------+------------+
| 2015101000 | 10102 | 高等代数 | | 88 | 0000-00-00 |
| 2015101001 | 10102 | 高等代数 | | 78 | 0000-00-00 |
| 2015101002 | 10102 | 高等代数 | | 97 | 0000-00-00 |
| 2015101003 | 10102 | 高等代数 | | 87 | 0000-00-00 |
| 2015101004 | 10102 | 高等代数 | | 77 | 0000-00-00 |
| 2015101005 | 10102 | 高等代数 | | 65 | 0000-00-00 |
| 2015101006 | 10102 | 高等代数 | | 68 | 0000-00-00 |
| 2015101007 | 10102 | 高等代数 | | 80 | 0000-00-00 |
| 2015101008 | 10102 | 高等代数 | | 96 | 0000-00-00 |
| 2015101009 | 10102 | 高等代数 | | 79 | 0000-00-00 |
| 2015101010 | 10102 | 高等代数 | | 52 | 0000-00-00 |
+------------+----------+--------------+----------+-----------+------------+
mysql> SELECT * FROM stud_score LIMIT 3; ###查看前3行
+------------+----------+--------------+----------+-----------+------------+
| stud_code | sub_code | sub_name | sub_tech | sub_score | stat_date |
+------------+----------+--------------+----------+-----------+------------+
| 2015101000 | 10101 | 数学分析 | | 90 | 0000-00-00 |
| 2015101000 | 10102 | 高等代数 | | 88 | 0000-00-00 |
| 2015101000 | 10103 | 大学物理 | | 67 | 0000-00-00 |
+------------+----------+--------------+----------+-----------+------------+
3 rows in set (0.00 sec)

mysql> SELECT * FROM stud_score LIMIT 2, 2; ###查看第3行开始后2行
+------------+----------+-----------------+----------+-----------+------------+
| stud_code | sub_code | sub_name | sub_tech | sub_score | stat_date |
+------------+----------+-----------------+----------+-----------+------------+
| 2015101000 | 10103 | 大学物理 | | 67 | 0000-00-00 |
| 2015101000 | 10104 | 计算机原理 | | 78 | 0000-00-00 |
+------------+----------+-----------------+----------+-----------+------------+
2 rows in set (0.00 sec)

2.4.2.3 模糊查询
利用LIKE 关键字可以进行模糊查询。下例查询学科代码以101开头的记录总数。

mysql> SELECT count(*) FROM stud_score WHERE sub_code LIKE '101%';
+----------+
| count(*) |
+----------+
| 55 |
+----------+
mysql>select * from stud_info where birthday REGEXP '^1998' ;
mysql> select * from stud_info where birthday REGEXP '10$' ;

2.4.2.4 分组查询
如果我们希望按学科来统计学生成绩、班级成绩该如何实现呢?这就涉及到分组统计问题,如果需要按学科统计,可以GROUP BY sub_code;然后取前3名学科,具体实现请看下例:

mysql> SELECT sub_code,SUM(sub_score) from stud_score GROUP BY sub_code;
+----------+----------------+
| sub_code | SUM(sub_score) |
+----------+----------------+
| 10101 | 863 |
| 10102 | 867 |
| 10103 | 830 |
| 10104 | 932 |
| 10105 | 857 |
| 20101 | 870 |
| 20102 | 892 |
| 20103 | 866 |
| 20104 | 864 |
| 20105 | 822 |
| 20106 | 843 |
+----------+----------------+
11 rows in set (0.00 sec)
mysql> SELECT sub_code,SUM(sub_score) AS sum_score from stud_score GROUP BY sub_code
-> ORDER BY sum_score DESC LIMIT 3; ###按学科总分排序,取前3。
+----------+-----------+
| sub_code | sum_score |
+----------+-----------+
| 10104 | 932 |
| 20102 | 892 |
| 20101 | 870 |
+----------+-----------+

2.4.3 多表查询
多表查询,需要连接2张或2张以上的表一起查询,连接有多种方式,如内连接(通常缺省连接)、外链接(又分为左连接,右连接)等。多表连接时,建议不宜一下连接很多表,尤其是数据比较大时,可以采用两两连接等方式。
要实现多表连接,还需创建一个存储学生基本信息的表(stud_info)并导入数据.
2.4.3.1 准备工作
创建一个学生基本信息的表(表定义如下),并把数据(一个数据文件)导入到表中。
准备步骤,1.定义并创建表,2,查看分析数据文件,3. 导入数据,并验证导入结果。
1.定义并创建表

mysql> CREATE TABLE stud_info (
-> stud_code varchar(20) NOT NULL,
-> stud_name varchar(100) NOT NULL,
-> stud_sex char(1) NOT NULL default 'M' COMMENT '性别',
-> birthday date default NULL,
-> log_date date default NULL,
-> orig_addr varchar(100) default NULL,
-> lev_date date default NULL,
-> college_code varchar(10) default NULL COMMENT '学院编码',
-> college_name varchar(100) default NULL,
-> state varchar(10) default NULL,
-> PRIMARY KEY (stud_code)
-> ) ;
Query OK, 0 rows affected (0.05 sec)

2.查看分析数据文件

hadoop@master:/tmp$ ls -l|grep 'stud_info*'
-rw-rw-r-- 1 feigu feigu 1508 Jul 6 15:47 stud_info.csv
hadoop@master:/tmp$ cat stud_info.csv|wc -l ###查看文件记录总数
23
hadoop@master:/tmp$ head -3 stud_info.csv ###查看前3行
stud_code,stud_name,stud_gend,birthday,log_date,orig_addr,lev_date,college_code,college_name,state
2015101000,王进,M,1997/8/1,2014/9/1,苏州,,10,理学院,1
2015101001,刘海,M,1997/9/29,2014/9/1,上海,,10,理学院,1

3.导入数据并验证结果

mysql> select count(*) from stud_info; ###查看表数据
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
mysql> load data infile '/tmp/stud_info.csv' into table stud_info fields terminated by "," ignore 1 lines;
Query OK, 22 rows affected, 22 warnings (0.05 sec)
Records: 22 Deleted: 0 Skipped: 0 Warnings: 22
mysql> select count(*) from stud_info; ###成功导入22条记录
+----------+
| count(*) |
+----------+
| 22 |
+----------+
1 row in set (0.00 sec)

2.4.3.2 多表连接查询
两表内连接、左连接,右连接的含义及使用关键字请参看下图:


(图6-1 多表连接示意图)

以下通过几个实例进一步说明多表连接的使用方法:

mysql> SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a,stud_score b WHERE a.stud_code=b.stud_code LIMIT 3; ###内连接,a定义为stud_info表的别名。
+-----------+--------------+-----------+
| stud_name | sub_name | sub_score |
+-----------+--------------+-----------+
| 王进 | 数学分析 | 90 |
| 王进 | 高等代数 | 88 |
| 王进 | 大学物理 | 67 |
+-----------+--------------+-----------+
mysql> SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a LEFT OUTER JOIN stud_score b ON a.stud_code=b.stud_code LIMIT 3; ###左连接
+-----------+--------------+-----------+
| stud_name | sub_name | sub_score |
+-----------+--------------+-----------+
| 王进 | 数学分析 | 90 |
| 王进 | 高等代数 | 88 |
| 王进 | 大学物理 | 67 |
+-----------+--------------+-----------+
3 rows in set (0.00 sec)
mysql> SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a RIGHT OUTER JOIN stud_score b ON a.stud_code=b.stud_code LIMIT 3; ###右连接
+-----------+--------------+-----------+
| stud_name | sub_name | sub_score |
+-----------+--------------+-----------+
| 王进 | 数学分析 | 90 |
| 王进 | 高等代数 | 88 |
| 王进 | 大学物理 | 67 |
+-----------+--------------+-----------+
3 rows in set (0.00 sec)

2.4.3.3 子连接查询
这里介绍以用IN关键字实现子链接,基本格式为:
SELECT * FROM 表名WHERE 字段 IN (SELECT 字段 FROM 表名 WHERE 条件);
使用子查询,比较灵活,且有利于把大表关联转换为小表关联。下例为表stud_info中
stud_code从表stud_score中成绩为大于或等于90分的子查询中获取。

mysql> SELECT stud_name FROM stud_info WHERE stud_code IN(SELECT stud_code FROM stud_score WHERE sub_score>=90 ) LIMIT 3;
+-----------+
| stud_name |
+-----------+
| 王进 |
| 刘海 |
| 张飞 |
+-----------+
3 rows in set (0.02 sec)

2.4.3.4 视图查询

上面的查询语句,有的比较简单,有的比较复杂,像对那些复杂的查询语句,包含了很多信息量,而且有可能还要经常使用,但命令行是无法保证这些语句的,如果下次还要使用是否又重新写一遍呢?大可不必,我们可以把这个查询语句以视图的形式保存到数据库中,然后直接查询这个视图即可。
如上面内连接的SQL语句:SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a,stud_score b WHERE a.stud_code=b.stud_code LIMIT 3;
我们可以把它定义为一个视图V_STUD,然后查询视图即可,非常方便!而且视图会保存到数据库中,但视图本身不保存数据。具体实现请看下例:

mysql> CREATE VIEW V_STUD AS SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a,stud_score b WHERE a.stud_code=b.stud_code LIMIT 3; ###创建视图
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM V_STUD; ###查询视图
+-----------+--------------+-----------+
| stud_name | sub_name | sub_score |
+-----------+--------------+-----------+
| 王进 | 数学分析 | 90 |
| 王进 | 高等代数 | 88 |
| 王进 | 大学物理 | 67 |
+-----------+--------------+-----------+
3 rows in set (0.00 sec)

2.5课后练习

一、在testdb库上创建表,表结构如下:

写一个sql文件,功能包括:
1、创建以上表结构,表名为:test_学号;
2、往表test_序号插入至少3条数据;
3、在表最后位置添加一个字段,字段要求如下:

二、创建表并把数据导入表中,然后查询相关数据,把这些操作放到一个SQL文件中,具体要求如下:
1、创建两表,表名分别为,stud_score_学号:stud_info_学号,表结构请参考第6.2.1节和6.3.1节
2、导入数据,把/tmp目录下stud_info.csv,stud_score.csv分别导入到表stud_info_学号,stud_score_学号中
3、查询数据,查询表stud_info_学号中,把姓刘的同学过滤出来。
三、多表查询,在一个SQL语句实现以下功能
1、内连接,实现stud_score_学号:stud_info_学号的内连接,连接字段为stud_code
2、左连接, 实现stud_score_学号:stud_info_学号的左连接,连接字段为stud_code
四、聚合查询
1、把选了大于5门课的同学的名称及各科总成绩打印出来。
五、写一个存储过程,实现功能,输入学院代码,输出,学生名称、学院名称、各科平均成绩,如下图:

第1章 Linux入门

1.1 Linux能做啥

Windows能做啥? 可能十个人十个都知道。具体能做啥,就不用我说了。Linux能做啥?为啥有了Windows,为啥还要Linux?
Windows与Linux的关系,打个不很恰当的比喻,如果把Windows和Linux都作为交通工具,Windows就想单车(自行车),Linux就像列车。前者方便易用,并几乎没有轨道的限制,但它只能单用户,能承载的重量有限;后者虽然不像前者方便易用,但它可以多用户,并且可以运送巨无霸的东西!如管理成千上万个节点的大数据集群,还有关键一点是安全,还免费,更不用说其庞大的志愿者和使用者,当然,它也有一些不足或不如人意的地方,如需通过命令方式操作,这给初学者带来不少挑战,不过Linux有很多奇妙的输入方法,使用这些方法将极大提高输入效率,这些方法我们后续章节将介绍,除了这些输入方式外,Linux还有很多更强大的方法,提高你的工作效率。

1.2 如何学习Linux

学好Linux将大大提升你的竞争力,哪如何学好它呢?是否存在捷径?我觉得有但又没有。
作为初学者,个人建议可以像小孩学英语一样,先学会用,而且是学哪些最常用、最基础、最重要的部分。如何会用,理解起来就方便多了。否则,已开发就学一大堆的语法,效果往往事倍功半。此外,就像锻炼身体、学习英语等一样,贵在实践再实践,“纸上得来终觉浅绝知此事要躬行”。
另,向大家推荐几个学习Linux的网站:
Linux中国:https://linux.cn
鸟哥的Linux私房菜: http://linux.vbird.org
Linux下载站: http://www.linuxdown.net
Linux公社: http://www.linuxidc.com

1.3 Linux的历史

Linux的前辈是Unix,但可能是由于历史原因,Unix派别林立而且主要用在大型服务器,如IBM的AIX,HP的HP-Unix,SUN公司(目前是Oracle的一部分)的SUN-Unix等等,更要命的一点是他们商业味道很浓。
1991年10月5日, 上午11时53分,有一个名为 Linus Torvalds 的年轻芬兰大学生在 comp.os.minix 这个新闻群组上发表了这样一个帖子,称他以bash,gcc等工具写了一个小小的内核程序,这个内核程序可以在Intel的386机器上运行,这引起很多的兴趣,由此标志L inux的诞生。
当初还是个大学生的 Linus 大概完全没想到当初被他视为个人兴趣的程式,在几年以后会有超千万个使用者,由他自己带头开发的操作系统现在已经在世界各地受到普遍的欢迎。

1.4 Linux的发行版本

Linux基于Debian、Red Hat和SUSE源的那些发行版本在生产环境中占据优势地位,以下为目前Linux比较流行的版本。

表1.1 Linux流行版本

对Linux而言,内核是其心脏,与硬件打交道,开机后留住在内存,而Shell为内核与应用程序间的桥梁,内核及shell构成Linux的主要内容,应用程序通过shell(或命令行)与服务器进行交互,shell调用内核(Kernel)来利用和管理服务器硬件资源,应用程序(如用户自己编写shell脚本等,office是windows操作系统的应用程序)保存在硬盘,需要时调入内存,它们之间的关系可用下图形表示:


(图1-1 Linux操作系统结构图)

1.5 登录系统

前面讲了Linux是个非常重要、功能强大、而且有意思的系统,而且还提到需要在命令行操作,不像使用windows一样,只需要按按鼠标即可,命令行操作确实不方便,尤其是对大多数用惯了windows的人来说。这确实有个‘痛苦’的过程,但是windows一般只能用来写写文档、上上网、打打游戏、看看电影之类,企业生产环境大都采用非windows环境,如Linux或Unix,因此对于大多数希望从事IT相关行业者,尤其进入大数据这个朝阳行业的人来说,是一道必需迈过的坎。
万事开头难,实际上你只要用过一段时间,将会慢慢喜欢上它,它有很多你想不到的优点,如tab补全功能。
如何登录Linux系统呢?如果Linux就在本地机器上,只需要启动Linux即可;如果Linu服务器不在本地,那也很简单,只要在你的客户端安装一个连接Linux的客户端(如Putty或Xshell等,这些在网上都可免费下载),然后做一个简单配置就可以了,下面我们以登录远程Linux服务为例(注:本例中Linux已安装好,并创建一个用户:feigu_shell):
第一步:安装Xshell 5
从网上下载一个Xshell 5,安装基本按照缺省情况即可
第二步:打开Xshell 5,点击文件菜单,然后点击新建,进入下一个界面

第三步:点击连接,配置服务器的IP,端口,如下图:

第四步:点击用户身份验证,配置用户及密码信息,如下图:

第五步:点击确认按键,所有配置完成,最后在会话界面将出现刚创建的连接,名为conn_linux,如下图。

第六步:点击连接,如果IP,用户密码等都正确的话,进入Linux系统,如能看到如下类似界面,祝贺你,你已经迈入Linux大门了!

Xshell 5是连接Linux服务器的工具之一,还可以通过Putty等工具,配置和使用也很方便,此外大家还可以利用Xftp 5工具来上传或下载Linux服务器上的文件,它的安装和配置比较简单,网上有很多这方面的文档,这里就不再展开来说了。

1.6 登录后,试试身手

登录Linux后,可以用一些简单命令,试一试Linux这个听起来有点神秘的操作系统。

注意,linux是区分大小写的或称为大小写敏感,所以pwd和PWD是不同的。
Linux系统要经常输入命令,而且有些命令或文件还比较长,如果要一个一个输入,不但麻烦,还容易出错。Linux是否有更有效的方法呢?答案是肯定的,那就是利用Tab键!它具有命令补全或文件补全的功能,这也是Linux很有趣的地方之一。当然,Linux为尽量减少你敲键盘次数,还有更强大的地方,如编写shell脚本,这个后面我们会详细介绍。
先看几个使用Tab键来偷点懒。
假如我们想进入homework1130这个目录,是否要cd homework1130?如采用Tab补全功能,在大大减少输入量的同时,还可大大提高你的准确率。具体实现方法为:输入目录名称的前几个字符即可,然后按Tab,系统自动补全该目录名称,具体操作如下图:


按Tab键后得如下界面

类似的神奇,Linux还有很多,如通过上下方向键(↑↓),可以调出前面输过的命令,输过了的命令可以不用重新输入了,这个功能也不错,顺便提一下,这些功能在MySQL数据库也有,讲MySQL时我们会介绍,看来好东西,大家都喜欢。

1.7 shell种类

shell是Linux内核与应用程序间的桥梁,起着非常重要的作用,shell由多种,如bsh(或sh),csh,ksh,bash等,这些shell基本内核对差不多,但还是有不少不一样的地方,所以我们在编写shell程序时,首先需要说明使用哪种shell来解释你程序,具体做法就是在shell程序第一行说明,如#!/bin/bash,这些内容大家先了解一下,后面我们会详细介绍,它们间关系大致如下:


(图1-2shell种类及关系图)

shell有多种,每个连接用户系统一般会先给个缺省shell(当然缺省shell可以修改),哪里可以看到你的缺省shell呢?查看文件/etc/passwd,或echo $SHELL 可以看到用户使用的缺省shell,查看/etc/shells可以看到目前已安装的shells。如何查看这些文件,后面将介绍。

1.8 文件与目录简介

说到文件和目录,我们从大家熟悉的windows开始,打开windows有关界面,我们通常能看到如下类似界面:


(图2-1 windows的目录及文件)

第[1]列为文件及目录名称:是文件还是目录,从图标很容易看出,其中有些为系统目录有 些是用户创建目录或文件。
第[2]列为修改时间;文件或目录的修改或创建时间。
第[3]列为类型:显式说了是文件夹还是文件,甚至为那种类型的文件。
第[4]列为文件大小,如果是文件,会显示其大小,单位为KB。
那Linux系统的目录及文件,又是如何排兵布阵的呢?windows进行下级目录,或上级目录只需要点击相关目录或到上级目录的方向图标,在Linux系统中如何进入下级目录或上级目录,如何打开文件?实际上在Linux系统虽然需要使用命令,但使用起来也很方便,而且在安全性、易用性方面更有独到的地方,目录的切换使用cd命令,查看当前目录或文件可以使用ls命令,详细操作我们如下实例。


(图2-2 Linux下的目录、文件及权限等)

用ls -la(,注意ls 与-la间有一个空格,l(list)及a(all)为两个参数)可以查看当前目录下的文件或目录的详细信息,包括列出隐含文件(文件名前为.,如上面的.bashrc文件)。
与windows的内容基本相同,windows的第[1]、[2]、[3]、[4]列分别对应Linux的第[7]、[6]、[1]、[5],虽然没有windows这么直观,但包含更丰富的信息。理解这些信息非常重要,以后经常提到这些相关内容,下面我们对上面7列代表的含义逐一进行说明。
 第[1]列:表示文件类型及权限,如果你仔细观察一下,会发现这列共有10个字符,
这10个字符的位置及含义,我们把它放大一下,如下图:


(图2-3文件及目录权限说明图)

第1字符为d或-,d表示对应是目录,-对应是文件;
第2,3,4字符,如[rw-]、[rwx],表示用户或文件所有者的权限,r(read)表示有读的权限,w(write)表示有写的权限,x(execute)表示有执行权限,-表示没有对应权限。[rw-]
表示文件所有者有读、写权限,但没有执行权限。
第5、6、7字符,表示文件所属的用户组的权限。
第8、9、10字符,表示其他用户(有点像windows中guest用户)的权限。

 第[2]列:表示文件或目录的连接数,如果目录至少为2,因任何一个目录至少包括.及..
两个目录,.表示当前目录,..表示上级目录,故如何我们需要切换到上级目录,可以cd ..
即可。
 第[3]列:表示文件或目录的所有者账号
 第[4]列:表示文件或目录所属用户组,如上面列出feigu feigu,说明所属用户为feigu,所属用户组也是feigu(这里两者同名,当然两者可以不同名)。
 第[5]列:表示文件大小,缺省单位为B(非KB)。
 第[6]列:表示文件文件或目录的创建或修改时间。
 第[7]列:表示文件或目录名称,如果文件前多一个点的表示隐含文件,隐含文件只有加上a这个参数才会显示出来,否则不会显示出来。
以上这些权限或属性当然不是一成不变的,可以通过命令修改,如何修改2.3节将介绍。

ls命令的使用方法还有很多,我这里介绍几种常用方法:
ls -lt ####结果按时间排序,降序。
ls -l ####结果按名称排序
ls -lS ####按文件大小排序,注意S为大写
ls -ltr ####结果按时间排序,变为升序,增加参数r(reverse),表示与缺省排序方式相反。
看到这里,可能有很多朋友不耐烦了,这么多命都要一一去记吗?其实完全不必要去死记忆,Linux有一个类似windows中help工具,他就是man这个命令,这个man非常强大,不过其来源是manual,而非男人哦。使用起来很方便,其格式为:man [command] ,显示结果中有各参数的含义、示例等。

1.9 切换目录

Linux的存储都是以目录的方式存在的,所以了解其目录结构、主要目录的含义非常重要,在不同目录间切换是Linux人员做得最多的事情之一,既然是经常干的活,是否有些妙招呢?有但又没有,关键还是要多练,“熟练才能生巧”。
我们先来看一看Linux的主要目录及用途:

 

(图2-4 Linux主要目录结构图)

切换目录使用cd命令,如果要到根目录(Linux系统最高目录),可以cd / 即可;如果切换到上级目录,使用cd ..;如果要回到用户主目录,用cd ~ 。
查看当前你所在的目录,使用pwd命令;查看当前目录下的有哪些文件或目录,使用ls命令。
.表示当前目录,也可用./表示;..表示上级目录,也可用../表示。
由根目录(/)开始的路径成为绝对路径,如:/home/feigu/hadoop;相对于当前路径的称为相对路径,如:./hadoop 、~/spark等。

1.10 创建文件或目录

前面我们介绍了Linux的目录结构、如何查看目录、如何修改文件或目录的属性和权限等内容,当然我们学Linux肯定不是来看看的,我们还想做一些实际事情,如创建文件或目录,拷贝文件,删除文件或目录等,这些任务在windows里相信大家都比较熟了,在Linux中该如何做呢?操作方便吗?不但方便还非常有趣,下面我们就来谈谈这方面的问题:
切换目录上面我们讲到了,利用cd命令即可,我们这里再小结一下:
cd /home/feigu/hadoop ####切换到指定目录
cd ~ ####切换到用户主目录,即用户一开始登录进入的目录
cd .. ####切换到上级目录
cd / ####切换到根目录
cd - ####切换到上一次使用的目录
查看目录可以用pwd,ls等命令。下面我们谈谈如何创建目录,用mkdir命令即可,其功能比windows前很多哦。其命令格式为:
mkdir [-p] 新建目录
-p ####可以递归创建目录,如果上级没有的系统将自动创建。示例如下:
 不用参数,创建单个目录

feigu@slave001:~/linux_test$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
feigu@slave001:~/linux_test$ mkdir mydirect ###创建新目录mydirect
feigu@slave001:~/linux_test$ ls -l
total 16
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect ###创建成功
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh

 使用-p参数,同时创建多级目录

feigu@slave001:~/linux_test$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
feigu@slave001:~/linux_test$ mkdir -p test1/test2 #同时创建两级目录
feigu@slave001:~/linux_test$ cd test1 #一级
feigu@slave001:~/linux_test/test1$ ls -l
total 4
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:26 test2 ###test1的子目录

删除目录的命令及其格式为:rmdir 目录名称
注意用rmdir命令删除目录时,需要该目录为空目录(即该目录下没有文件或子目录),否则,会报错。如果该目录下有很多文件和目录,岂不要逐一删除?是否有更强的命令?
有的,接下来这个命令就可。
rm [-fir] 文件或目录
-f ###f就是force之意,不提示,对不存的目录或文件也不报错或警告
-i ###i 就是interactive之意,删除前会询问是否删除
-r ###r就是recursive之意,即递归删除,这个非常强大,但也需非常谨慎使用。

feigu@slave001:~/linux_test$ ls -l
total 20
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
drwxrwxr-x 3 feigu feigu 4096 Oct 18 14:00 test1
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
feigu@slave001:~/linux_test$ rm -rf test1 ###删除test1及其子目录test2
feigu@slave001:~/linux_test$ ls -l
total 16
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh

与windows一样,对一些不满意的目录或文件,或根据实际情况,我们可重命名目录或文件名称,具体可使用mv命令,其格式为:
mv [-fiu] 原目录或文件目标目录或文件.
其实该命令也常用来移动目录或文件,就像windows中移动文件或目录一样。
目录或文件除了可以移动,当然可以复制,windows中有的,Linux也有。cp命令可以很方便用来复制文件或目录,其命令格式为:
cp [-fipr] 原文件或目录目标文件或目录

-f ###强制执行
-i ###如目标文件已存在,则会提示是否覆盖,覆盖按y,否则,按n
-p ###同时把文件或目录权限一起复制过去,而不仅仅默认权限或属性
-r ###递归复制,这个参数很给力
以上是一些常用参数,其实cp参数还有很多,大家可以通过man去了解更多参数的使用。
创建目录可以用mkdir,哪创建文件呢?创建文件比较简单,可用touch 文件名。当然也可用vi或vim等方法,vi或vim后续我们会讲到。

feigu@slave001:~/linux_test/mydirect$ ls -l
total 0
###用touch命令,创建一个名为myfile.txt的空文件
feigu@slave001:~/linux_test/mydirect$ touch myfile.txt
feigu@slave001:~/linux_test/mydirect$ ls -l
total 0
-rw-rw-r-- 1 feigu feigu 0 Oct 24 17:31 myfile.txt

【延伸思考】
如果要在两台服务器间复制文件,该如何操作呢?有兴趣可以考虑一下,没有兴趣的可跳过]

1.11 查看文件内容

前面我们谈了如何查看当前目录(pwd命令)、用ls命令查看当前目录下有哪些文件或目录,如何查看文件内容呢?在windows中我们要看一个文件需要打开才能看到,在Linux中,查看文件灵活的多,而且有趣的多。如可以不open也可查看,而且还可以顺着看,也可倒着看,等等,下面我们就来讲讲如何查看文件内容。
查看文件内容,我们可用的命令很多,常用的有:
cat ###从第一行开始查看,一次打开,如果文件较大时,建议采用其方法。
tac ###从最后一行开始看,tac啥意思?其实它没意思,就是cat的倒写。
more ###逐页地看,按回车,继续查看,按q退出。
less ###与more类似,但可以往前翻(按PgDn)。
###/ 字符串,可向下查询字符串
###?字符串, 可向上查看字符串
###n、N可继续查看以上字符串
###q退出。
head ###查看前几行
tail ###查看后几行
最后2个命令可指定查看头几行或最后几行,命令格式为:
head -n 数字文件名称 ####没有参数,缺省查看前10行
tail -n 数字文件名称 ####不带参数,缺省查看最后10行
以下为示例:

feigu@slave001:~$ head -n 5 stud_score_bak.csv ###查看前5行
stud_code,sub_code,sub_nmae,sub_tech,sub_score,stat_date
2015101000,10101,数学分析,,90,
2015101000,10102,高等代数,,88,
2015101000,10103,大学物理,,67,
2015101000,10104,计算机原理,,78,
feigu@slave001:~$ tail -n 3 stud_score_bak.csv ###查看最后3行
2015201010,20104,概率统计,,96,
2015201010,20105,汇编语言,,91,
2015201010,20106,数据结构,,87,

1.12 shell脚本

1.12.1 vim编辑器
vim编辑器是啥东西?它有哪些功能?为何要用vim?在windows中我们要编辑一个文件,可以用word、文本编辑器、excel等,如果要说这些编辑器哪个更像vim,那就是文本编辑器了,它们都不支持图片,但vim比文本编辑器功能上强很多,如支持语法高量、远程编辑、崩溃后文件恢复、与vi(vim是vi的加强版,vi也是很多Linux自带的编辑器)兼容等等,所以vim通常作为程序编辑器来使用。
刚开始用vi或vim来编辑文件或脚本时,大多数人都不习惯,这很正常,就像我们穿一双新鞋一样,大都有个磨合期。对于用惯word的朋友,在word中大部分动作基本用鼠标就可搞定,但在vi或vim中鼠标好像不起作用,Linux靠命令驱动,vi或vim也不例外,不过它有很多优点,时间久了你就可慢慢体会到。
好闲话少说,下面我们开始介绍vi或vim的使用方法,用vi或vim来编辑文件,操作上几乎一样,而且两者相互兼容,下面以vim为例来进行说明。
vim的使用分为三种模式,即普通模式、编辑模式、低行模式,了解这三种模式非常重要,实现这三种模式的转换很简单,就是按Esc键。
 普通模式
用vim打开文件时就进入了普通模式,在这种模式中,可使用方向键移动光标,也 可以删除、复制、粘贴字符
 编辑模式
顾名思义,在这种模式中,可以对插入、删除字符,在普通模式下,按下i或o或a之后,马上就进入到编辑模式,注意前提是在普通模式下。如果保证当前模式为普通模式,有个诀窍,多按几次Esc键。
 低行模式
在普通模式下,按“:或/或?”,就进入低行模式,为何叫低行模式?因此时光标会自动跳到低行,在低行模式下,可以进行保存、退出、查询等操作,这里同样要注意其前提,如何保证当前为普通模式,多按几次Esc键。
以下是vim三种模式的转换关系
(图3-1 vim三种模式间的转换图)

以下通过几个实例来说明:
(1)普通模式,利用vim或vi打开一个文件,处于普通模式

(2)在普通模式基础上,按i或a、o键,将进入编辑模式,在编辑模式下,用户可对文件进行增删改等操作。

(3)在编辑模式时按“Esc”键,将返回到普通模式,在普通模式时,按冒号(:),斜杠(/)或问号(?)将进入低行模式,低行模式可对文件进行保存退出等操作。:wq!表示保存并强制退出, :q! 不保存强制退出。

以上这些命令是最常用、最基础的,不过还有很多命令,而且还很多功能不错也很有趣的命令,大家可以从man或网上找到。
[注意:如果不正常退出或编辑时正好断网断线等异常情况,vim将在当前目录下创建一个扩展名为swp的暂时文件,利用这个文件可以用来恢复文件,恢复后可以删除该文件,否则下次再编辑该文件时,vim还会提示你是否要恢复或只读进入方式打开文件等。]
1.12.2 shell变量
说到变量相信大家都不陌生,像X,Y,Z都可以称为变量,与变量相对的就是常量,如a,b,c,2,3等等。shell的变量有不少特殊的地方,首先shell变量无需声明类型(顺便提一下:这点与Java、C、C++等不同,但与python 相似),下面具体讲讲shell变量的特点:
 变量的赋值:
格式为,变量名=变量值,
如,V1="abc"
字符串可以使用双引号"",和单引号'',但两者间有些区别,用双引号可以保留字符含 义,单引号将视特殊字符为一般字符,具体下面举例说明。
 变量的使用
需要在变量前加上$,如$v1 或${v1}。
 变量的显示:
echo $v1,下面通过一个示例说明双引号与单引号的区别:

feigu@slave001:~$ v1="numpy"
feigu@slave001:~$ v2="import $v1" ###双引号中含特殊字符$
feigu@slave001:~$ echo $v2
import numpy ###双引号保留了$的特殊含义
feigu@slave001:~$ v3='import $v2'
feigu@slave001:~$ echo $v3
import $v2 ###单引号内的$被作为一般字符

1.12.3 编写shell脚本
shell脚本(shell script)是啥样?为何要使用shell脚本?用了能带来啥好处?不用又会带来哪些不便?
shell脚本简单来说就是由shell命令写成一个程序,有点类似windows中dos下批处理文件(.bat),shell脚本无需编译就可直接运行。
假设哪天要你管理几十台甚至几百几千台Linux服务器,需要在每台服务器上创建很多相同目录、修改很多相同配置文件,你该如何处理呢?在每台服务器上都一个一个命令执行一下?恐怕几天都搞不完,即使完成了,也很难保证在每台服务器上做的都是一样。
如果我们把这些命令写成一个脚本,并在一台服务器上测试好,那么剩下的工作就是把这个脚本部署不同服务器上,运行一下即可(熟练的话,这些部署和运行都可一键搞定),如此不但快、而且质量也高。这就是shell脚本强大之一。
下面我们试着写一个简单的shell脚本,加深大家的理解。

feigu@slave001:~$ cat myfirst.sh ####shell脚本名称,以sh为扩展名
#!/bin/bash ###说明使用哪种shell解释你的程序,这里使用bash

#在界面上显示字符串 ###这一行为注释,shell注释用井号(#)
echo "I like linux!" ###把字符串“I like linux!”输出到窗口

关于编辑shell脚本的几个良好习惯
1、第一行说明用哪种shell解释你的脚本
如:#!/bin/bash
2、说明该脚本的功能、变更历史等。
3、添加必要的注释,方便别人更好理解你的程序,尤其是团体开发时。

脚本写好了,该如何执行呢?很简单哟,shell脚本无需编译,可以直接运行,运行方式大致有:
1、利用sh或bash运行,直接运行,但脚本权限要求不高,有读的权限即可。
如:sh myfirst.sh
2、利用. ./myfirst.sh或source ./myfirst.sh的方式,可直接运行, 有读的权限即可。
注./myfirst.sh前有空格
3、如果脚本有执行权限(即x权限),可采用如下格式:
./myfirst.sh

1.13 常用命令

在linux环境中,进行需要查找文件或目录,为此,我们可以利用find、或locate等命令;目前的大数据环境一般都是集群方式,如何访问集群中各个节点或服务器,如何在各节点间复制或传输文件等?这是本节主要介绍的内容。
 locate命令
locate也是从Linux的数据库文件中读取,但比whereis更灵活,其参数可使用正则表达式,其语法格式为:
locate [-ir] 文件或目录
-i ###查询时忽略大小写
-r ###后可接正则表达式

feigu@slave001:~$ locate -n 5 -i ipython ###查看含ipython目录或文件的前5条
/home/feigu/.ipython
/home/feigu/.ipython/README
/home/feigu/.ipython/extensions
/home/feigu/.ipython/nbextensions
/home/feigu/.ipython/profile_default
feigu@slave001:~$ locate -n 5 -r ^/var/lib/dpkg/info ###查看以/var/lib开头的
/var/lib/dpkg/info
/var/lib/dpkg/info/accountsservice.conffiles
/var/lib/dpkg/info/accountsservice.list
/var/lib/dpkg/info/accountsservice.md5sums
/var/lib/dpkg/info/accountsservice.postinst

 find命令
如果用whereis和locate无法查找到我们需要的文件时,可以使用find,但是find是在硬盘上遍历查找,因此将消耗较多资源,而且速度也比较慢,不过find可以根据指定路径或修改时间等进行查找,其语法格式为:
find [路径] [参数] [-print ] [-exec 或-ok command] {} \;
时间参数:
-mtime n ###列出n天之前的1天之内修改过的文件
-mtime +n ###列出n天之前(不含n天本身)修改过的文件
-mtime -n ###将列出n天之内(含n天本身)修改过的文件
名称参数:
-group name ###寻找群组名称为name的文件
-user name ###寻找用户者名称为name的文件
-name file ###寻找文件名为file的文件(可以使用通配符)
-print ###将查找到的文件输出到标准输出
-exec command {} \; ###将查到的文件执行command操作,{} 和 \;之间有空格
-ok ###和-exec相同,只不过在操作前要询用户
###列出1天内修改过的文件

feigu@slave001:~$ find /home/feigu/linux_test/ -mtime -1
###列出/home/feigu/linux_test目录下文件以myfirst开头的文件
feigu@slave001:~$ find /home/feigu/linux_test/ -name myfirst* -print

 操作集群:ssh或scp
如下图,假如我们需要通过一个客户端访问以下每台服务器,如何实现呢?我们可以在客户端安装xshell软件,然后通过ssh协议,访问集群中每台服务器,具体使用ssh命令,如何需要在服务器间copy文件,可以使用scp命令,这节介绍这些命令的具体使用。

hadoop@master:~$ ssh slave01 ###通过ssh命令,由master服务器登录到slave01
Welcome to Ubuntu 14.04.2 LTS (GNU/Linux 3.13.0-61-generic x86_64)

* Documentation: https://help.ubuntu.com/
...................
Last login: Sun Mar 19 23:18:45 2017 from master
hadoop@slave01:~$ exit ###返回master服务器
logout
Connection to slave01 closed.
hadoop@master:~$ ls ##查看当前目录下的文件或目录
anaconda2 ch7.jar student_info.csv test.py
mydemo seafile testpbd.py
###以下命令为把master服务器上的文件test.py复制到slave01节点上
hadoop@master:~$ scp test.py hadoop@slave01:/home/hadoop

【延伸思考】
如何把一台服务器上的一个目录,复制到另一台服务器上?

1.14 课后练习

一、在登录用户当前目录下,创建一个名称为Linux01的目录,然后在该目录创建一个空文件test.txt
二、在第一题的基础上,往文件test.txt插入4行内容:
linux
python
hadoop
spark
三、列出几种查看test.txt文件内容的方式
四、编写一个shell脚本(名称可为:姓名全拼_所在学校.sh,如wufang_jiaoda.sh),该脚本的功能如:
1、在屏幕上显示你的大名;
2、在屏幕上显示当前服务器时间。
五、把wufang_jiaoda.sh复制到slave01节点上。

这部分主要包括以下内容:

1、Linux入门
2、MySQL入门
3、Python入门
4、R简介
5、Hadoop入门
6、Spark入门
7、深度学习最火框架TesorFlow简介
8、机器学习简介

******大数据、人工智能入门******

Python基础

这部分主要介绍Python的多种库及其详细使用方法,另外大家也可参考网上有关介绍:
最全数据分析资料汇总(含python、爬虫、数据库、大数据、tableau、统计学等)
Python 开源项目最新月榜TOP 10
Python3 网络爬虫开发实战(崔庆才)
Scrapy 官网
Python2爬虫学习系列教程
廖雪峰基于Python3的全新教程
Python的100多篇经典博客
Python较全的面试题
Python程序设计 600题
Python官网
Python中文开发者社区
Python入门深度学习完整指南

Spark基础

林子雨老师的Spark入门基础

大数据框架

Hadoop官网
Hadoop文档
CDH介绍
华为FusionInsight HD

******人工智能进阶******

Python与人工智能
这部分我们将以Python为主要内容,从获取数据、处理数据,到数据分析、机器学习、深度学习等,以循序渐进的方式展开,其中不乏机器学习、深度学习的一些常用方法、重要技术的介绍,如正则化方法、降维、优化、自动编码、共享参数等内容。深度学习我们介绍TensorFlow、Theano、Keras等架构及卷积神经网络、循环神经网络原理及实例案例。如图像识别、自然语言处理、人情分析、人机对话等实例案例。这部分我们将优先实现。

数学基础

数学基础

机器学习算法

数据挖掘150道笔试题
150多个最好的与机器学习,自然语言处理和Python相关的教程
机器学习入门
机器学习200多篇文章
斯坦福机器学习公开课
Kaggle竞赛(数据集及算法实现)

机器学习常用方法

机器学习常见几种优化方法
机器学习新手必看十大算法

SKLearn基础

Sklearn基础学习
SKLearn的GitHub项目
SKLearn官网

TensorFlow基础

TensorFlow 2.0 你需要了解的一切
TensorFlow及Python系列实战案例
TensorLayer 中文文档
TFLearn很多实例
莫烦博主TensorFlow系列视频
TensorFlow快速入门
TensorFlow入门之基本使用
Tensorflow一些常用基本概念与函数
TensorFlow官网中文翻译网
TensorFlow常用函数
TensorFlow的GitHub项目

深度学习最好切入点--keras

《深度学习框架Pytorch入门与实践》实例代码
Pytorch学习资源大汇总
fastai项目
Keras的GitHub项目
Keras中文文档
Keras基础
caffe2基础

Pytorch基础

Pytorch 0.4版本新功能
Pytorch官网

******R、C\C++、JAVA******

R语言(推荐网站或博客)

R语言官网
R语言中文网
10 种机器学习算法的要点(附 Python和R代码)

Java开发系列(推荐网站或博客)

JavaScript教程
Android开发入门经典实例
在Android上使用TensorFlow

C和C++(推荐网站或博客)

C语言入门
C语言中文网
C++入门教程
C++菜鸟教程

在使用卷积神经网络、循环神经网络等对图像进行深度学习时,一般需要对图像进行预处理,如进行格式转换,TensorFlow一般建议转换为TFRecord格式,它把二进制数据和标签数据存储在同一个文件中,这个格式文件不对数据进行压缩,可以快速加载到内存中。如何把图像转换为TFRecord格式?
在一般参考书上,通过使用TensorFlow自带的函数来生成,如input_data,这个函数一般只限定图像如,MNIST等。如果我们需要处理一般图像,将无法使用该函数。
这里我们介绍一种把图像转换为TFRecord格式的方法,该方法的一般步骤为:

对以上步骤进行详细说明:

1、获取数据
这里找我选了2类小狗的图片, Chihuahua和papillon, 全部 resize成128 * 128大小
保存地址为
/home/hadoop/data/dogs/n02085620-Chihuahua或n02086910-papillon
目录结构如下:

2、制作TFRecord文件
2.1 先讲一下tfrecord, 这是一种将图像数据和标签放在一起的二进制文件,能更好的利用内存,
为存储文件TFRecord
先创建目录: /home/hadoop/data/dogs/output
在tensorflow中快速的复制,移动,读取,存储 等等。
这里注意,tfrecord会根据你选择输入文件的类,自动给每一类打上同样的标签
如在本例中,只有0,1 两类。
2.2 以下是生成TFRecord详细代码:

import os
import tensorflow as tf
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np

filepath="/home/hadoop/data/dogs/"
classes={'n02085620-Chihuahua','n02086910-papillon'} #人为 设定 2 类
writer= tf.python_io.TFRecordWriter("/home/hadoop/data/dogs/output/dogs.tfrecords") #要生成的文件

for index,name in enumerate(classes):
class_path=filepath+name+'/'
for img_name in os.listdir(class_path):
img_path=class_path+img_name #每一个图片的地址

img=Image.open(img_path)
img= img.resize((128,128))
img_raw=img.tobytes()#将图片转化为二进制格式
example = tf.train.Example(features=tf.train.Features(feature={
"label": tf.train.Feature(int64_list=tf.train.Int64List(value=[index])),
'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
})) #example对象对label和image数据进行封装
writer.write(example.SerializeToString()) #序列化为字符串

writer.close()

          tf.Example 协议内存块包含了Features字段,通过feature将图片的二进制数据和label进行统一封装,然后将example协议内存块转化为字符串,  tf.python_io.TFRecordWriter 写入到TFRecords文件中。

 3.读取TFRecord文件

在制作完tfrecord文件后, 将该文件读入到数据流中。

def read_and_decode(filename): # 读入dogs.tfrecords
filename_queue = tf.train.string_input_producer([filename])#生成一个queue队列

reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue)#返回文件名和文件
features = tf.parse_single_example(serialized_example,
features={
'label': tf.FixedLenFeature([], tf.int64),
'img_raw' : tf.FixedLenFeature([], tf.string),
})#将image数据和label取出来

img = tf.decode_raw(features['img_raw'], tf.uint8)
img = tf.reshape(img, [128, 128, 3]) #reshape为128*128的3通道图片
img = tf.cast(img, tf.float32) * (1. / 255) - 0.5 #在流中抛出img张量
label = tf.cast(features['label'], tf.int32) #在流中抛出label张量
return img, label

4.显示tfrecord格式的图片

有些时候我们希望检查分类是否有误,或者在之后的网络训练过程中可以监视,输出图片,来观察分类等操作的结果,那么我们就可以session回话中,将tfrecord的图片从流中读取出来,再保存。

filename_queue = tf.train.string_input_producer(["/home/hadoop/data/dogs/output/dogs.tfrecords"]) #读入流中
reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue) #返回文件名和文件
features = tf.parse_single_example(serialized_example,
features={
'label': tf.FixedLenFeature([], tf.int64),
'img_raw' : tf.FixedLenFeature([], tf.string),
}) #取出包含image和label的feature对象
image = tf.decode_raw(features['img_raw'], tf.uint8)
image = tf.reshape(image, [128, 128, 3])
label = tf.cast(features['label'], tf.int32)
with tf.Session() as sess: #开始一个会话
#init_op = tf.initialize_all_variables()
init_op=tf.global_variables_initializer()
sess.run(init_op)
coord=tf.train.Coordinator()
threads= tf.train.start_queue_runners(coord=coord)
for i in range(20):
example, l = sess.run([image,label])#在会话中取出image和label
img=Image.fromarray(example, 'RGB')#这里Image是之前提到的
img.save(filepath+str(i)+'_''Label_'+str(l)+'.jpg')#存下图片
print(example, l)
coord.request_stop()
coord.join(threads)

代码运行完后, 将在目录"/home/hadoop/data/dogs"下生成带有标签名称一些文件,具体文件名称如下:

[feigu@webserver dogs]$ ll
total 132
-rwxr-xr-x 1 feigu feigu 4886 Jul 27 22:57 0_Label_0.jpg
-rwxr-xr-x 1 feigu feigu 4507 Jul 27 22:57 10_Label_1.jpg
-rwxr-xr-x 1 feigu feigu 3419 Jul 27 22:57 11_Label_1.jpg
-rwxr-xr-x 1 feigu feigu 4886 Jul 27 22:57 12_Label_0.jpg

 

 

Python基础

第1章.Python基础
1.1 Python概述
Python是做啥用的? 为啥要学Python?
前面我们介绍了Linux操作系统、MySQL数据库。Linux作为大数据平台的基石,其重要性,在这里就不说了;MySQL数据库或推广为其他数据库(实际上其他数据库,如Oracle、DB2、SQL server等)作为存储和管理数据系统,在企业中已运行几十年(当然还将将继续使用下去,在事物处理方面尤其独特优势)积累大量各行各业的数据,这些数据是大数据重要来源。平台有了,数据有了,接下来要做的就是处理和分析这些数据,而Python恰恰是解决这类问题的高手之一,可以毫不夸张的说,Python将把你带进一个数据处理、数据分析的五彩缤纷世界!Python将助你达到一个新境界!
当然在数据处理、数据分析、机器学习等方面除了Python还有其他一个工具,如R,MATLAB等,我们为啥选择Python?1、它开源,2,它易学,3,它强大,4,它与时俱进,它和大数据计算平台SPark的结合,可为强强联合,优势互补、相得益彰!Spark后面我们会重点介绍。
1.2 Python简介
Python是一种动态的高级解释性编程语言,简单易学,可读性高,功能强大,目前已广泛用于学校、企业、金融机构等。Python支持面向对象、模块和函数式编程,可以在windows、linux、Unix等操作系统上运行。它有如下主要特征:
 开源:
由荷兰人Guido van Rossum在1989开发的,1991发行第一个正式版本,Python开发的里程碑:
1991年发行Python0.9.0;
1994年发行Python1.0;
2000年发行Python2.0;
2008年发行Python2.6;
2010年发行Python2.7;
2008年发行Python3.0;
2010年发行Python3.3;
2014年发行Python3.4.
从2008年开发有两版本在同时使用,而且这两个版本的代码不是100%兼容的,目前大部分实际使用的一般是Python2.6或Python2.7版本编写。
 跨平台;
Python支持常用的操作系统,如Windows、Linux、Unix和Mac OS,既可在集群,也可运行在一些小设备上。
 面向对象
Python支持面向对象、命令式、函数或过程式编程。
 动态性
Python与JavaScript、PHP、Perl等语言类似,无需预先声明,直接赋值即可。
 缩进感知
Python和大部分编程语言不同,它没有分号、begin、end等标记,使用缩进标记代码块、逻辑块,代替圆括号、方括号或分号等。
 多用途
目前Python已广泛应用于web开发、数据库、图像处理、自然语言处理、网络、操作系统扩展等大型应用程序,也用于高级数据分析、图形处理等领域。
1.3 Python重要库
NumPy:基于Python的科学计算第三方库,提供了矩阵,线性代数,概率统计等的解 决方案。
Matplotlib:用以绘制一些高质量的数学2D或3D的图形。
Pandas:pandas构建在numpy之上,是python下强大的数据分析和探索工具,有类似 SQL的数据增、删、改并带有丰富的数据处理函数。
Ipython:它为交互式和探索式计算提供了一个强健且高效的环境,是一个增强的Python shell,是一个编程、测试、调试非常方便的工具。
StatsModels:注重数据的统计建模分析,它是python有了R语言的味道,它与Pandas 进行数据交互,安装时它依赖pandas。
Scikit-Learn:它是python下机器学习工具包,提供了完善的数据预处理、分类、回归、 聚类、预测和模型分析等数据分析与挖掘功能。
SciPy:主要功能:基于Python的matlab实现
Keras:它包含一种深度学习算法-人工神经网络,基于Theano,可以搭建各种深度学习 模型。
1.4 安装配置
上一节介绍的这些库可以用python的安装管理包Anaconda轻松搞定,Anaconda有Linux、windows平台的,在Linux下安装,先下载管理包Anaconda2-4.0.0-Linux-x86_64.sh,然后在Linux命令行下运行:bash Anaconda2-4.0.0-Linux-x86_64.sh,然后按缺省步骤即可,如果要安装其他库,如scipy,只要运行conda install scipy即可。
Python安装完成后,在命令运行python后,启动Python解释器:

$ python
Python 3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:09:58)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1+2
3
>>> a=10
>>> print(a)
10
>>></p>

退出Python解释器,在提示符下输入quit() 或 Ctrl-D即可。

当然我们也可用IPython,IPython是一种加强的交互式Python解释器,它更加方便、功能更强大、更神奇,它已经成为Python科学计算界的标准配置。启动Ipython,只要在命令行输入ipython。

$ ipython
Python 3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:09:58)
Type "copyright", "credits" or "license" for more information.

IPython 5.3.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.

In [1]: 1+2
Out[1]: 3

In [2]: a=4

In [3]: print(a)
4
In [4]: !pwd   #查看当前目录
/home/feigu

在IPython我们可以进行计算、编程、运行脚本(用%run 脚本)、copy脚本(%paste)、测试脚本、可视化数据等,甚至可以运行shell命令,当然Tab补全功能、回看历史命令等它都有,方便快捷,功能强大,退出IPython解释器,在命令行输入quit或exit或Ctrl+D。后面我们以IPython为主。
继续阅读

Python与深度学习--TensorFlow卷

Python循序渐进学深度学习
第1章、Python基础
1.1函数
1.2异常处理
1.3装饰器
1.4对象简介
第2章、Numpy常用操作
第3章、Pandas基础
3.1 DataFrame简介
3.2 DataFrame数据处理
3.3 可视化
3.4 文件处理
3.5 操作数据库
第4章、Python与爬虫
第5章、数学基础
5.1线性代数
5.2概率与信息论
5.3概率图模型
第6章、Python与机器学习
6.1Sklearn简介
6.2 机器学习常用方法
6.2.1正则化
6.2.2降维处理
6.2.3交叉验证
6.2.4网格搜索
6.2.5流水线
6.3监督学习
6.4无监督学习
6.5常用性能评估指标
6.6客户价值分析实例
第7章 Python与深度学习
7.1前馈神经网络
7.2 theano简介
7.2.1深度学习框架
7.2.2图像识别实例
7.3 keras简介
7.3.1深度学习框架
7.3.2图像识别实例
7.3.3自然语言处理实例
第8章、Python与Spark
8.1PySpark架构
8.2流水线
8.3 交叉验证
8.4 机器学习实例
第9章、Python与图形处理
9.1 如何处理图形
9.2 如何把特征与标签整合
第10章、Python与语言处理
10.1语言处理
10.2 常用模型(tf-idf、word2vec)
10.3 情感分析实例
10.4 聊天机器人
第11章、Python与TensorFlow
11.1TensorFlow简介
11.1.1TensorFlow基础
11.1.2TensorBoard
11.1.3图像与TensorFlow
11.2激励函数
11.3优化方法
11.4自编码
11.5 深度学习问题及解决方法
11.6 卷积神经网络
11.6.1原理
11.6.2手写数字识别实例
11.6.3头像识别实例
11.7 循环神经网络
11.7.1原理
11.7.2情感分析实例
11.7.3人机对话实例
11.8 Tklearn图像识别实例
11.9 Keras图像识别实例
第12章、综合练习
1、人机对话实例:
爬虫获取网络数据集(点评、图像)=>pandas+Spark数据处理=>Pyspark数据可视化=>TensorFlow建立模型=>训练评估模型=>人机对话。
2、人脸识别实例:
数据下载=>加载数据=>数据预处理=>TensorFlow建立模型=>训练评估模型=>人脸识别
附录 A、几种优化方法比较:

contours_evaluation_optimizers
从上图可以看出, Adagrad、Adadelta与RMSprop在损失曲面上能够立即转移到正确的移动方向上达到快速的收敛。而Momentum 与NAG会导致偏离。同时NAG能够在偏离之后快速修正其路线,因为其根据梯度修正来提高响应性

saddle_point_evaluation_optimizers

从上图可以看出,在鞍点(saddle points)处(即某些维度上梯度为零,某些维度上梯度不为零),SGD、Momentum与NAG一直在鞍点梯度为零的方向上振荡,很难打破鞍点位置的对称性;Adagrad、RMSprop与Adadelta能够很快地向梯度不为零的方向上转移。
从上面两幅图可以看出,自适应学习速率方法(Adagrad、Adadelta、RMSprop与Adam)在这些场景下具有更好的收敛速度与收敛性。

如何选择SGD优化器

如果你的数据特征是稀疏的,那么你最好使用自适应学习速率SGD优化方法(Adagrad、Adadelta、RMSprop与Adam),因为你不需要在迭代过程中对学习速率进行人工调整。
RMSprop是Adagrad的一种扩展,与Adadelta类似,但是改进版的Adadelta使用RMS去自动更新学习速率,并且不需要设置初始学习速率。而Adam是在RMSprop基础上使用动量与偏差修正。RMSprop、Adadelta与Adam在类似的情形下的表现差不多。Kingma[15]指出收益于偏差修正,Adam略优于RMSprop,因为其在接近收敛时梯度变得更加稀疏。因此,Adam可能是目前最好的SGD优化方法。
有趣的是,最近很多论文都是使用原始的SGD梯度下降算法,并且使用简单的学习速率退火调整(无动量项)。现有的已经表明:SGD能够收敛于最小值点,但是相对于其他的SGD,它可能花费的时间更长,并且依赖于鲁棒的初始值以及学习速率退火调整策略,并且容易陷入局部极小值点,甚至鞍点。因此,如果你在意收敛速度或者训练一个深度或者复杂的网络,你应该选择一个自适应学习速率的SGD优化方法。
附录B:卷积共享变量示意图
在卷积神经网络中,参数共享是如何体现的呢?卷积核如何移动?,我们从以下图可以看出,比如说 8×8 作为样本,并且从这个小块样本中学习到了一些特征,这时我们可以把从这个 8×8 样本中学习到的特征作为探测器,应用到这个图像的任意地方中去。特别是,我们可以用从 8×8 样本中所学习到的特征跟原本的大尺寸图像作卷积,从而对这个大尺寸图像上的任一位置获得一个不同特征的激活值。
如下图所示,展示了一个33的卷积核在55的图像上做卷积的过程。每个卷积都是一种特征提取方式,就像一个筛子,将图像中符合条件(激活值越大越符合条件)的部分筛选出来

saddle_point_evaluation_optimizers

Python基础11

1.Python基础

1.1 Python概述

Python是做啥用的? 为啥要学Python?

前面我们介绍了Linux操作系统、MySQL数据库。Linux作为大数据平台的基石,其重要性,在这里就不说了;MySQL数据库或推广为其他数据库(实际上其他数据库,如Oracle、DB2、SQL server等)作为存储和管理数据系统,在企业中已运行几十年(当然还将将继续使用下去,在事物处理方面尤其独特优势)积累大量各行各业的数据,这些数据是大数据重要来源。平台有了,数据有了,接下来要做的就是处理和分析这些数据,而Python恰恰是解决这类问题的高手之一,可以毫不夸张的说,Python将把你带进一个数据处理、数据分析的五彩缤纷世界!Python将助你达到一个新境界!

当然在数据处理、数据分析、机器学习等方面除了Python还有其他一个工具,如R,MATLAB等,我们为啥选择Python?1、它开源,2,它易学,3,它强大,4,它与时俱进,它和大数据计算平台SPark的结合,可为强强联合,优势互补、相得益彰!Spark后面我们会重点介绍。

1.2 Python简介

Python是一种动态的高级解释性编程语言,简单易学,可读性高,功能强大,目前已广泛用于学校、企业、金融机构等。Python支持面向对象、模块和函数式编程,可以在windows、linux、Unix等操作系统上运行。它有如下主要特征:

  • 开源:

由荷兰人Guido van Rossum在1989开发的,1991发行第一个正式版本,Python开发的里程碑:

1991年发行Python0.9.0;
1994年发行Python1.0;
2000年发行Python2.0;
2008年发行Python2.6;
2010年发行Python2.7;
2008年发行Python3.0;
2010年发行Python3.3;
2014年发行Python3.4.

从2008年开发有两版本在同时使用,而且这两个版本的代码不是100%兼容的,目前大部分实际使用的一般是Python2.6或Python2.7版本编写。

  • 跨平台;

Python支持常用的操作系统,如Windows、Linux、Unix和Mac OS,既可在集群,也可运行在一些小设备上。

  • 面向对象

Python支持面向对象、命令式、函数或过程式编程。

  • 动态性

Python与JavaScript、PHP、Perl等语言类似,无需预先声明,直接赋值即可。

  • 缩进感知

Python和大部分编程语言不同,它没有分号、begin、end等标记,使用缩进标记代码块、逻辑块,代替圆括号、方括号或分号等。

  • 多用途

目前Python已广泛应用于web开发、数据库、图像处理、自然语言处理、网络、操作系统扩展等大型应用程序,也用于高级数据分析、图形处理等领域。

1.3 Python重要库

NumPy:基于Python的科学计算第三方库,提供了矩阵,线性代数,概率统计等的解                          决方案。

Matplotlib:用以绘制一些高质量的数学2D或3D的图形。

Pandas:pandas构建在numpy之上,是python下强大的数据分析和探索工具,有类似                  SQL的数据增、删、改并带有丰富的数据处理函数。

Ipython:它为交互式和探索式计算提供了一个强健且高效的环境,是一个增强的Python                      shell,是一个编程、测试、调试非常方便的工具。

StatsModels:注重数据的统计建模分析,它是python有了R语言的味道,它与Pandas                         进行数据交互,安装时它依赖pandas。

Scikit-Learn:它是python下机器学习工具包,提供了完善的数据预处理、分类、回归、                      聚类、预测和模型分析等数据分析与挖掘功能。

SciPy:主要功能:基于Python的matlab实现

Keras:它包含一种深度学习算法-人工神经网络,基于Theano,可以搭建各种深度学习                 模型。

1.4 安装配置

上一节介绍的这些库可以用python的安装管理包Anaconda轻松搞定,Anaconda有Linux、windows平台的,在Linux下安装,先下载管理包Anaconda2-4.0.0-Linux-x86_64.sh,然后在Linux命令行下运行:bash Anaconda2-4.0.0-Linux-x86_64.sh,然后按缺省步骤即可,如果要安装其他库,如scipy,只要运行conda install scipy即可。

Python安装完成后,在命令运行python后,启动Python解释器:

退出Python解释器,在提示符下输入quit() 或 Ctrl-D即可。

当然我们也可用IPython,IPython是一种加强的交互式Python解释器,它更加方便、功能更强大、更神奇,它已经成为Python科学计算界的标准配置。启动Ipython,只要在命令行输入ipython。

在IPython我们可以进行计算、编程、运行脚本(用%run 脚本)、copy脚本(%paste)、测试脚本、可视化数据等,甚至可以运行shell命令,当然Tab补全功能、回看历史命令等它都有,方便快捷,功能强大,退出IPython解释器,在命令行输入quit或exit或Ctrl+D。后面我们以IPython为主。

linux系统基础

第1章 Linux简介
1.1 Linux能做啥
Windows能做啥? 可能十个人十个都知道。具体能做啥,就不用我说了。Linux能做啥?为啥有了Windows,为啥还要Linux?
Windows与Linux的关系,打个不很恰当的比喻,如果把Windows和Linux都作为交通工具,Windows就想单车(自行车),Linux就像列车。前者方便易用,并几乎没有轨道的限制,但它只能单用户,能承载的重量有限;后者虽然不像前者方便易用,但它可以多用户,并且可以运送巨无霸的东西!如管理成千上万个节点的大数据集群。关键一点是安全,还免费,更有很庞大的志愿者和使用者,当然,它也有一个不足,必须使用命令方式操作,就像列车务必在铁轨上一样。当然这也是保证其安全所需要的。
1.2 如何学习Linux
学好Linux将大大提升你的竞争力,哪如何学好它呢?是否存在捷径?我觉得有但又没有。
作为初学者,个人建议可以像小孩学英语一样,先学会用,而且是学哪些最常用、最基础、最重要的部分。如果会用,理解起来就方便多了。否则,已开始就学一大堆的语法,效果往往事倍功半。此外,就像锻炼身体、学习英语等一样,贵在实践再实践,“纸上得来终觉浅,绝知此事要躬行”。
另,向大家推荐几个学习Linux的网站:
Linux中国:https://linux.cn
鸟哥的Linux私房菜: http://linux.vbird.org
Linux下载站: http://www.linuxdown.net
Linux公社: http://www.linuxidc.com
《鸟哥的Linux基础学习篇》鸟哥著
1.3 Linux的历史
Linux的前辈是Unix,但可能是由于历史原因,Unix派别林立而且主要用在大型服务器,如IBM的AIX,HP的HP-Unix,SUN公司(目前是Oracle的一部分)的SUN-Unix等等,更要命的一点是他们商业味道很浓。
1991年10月5日, 上午11时53分,有一个名为 Linus Torvalds 的年轻芬兰大学生在 comp.os.minix 这个新闻群组上发表了这样一个帖子,称他以bash,gcc等工具写了一个小小的内核程序,这个内核程序可以在Intel的386机器上运行,这引起很多人的兴趣,由此标志L inux的诞生。
当初还是个大学生的 Linus 大概完全没想到当初被他视为个人兴趣的程式,在几年以后会有超千万个使用者,由他自己带头开发的操作系统现在已经在世界各地受到普遍的欢迎。
1.4 Linux的优缺点
Linux继承了Unix 稳定有效率的特点。网路上安装 Linux 的主机连续运做一年以上而不曾宕机、不必关机是稀松平常的事,不过Linux却不像一般Unix要负担庞大的版权费用,也不需要在专属的昂贵硬体上才可以使用;Linux可以在一般的 i386 PC 上执行,效能又高,自然而然的接收了过去几十年来在Unix上累积的程式资源跟使用者,加上GPL的版权允许大家自由散发Linux的原始码,并针对自己的需求修改程式,使得Linux在目前已经成为非常受人欢迎的一个多用户、多任务、免费、稳定、效率高、可以在众多不同电脑系统平台上执行、拥有亿万粉丝的操作系统。当然,Linux也不是完美无缺的,它的一个最大缺点就是需要使用“命令行”来操作和管理,尤其是对初学者,不过在“安全第一”的今天,这个缺点或许又是保证其优点的重要方面。
1.5 Linux的发行版本
Linux基于Debian、Red Hat和SUSE源的那些发行版本在生产环境中占据优势地位,以下为目前Linux比较流行的版本。

表1.1 Linux流行版本

对Linux而言,内核是其心脏,与硬件打交道,开机后留住在内存 ,而Shell为内核与应用程序间的桥梁,内核及shell构成Linux的主要内容,应用程序通过shell(或命令行)与服务器进行交互,shell调用内核(Kernel)来利用和管理服务器硬件资源,应用程序(如用户自己编写shell脚本等,office是windows操作系统的应用程序)保存在硬盘,需要时调入内存,它们之间的关系可用下图形表示:
(图1-1 Linux操作系统结构图)

 

1.6 登录系统
前面讲了Linux是个非常重要、功能强大、而且有意思的系统,而且还提到需要在命令行操作,不像使用windows一样,只需要按按鼠标即可,命令行操作确实不方便,尤其是对大多数用惯了windows的人来说。这确实有个‘痛苦’的过程,但是windows一般只能用来写写文档、上上网、打打游戏、看看电影之类,企业生产环境大都采用非windows环境,如Linux或Unix,因此对于大多数希望从事IT相关行业者,尤其进入大数据这个朝阳行业的人来说,是一道必需迈过的坎。
万事开头难,实际上你只要用过一段时间,将会慢慢喜欢上它,它有很多你想不到的优点,如tab补全功能。
如何登录Linux系统呢?如果Linux就在本地机器上,只需要启动Linux即可;如果Linu服务器不在本地,那也很简单,只要在你的客户端安装一个连接Linux的客户端(如Putty或Xshell等,这些在网上都可免费下载),然后做一个简单配置就可以了,下面我们以登录远程Linux服务为例(注:本例中Linux已安装好,并创建一个用户:conn_linux):
第一步:安装Xshell 5
从网上下载一个Xshell 5,安装基本按照缺省情况即可
第二步:打开Xshell 5,点击文件菜单,然后点击新建,进入下一个界面

第三步:点击连接,配置服务器的IP,端口,如下图:

第四步:点击用户身份验证,配置用户及密码信息,如下图:

第五步:点击确认按键,所有配置完成,最后在会话界面将出现刚创建的连接,名为conn_linux,如下图。

第六步:点击连接,如果IP,用户密码等都正确的话,进入Linux系统,如能看到如下类似界面,祝贺你,你已经迈入Linux大门了!

Xshell 5是连接Linux服务器的工具之一,还可以通过Putty等工具,配置和使用也很方便,此外大家还可以利用Xftp 5工具来上传或下载Linux服务器上的文件,它的安装和配置比较简单,网上有很多这方面的文档,这里就不再展开来说了。
1.7 登录后,试试身手
登录Linux后,可以用一些简单命令,试一试Linux这个听起来有点神秘的操作系统。

注意,linux是区分大小写的或称为大小写敏感,所以pwd和PWD是不同的。
Linux系统要经常输入命令,而且有些命令或文件还比较长,如果要一个一个输入,不但麻烦,还容易出错。Linux是否有更有效的方法呢?答案是肯定的,那就是利用Tab键!它具有命令补全或文件补全的功能,这也是Linux很有趣的地方之一。当然,Linux为尽量减少你敲键盘次数,还有更强大的地方,如编写shell脚本,这个后面我们会详细介绍。
先看几个使用Tab键来偷点懒。
假如我们想进入homework1130这个目录,是否要cd homework1130?如采用Tab补全功能,在大大减少输入量的同时,还可大大提高你的准确率。具体实现方法为:输入目录名称的前几个字符即可,然后按Tab,系统自动补全该目录名称,具体操作如下图:


按Tab键后得如下界面

类似的神奇,Linux还有很多,如通过上下方向键(↑↓),可以调出前面输过的命令,输过了的命令可以不用重新输入了,这个功能也不错,顺便提一下,这些功能在MySQL数据库也有,讲MySQL时我们会介绍,看来好东西,大家都喜欢。
1.8 shell种类
shell是Linux内核与应用程序间的桥梁,起着非常重要的作用,shell由多种,如bsh(或sh),csh,ksh,bash等,这些shell基本内核对差不多,但还是有不少不一样的地方,所以我们在编写shell程序时,首先需要说明使用哪种shell来解释你程序,具体做法就是在shell程序第一行说明,如#!/bin/bash,这些内容大家先了解一下,后面我们会详细介绍,它们间关系大致如下:
(图1-2shell种类及关系图)

shell有多种,每个连接用户系统一般会先给个缺省shell(当然缺省shell可以修改),哪里可以看到你的缺省shell呢?查看文件/etc/passwd,或echo $SHELL 可以看到用户使用的缺省shell,cat /etc/shells可以看到目前已安装的shells。如何查看这些文件,后面将介绍。

第2章 文件与目录
2.1 文件与目录简介
说到文件和目录,我们从大家熟悉的windows开始,打开windows有关界面,我们通常能看到如下类似界面:
(图2-1 windows的目录及文件)

第[1]列为文件及目录名称:是文件还是目录,从图标很容易看出,其中有些为系统目录有 些是用户创建目录或文件。
第[2]列为修改时间;文件或目录的修改或创建时间。
第[3]列为类型:显式说了是文件夹还是文件,甚至为那种类型的文件。
第[4]列为文件大小,如果是文件,会显示其大小,单位为KB。
那Linux系统的目录及文件,又是如何排兵布阵的呢?windows进行下级目录,或上级目录只需要点击相关目录或到上级目录的方向图标,在Linux系统中如何进入下级目录或上级目录,如何打开文件?实际上在Linux系统虽然需要使用命令,但使用起来也很方便,而且在安全性、易用性方面更有独到的地方,目录的切换使用cd命令,查看当前目录或文件可以使用ls命令,详细操作我们如下实例。
(图2-2 Linux下的目录、文件及权限等)

用ls -la(,注意ls 与-la间有一个空格,l(list)及a(all)为两个参数)可以查看当前目录下的文件或目录的详细信息,包括列出隐含文件(文件名前为.,如上面的.bashrc文件)。
与windows的内容基本相同,windows的第[1]、[2]、[3]、[4]列分别对应Linux的第[7]、[6]、[1]、[5],虽然没有windows这么直观,但包含更丰富的信息。理解这些信息非常重要,以后经常提到这些相关内容,下面我们对上面7列代表的含义逐一进行说明。
 第[1]列:表示文件类型及权限,如果你仔细观察一下,会发现这列共有10个字符,
这10个字符的位置及含义,我们把它放大一下,如下图:
(图2-3文件及目录权限说明图)

第1字符为d或-,d表示对应是目录,-对应是文件;
第2,3,4字符,如[rw-]、[rwx],表示用户或文件所有者的权限,r(read)表示有读的权限,w(write)表示有写的权限,x(execute)表示有执行权限,-表示没有对应权限。[rw-]
表示文件所有者有读、写权限,但没有执行权限。
第5、6、7字符,表示文件所属的用户组的权限。
第8、9、10字符,表示其他用户(有点像windows中guest用户)的权限。

 第[2]列:表示文件或目录的连接数,如果目录至少为2,因任何一个目录至少包括.及..
两个目录,.表示当前目录,..表示上级目录,故如何我们需要切换到上级目录,可以cd ..
即可。
 第[3]列:表示文件或目录的所有者账号
 第[4]列:表示文件或目录所属用户组,如上面列出feigu feigu,说明所属用户为feigu,所属用户组也是feigu(这里两者同名,当然两者可以不同名)。
 第[5]列:表示文件大小,缺省单位为B(非KB)。
 第[6]列:表示文件文件或目录的创建或修改时间。
 第[7]列:表示文件或目录名称,如果文件前多一个点的表示隐含文件,隐含文件只有加上a这个参数才会显示出来,否则不会显示出来。
以上这些权限或属性当然不是一成不变的,可以通过命令修改,如何修改2.3节将介绍。

ls命令的使用方法还有很多,我这里介绍几种常用方法:

ls -lt ####结果按时间排序,降序。
ls -l ####结果按名称排序
ls -lS ####按文件大小排序,注意S为大写
ls -ltr ####结果按时间排序,变为升序,增加参数r(reverse),表示与缺省排序方式相反。

看到这里,可能有很多朋友不耐烦了,这么多命都要一一去记吗?其实完全不必要去死记忆,Linux有一个类似windows中help工具,他就是man这个命令,这个man非常强大,不过其来源是manual,而非男人哦。使用起来很方便,其格式为:man [command] ,显示结果中有各参数的含义、示例等。
2.2 切换目录
Linux的存储都是以目录的方式存在的,所以了解其目录结构、主要目录的含义非常重要,在不同目录间切换是Linux人员做得最多的事情之一,既然是经常干的活,是否有些妙招呢?有但又没有,关键还是要多练,“熟练才能生巧”。
我们先来看一看Linux的主要目录及用途:

(图2-4 Linux主要目录结构图)

切换目录使用cd命令,如果要到根目录(Linux系统最高目录),可以cd / 即可;如果切换到上级目录,使用cd ..;如果要回到用户主目录,用cd ~ 。
查看当前你所在的目录,使用pwd命令;查看当前目录下的有哪些文件或目录,使用ls命令。
.表示当前目录,也可用./表示;..表示上级目录,也可用../表示。
由根目录(/)开始的路径成为绝对路径,如:/home/feigu/hadoop;相对于当前路径的称为相对路径,如:./hadoop 、~/spark等。
2.3 改变文件或目录属性和权限
”安全第一“,这句话用在操作系统方法同样适合。我们知道windows也有权限设计,它主要通过用户或角色的设置,没有细到文件和目录的,比较粗放,因毕竟它是PC机,自个用的没必要搞得那么复杂。Linux系统一开始基于互联网的,所以其安全设计方面做得好。
在第2.1小节,通过ls -la命令我们可以看到其权限的大致情况,可精准到目录和文件,对使用者分为所有者、用户组、其他人;权限又有r、w、x等。权限这部分迷惑了不少初学者,时常被“Permission denied”搞得头大,实际上问题就出在权限设置方面,这是一个坎,但过了这个坎,你就前进一大步了!
其实这个问题,说难也不难,掌握如此细腻的东西,耐心是最好的武器。下面我们来逐一分解。
由于Linux是多用户、多任务的系统,有时可能出现几十、几百个人同时连到一台服务器,甚至还有考虑一些不速之客,所以对如此多用户就需要进行划分了,自己应该就是所有者,其他人可以分成几个组,如开发组、测试组、业务组(你可以是组员之一、也可以不在这些组),在这些组之外,再设置一个others(其他人)。root拥有Linux系统最高权限,就像windows中administor,我们日常使用时,出于安全考虑最好选用其他用户。
使用文件或目录者分为三种身份(所有者、用户组、其他人)),权限分为r、w、x,这些权限对文件和目录的作用是否相同呢?下面我们来仔细分析分析一下:
 权限对文件的影响
r-- 可以读取文件内容
w--可以修改、新增文件内容,但不能删除文件或创建文件;
x--可以执行文件,如shell脚本等其他程序。
r、w、x一般是针对文件内容的
 权限对目录的影响
r-- 可以查询该目录下的文件或目录,如可以使用ls等命令。
w--可以新建或删除目录和该目录下文件、可以重命该目录及其下的文件等。
x--可以把当前目录作为工作目录,如可用cd命令进入该目录。
上面对用户身份及对应权限做了一些介绍,下面我们来回到这节的主题,如何修改文件或目录的属性和权限呢?比较简单,主要用到以下三个命令:
 chgrp: change group的简称,修改目录或文件所属用户组。
 chown:change owner的简称, 修改目录或文件所属所有者
 chmod:change mode的简称,修改目录或文件权限。
 chgrp的命令格式:chgrp [-R] 用户组 文件或目录
其中参数-R,表示可递归修改该目录及其所有子目录下的文件、 目录,使用该命令一般需要root权限,变更时涉及/etc/group 系统文件。
 chown的命令格式:chown [-R] 所有者 文件或目录
或chown [-R] 所有者:用户组 文件或目录。变更时涉及 /etc/passwd系统文件。
执行该命令一般需要root权限,以下为一个示例:

# ls -l
total 16
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
-rw-rw-r-- 1 feigu feigu 31 Oct 16 11:12 install.log
-rw-rw-r-- 1 feigu feigu 1146 Oct 16 11:11 select.sh
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
root@slave001:/home/feigu/linux_test# chown -R hadoop:hadoop shell_script/
root@slave001:/home/feigu/linux_test# ls -l
total 16
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
-rw-rw-r-- 1 feigu feigu 31 Oct 16 11:12 install.log
-rw-rw-r-- 1 feigu feigu 1146 Oct 16 11:11 select.sh
dr-xr--r-- 2 hadoop hadoop 4096 Oct 17 14:32 shell_script

 chmod的命令格式:chmod [-R] xyz目录或文件,其中权限xyz有两种表达方式,一种是字符,r、w、x;还有一种是数据4、2、1,字符和数据间有个对应关系
(表2-1 权限两种表达方式)

所有者名称与简称的对应关系:u代表user(owner),g代表group,o代表others,此外对权限的操作方式有:+表示加入权限,-表示除去权限。
在2.1节提到了文件或目录的权限,涉及权限部分共有9个字符,如[rwxr--r-x],每三个字符为一组,
第一组对于所有者权限[rwx],
第二组用户组权限[r--],
第三组为其他用户权限[r-x],
用字符来赋权限,上面这个我们可以写成:chmod u=rwx,g=r,o=rx test.sh
这种表示方法有个好处是直观,但有时比较麻烦。
如果用数字该如何表示呢?用三位数来表示,
第一位表示所有者权限,如上面的[rwx]对应数字为[4+2+1]=7,
第二位表示用户组权限,如上面的[r--]对应数字为[4+0+0]=4
第三位表示其他用户权限,如上面的[r-x]对应数字为[4+0+1]=5
所以[rwxr--r-x]用数字表示就是745
赋权语句就是:chmod 745 test.sh,以下是该示例

$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
dr-xr--r-- 2 hadoop hadoop 4096 Oct 17 14:32 shell_script
-rw-rw-r-- 1 feigu feigu 62 Oct 17 22:59 test.sh ####修改前
feigu@slave001:~/linux_test$ chmod 745 test.sh ####修改权限
feigu@slave001:~/linux_test$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
dr-xr--r-- 2 hadoop hadoop 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh #####修改后

2.4 文件或目录管理
前面我们介绍了Linux的目录结构、如何查看目录、如何修改文件或目录的属性和权限等内容,当然我们学Linux肯定不是来看看的,我们还想做一些实际事情,如创建文件或目录,拷贝文件,删除文件或目录等,这些任务在windows里相信大家都比较熟了,在Linux中该如何做呢?操作方便吗?不但方便还非常有趣,下面我们就来谈谈这方面的问题:
切换目录上面我们讲到了,利用cd命令即可,我们这里再小结一下:

cd /home/feigu/hadoop ####切换到指定目录
cd ~ ####切换到用户主目录,即用户一开始登录进入的目录
cd .. ####切换到上级目录
cd / ####切换到根目录
cd - ####切换到上一次使用的目录

查看目录可以用pwd,ls等命令。下面我们谈谈如何创建目录,用mkdir命令即可,其功能比windows前很多哦。其命令格式为:
mkdir [-p] 新建目录
-p ####可以递归创建目录,如果上级没有的系统将自动创建。示例如下:
 不用参数,创建单个目录

$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
$ mkdir mydirect ###创建新目录mydirect
$ ls -l
total 16
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect ###创建成功
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh

 使用-p参数,同时创建多级目录

$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
$ mkdir -p test1/test2 #同时创建两级目录
$ cd test1 #一级
feigu@slave001:~/linux_test/test1$ ls -l
total 4
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:26 test2 ###test1的子目录

删除目录的命令及其格式为:rmdir 目录名称
注意用rmdir命令删除目录时,需要该目录为空目录(即该目录下没有文件或子目录),否则,会报错。如果该目录下有很多文件和目录,岂不要逐一删除?是否有更强的命令?
有的,接下来这个命令就可。
rm [-fir] 文件或目录
-f ###f就是force之意,不提示,对不存的目录或文件也不报错或警告
-i ###i 就是interactive之意,删除前会询问是否删除
-r ###r就是recursive之意,即递归删除,这个非常强大,但也需非常谨慎使用。

$ ls -l
total 20
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
drwxrwxr-x 3 feigu feigu 4096 Oct 18 14:00 test1
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
$ rm -rf test1 ###删除test1及其子目录test2
$ ls -l
total 16
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh

与windows一样,对一些不满意的目录或文件,或根据实际情况,我们可重命名目录或文件名称,具体可使用mv命令,其格式为:
mv [-fiu] 原目录或文件 目标目录或文件.
其实该命令也常用来移动目录或文件,就像windows中移动文件或目录一样。
目录或文件除了可以移动,当然可以复制,windows中有的,Linux也有。cp命令可以很方便用来复制文件或目录,其命令格式为:
cp [-fipr] 原文件或目录 目标文件或目录

-f ###强制执行
-i ###如目标文件已存在,则会提示是否覆盖,覆盖按y,否则,按n
-p ###同时把文件或目录权限一起复制过去,而不仅仅默认权限或属性
-r ###递归复制,这个参数很给力
以上是一些常用参数,其实cp参数还有很多,大家可以通过man去了解更多参数的使用。
创建目录可以用mkdir,哪创建文件呢?创建文件比较简单,可用touch 文件名。当然也可用vi或vim等方法,vi或vim后续我们会讲到。

$ ls -l
total 0
###用touch命令,创建一个名为myfile.txt的空文件
$ touch myfile.txt
$ ls -l
total 0
-rw-rw-r-- 1 feigu feigu 0 Oct 24 17:31 myfile.txt

【延伸思考】
如果要在两台服务器间复制文件,该如何操作呢?有兴趣可以考虑一下,没有兴趣的可跳过]
2.5 查看文件内容
前面我们谈了如何查看当前目录(pwd命令)、用ls命令查看当前目录下有哪些文件或目录,如何查看文件内容呢?在windows中我们要看一个文件需要打开才能看到,在Linux中,查看文件灵活的多,而且有趣的多。如可以不open也可查看,而且还可以顺着看,也可倒着看,等等,下面我们就来讲讲如何查看文件内容。
查看文件内容,我们可用的命令很多,常用的有:
cat ###从第一行开始查看,一次打开,如果文件较大时,建议采用其方法。
tac ###从最后一行开始看,tac啥意思?其实它没意思,就是cat的倒写。
more ###逐页地看,按回车,继续查看,按q退出。
less ###与more类似,但可以往前翻(按PgDn)。
###/ 字符串,可向下查询字符串
###?字符串, 可向上查看字符串
###n、N可继续查看以上字符串
###q退出。
head ###查看前几行
tail ###查看后几行
最后2个命令可指定查看头几行或最后几行,命令格式为:
head -n 数字 文件名称 ####没有参数,缺省查看前10行
tail -n 数字 文件名称 ####不带参数,缺省查看最后10行
以下为示例:

$ head -n 5 stud_score_bak.csv ###查看前5行
stud_code,sub_code,sub_nmae,sub_tech,sub_score,stat_date
2015101000,10101,数学分析,,90,
2015101000,10102,高等代数,,88,
2015101000,10103,大学物理,,67,
2015101000,10104,计算机原理,,78,
feigu@slave001:~$ tail -n 3 stud_score_bak.csv ###查看最后3行
2015201010,20104,概率统计,,96,
2015201010,20105,汇编语言,,91,
2015201010,20106,数据结构,,87,

2.7 文件压缩与打包
以下列出Linux一些常用的压缩解压、打包解包的常用命令,供大家参考:

第3章 Shell脚本
3.1 vim编辑器
vim编辑器是啥东西?它有哪些功能?为何要用vim?在windows中我们要编辑一个文件,可以用word、文本编辑器、excel等,如果要说这些编辑器哪个更像vim,那就是文本编辑器了,它们都不支持图片,但vim比文本编辑器功能上强很多,如支持语法高量、远程编辑、崩溃后文件恢复、与vi(vim是vi的加强版,vi也是很多Linux自带的编辑器)兼容等等,所以vim通常作为程序编辑器来使用。
刚开始用vi或vim来编辑文件或脚本时,大多数人都不习惯,这很正常,就像我们穿一双新鞋一样,大都有个磨合期。对于用惯word的朋友,在word中大部分动作基本用鼠标就可搞定,但在vi或vim中鼠标好像不起作用,Linux靠命令驱动,vi或vim也不例外,不过它有很多优点,时间久了你就可慢慢体会到。
好闲话少说,下面我们开始介绍vi或vim的使用方法,用vi或vim来编辑文件,操作上几乎一样,而且两者相互兼容,下面以vim为例来进行说明。
vim的使用分为三种模式,即普通模式、编辑模式、低行模式,了解这三种模式非常重要,实现这三种模式的转换很简单,就是按Esc键。
 普通模式
用vim打开文件时就进入了普通模式,在这种模式中,可使用方向键移动光标,也 可以删除、复制、粘贴字符
 编辑模式
顾名思义,在这种模式中,可以对插入、删除字符,在普通模式下,按下i或o或a之后,马上就进入到编辑模式,注意前提是在普通模式下。如果保证当前模式为普通模式,有个诀窍,多按几次Esc键。
 低行模式
在普通模式下,按“:或/或?”,就进入低行模式,为何叫低行模式?因此时光标会自动跳到低行,在低行模式下,可以进行保存、退出、查询等操作,这里同样要注意其前提,如何保证当前为普通模式,多按几次Esc键。
以下是vim三种模式的转换关系


(图3-1 vim三种模式间的转换图)

以下通过几个实例来说明:
(1)普通模式,利用vim或vi打开一个文件,处于普通模式

(2)在普通模式基础上,按i或a、o键,将进入编辑模式,在编辑模式下,用户可对文件进行增删改等操作。

(3)在编辑模式时按“Esc”键,将返回到普通模式,在普通模式时,按冒号(:),斜杠(/)或问号(?)将进入低行模式,低行模式可对文件进行保存退出、查询等操作。:wq!表示保存并强制退出, :q! 不保存强制退出。

以上这些命令是最常用、最基础的,不过还有很多命令,而且还很多功能不错也很有趣的命令,大家可以从man或网上找到。
[注意:如果不正常退出或编辑时正好断网断线等异常情况,vim将在当前目录下创建一个扩展名为swp的暂时文件,利用这个文件可以用来恢复文件,恢复后可以删除该文件,否则下次再编辑该文件时,vim还会提示你是否要恢复或只读进入方式打开文件等。]
3.2 shell变量
说到变量相信大家都不陌生,像X,Y,Z都可以称为变量,与变量相对的就是常量,如a,b,c,2,3等等。shell的变量有不少特殊的地方,首先shell变量无需声明类型(顺便提一下:这点与Java、C、C++等不同,但与python 相似),下面具体讲讲shell变量的特点:
 变量的赋值:
格式为,变量名=变量值,
如,V1="abc"
字符串可以使用双引号"",和单引号'',但两者间有些区别,用双引号可以保留字符含 义,单引号将视特殊字符为一般字符,具体下面举例说明。
 变量的使用
需要在变量前加上$,如$v1 或${v1}。
 变量的显示:
echo $v1,下面通过一个示例说明双引号与单引号的区别:

$ v1="numpy"
$ v2="import $v1" ###双引号中含特殊字符$
$ echo $v2
import numpy ###双引号保留了$的特殊含义
$ v3='import $v2'
$ echo $v3
import $v2 ###单引号内的$被作为一般字符

 环境变量
在windows有环境变量,如何设置windows环境变量?Linux的环境变量如何设置?环境变量有何作用?
这部分我们来介绍Linux环境变量问题,首先我们看一看windows环境如何设置环境变量问题,在windows的我的电脑=〉属性=〉高级属性设置=〉高级系统属性中有一个“环境变量”设置菜单,点击这个菜单就看到类似如下界面:


(图3-2 windows环境变量设置界面)
里面就是windows环境变量,如PATH、JAVA_HOME、MYSQL_HOME等等,这些环境变量有何作用呢?它的作用不小,给我们提供很多方便,具体作用还是举一个例子来说吧,如果我们想在cmd下运行命令mysql,而该命令所在路径不在环境变量PATH中,我们只有在mysql所在目录运行,否则将报错;如果在环境变量中添加了执行程序mysql所在路径,如:C:\MySQL\MySQL Server 5.5\bin,,那么在cmd下运行mysql命令时,系统将自动在这些目录中查找是否存在该命令,因在path或PATH中添加了,故你可以在任何目录运行该命令。环境变量系统有些时安装时自动生成的,有些需要手工添加。
理解了windows的环境变量,实际也基本了解了Linux的环境变量及其作用,只是设置存储环境变量的方式不同而已。那么如何在Linux系统查看环境变量?如何设置?下面就这些类似问题进行说明。
 查看环境变量
用env(environmnet的缩写)命令查看当前用户可用的环境变量
 常用的环境变量
PATH、HOME、LANG、SHELL等等,如果我们运行的一些命令不在PATH环境变量,运行该命令是,有可能报找不到该命令。
 设置环境变量的几个文件及作用范围


(图3-3 Linux环境变量设置文件及其关系)

[注:/etc下配置文件为所有用户,~下配置文件只作用于当前登录用户,如果要使修改后的配置文件立即生效,记得用”source 配置文件“命令运行该文件]
 环境变量与export
环境变量在某些场合又成为全局变量,我们自定义的变量一般是局部变量,如何使局面变量成为全局变量(如:父进程、子进程都有效)?在局面变量前加上关键字export,下面通过一个实例来说明export的作用。
我们这里有两个简单shell脚本:test1.sh和test11.sh,test1.sh调用test11.sh
详细脚本如下:

$ cat test1.sh
#!/bin/bash
##test export

v1="Linux shell"
v2="spark RDD"

echo "显示变量V1的值: "$v1
sh test10.sh ###调用test10.sh这个脚本,希望获取v2变量的值
#####################################
$ cat test10.sh
#!/bin/bash
#test export
echo "获取test1.sh中变量v2的值: "$v2

运行test1.sh脚本,看能否获取到变量v2的值?
$ sh test1.sh
显示变量V1的值: Linux shell
获取test1.sh中变量v2的值: ####没有获取到变量v2的值
下面我们对test1.sh做一个小小的改动,即在变量v2前,加上export 关键字,其他不变,

$ cat test1.sh
#!/bin/bash
##test export

v1="Linux shell"
export v2="spark RDD" ###添加export关键字

echo "显示变量V1的值: "$v1
sh test10.sh ###调用test10.sh这个脚本,希望获取v2变量的值
再运行test1.sh,看test10.sh是否能获取v2变量的值?
$ sh test1.sh

显示变量V1的值: Linux shell
获取test1.sh中变量v2的值: spark RDD ###这次获取到了v2变量的值!
通过以上实例说明了,在局部变量或自定义变量前加上export,就马上变成全局变量,或子程序(或子进程)也可获取父脚本(或父进程)中的变量!所以,在变量设置中记得添加export。它将使你的程序更强大!
 几个特殊又常用的变量
(表3-1 特殊变量)

这些变量的使用或作用我们后面将讲到,大家先有个了解。
3.3 数据流重定向
数据流重定向,这个概念好像有点陌生,但相关的事我们可能天天都在做,如在windows系统中编辑word文档,首先我们敲击键盘,把键盘上的字符输入到电脑,然后电脑把结果(或称为数据)输出到屏幕,为了以后可以重用、或给其他人,我们通常会把屏幕结果保存为文件或输出到打印机,实际上,这时我们完成了一个数据流重定向任务(即把输出到屏幕上的文字改为输出到文件或打印机等)。
数据流重定向,在Linux系统大同小异,如我们把键盘上的ls字符(或称为数据),输入到Linux系统,回车执行该命令,然后系统把结果数据输出到屏幕(缺省),如果有错误等信息也输出到屏幕,因正确和错误信息都输出到屏幕,有时造成一定的混乱,是否有方法把两者分开输出呢?有的,这里就涉及到数据流重定向过程。我们可把正确信息输出到一个文件,把错误信息输出到另一个文件,这是一个典型的数据流重定向问题。
Linux数据流重定向整个流程可图形化为:
(图3-3 数据流重定向图形)

了解了数据流重定向的概念,理解它的意义就不难了。下图为数据流重定向的一般流程。

 

(图3-4命令执行的数据流向图)

其中标准输入(Standard input)可能比较好理解,说白了就是键盘或文件,命令(command)也不难理解,就是shell中一些命令,如ls、pwd、mkdir等等,标准输出(Standard ouput)可理解为执行命令后的正确信息,而标准错误(Standard error)输出就是命令执行失败的信息。
标准输入输出操作符的一些约定:
1. 标准输入(stdin):代码为 0 ,操作符有: < 或 << 2. 标准输出(stdout):代码为 1 ,操作符有: > 或 >>
3. 标准错误输出(stderr):代码为 2 ,操作符有: > 或 >>

 shell遇到“>” 操作符,会判断右边文件是否存在,如果存在就先删除,并且创建新文件。不存在直接创建。 无论左边命令执行是否成功。右边文件都会变为空。
 shell “>>”操作符,判断右边文件,如果不存在,先创建;如果存在,则追加。
 shell “<”或“<<”操作符,与上面含有基本相同,只是操作其左边的文件。 下面举例说明数据流重定向及相关代码的应用:

 $ ls -l >lstest.txt ###把ls -l结果保存到文件
###find为查找命令,即在目录/home/feigu/linux_test下查找文件lstest.txt
$ find /home/feigu/linux_test/ -name lstest.txt
/home/feigu/linux_test/lstest.txt
###把查找到的正确结果(代码为1)添加(操作符为>)到文件findstdout.txt
$ find /home/feigu/linux_test/ -name lstest.txt 1>findstdout.txt
$ cat findstdout.txt
/home/feigu/linux_test/lstest.txt
$ find /home/feigu/linux_test11/ -name lstest.txt 1>findstdout.txt
find: <code>/home/feigu/linux_test11/': No such file or directory
###把正确结果(代号为2)添加(>)到文件findstderr.txt
$ find /home/feigu/linux_test11/ -name lstest.txt 2>findstderr.txt
feigu@slave001:~/linux_test$ cat findstderr.txt
find: </code>/home/feigu/linux_test11/'
: No such file or directory
###把正确和错误信息同时((代码表示为:2>&1)追加(操作符为>>)到findstderr.txt
$ find /home/feigu/linux_test11/ -name lstest.txt >>findstderr.txt 2>&1
###把正确和错误信息存放到一个垃圾桶黑洞(/dev/null)里
$ find /home/feigu/linux_test11/ -name lstest.txt >/dev/null 2>&1

【延伸思考】
1、>或>>缺省会输出标准错误信息吗?即,不指明输出是1或2,或2>&1。
2、>或>>输出到文件后,屏幕上就看不到了;是否有方法能同时输出到文件和屏幕?
3.4 管道与过滤
过滤我们都不陌生,Linux也有管道?它长得啥样?有啥作用?不过我们日常生活中不少管道,如排水管道,把管道接前后连接,给我们带来很多便利和1+1>2的功效。我们知道管道的一个基本事实就是,前个管道的流出也就是后一个管道的流入。Linux中我们可以把一个命令看成一个管道,如果把两个的命令前后连在一起,那么前一个命令的输出就是后一个命令的输入,从这一点看,确实非常像管道,如下图:
图3-5 管道示意图

在Linux中管道用竖线(|)表示,其作用如何,我们还是让实例说话吧。

$ ls -l
total 28
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
drwxrwxr-x 2 feigu feigu 4096 Oct 18 14:27 shell_script
-rw-rw-r-- 1 feigu feigu 3 Oct 18 14:28 te1.txt
-rw-rw-r-- 1 feigu feigu 3 Oct 19 08:31 tel.py
-rwxr--r-x 1 feigu feigu 82 Oct 18 23:45 test_bak.sh
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
$ ls -l|grep "drw" ###过滤出ls -l中的目录
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
drwxrwxr-x 2 feigu feigu 4096 Oct 18 14:27 shell_script

这个结果其实不用管道也可实现,如:
第一步把ls -l的结果导入到文件中
第二步查找这个文件
但用管道,一步就搞定,而且逻辑非常清晰和直观!
其中grep为一个很有用的过滤命令,下面我们会重点介绍。

Linux中常用的过滤或查找命令有:
 grep命令
grep 是一个强大的文本搜索工具,可以使用正则表达式(下节讲到),并返回匹配的行, 语法为:
grep [-cinv] '匹配字符串' filename
如果不指定file参数,grep命令将读取标准输入,如上例中的ls -l显示结果。“grep”源 于 ed(Linux的一个行文本编辑器)的 g/re/p 命令,g/re/p 是“globally search for a regular expression and print all lines containing it”的缩写,意思是使用正则表 达式进 行全局检索,并把匹配的行打印出来。
grep 命令有很多选项,比较常用的有:
-c ###输出匹配的总行数。
-i ###不区分大小写进行匹配。
-n ###输出匹配的行以及行号。
-v ###反转查询,输出不匹配的行。例如,grep -v "test" demo.txt 将输出不包含"test" 的行。
举例说明:

$ ls -l
total 20
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
drwxrwxr-x 2 feigu feigu 4096 Oct 18 14:27 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
-rw-rw-r-- 1 feigu feigu 38 Oct 21 15:00 Test.txt ###这个文件有个大写的T
feigu@slave001:~/linux_test$ ls -l|grep 'test' ####区分大小写
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
feigu@slave001:~/linux_test$ ls -l|grep -i 'test' ###带参数i,表示不区分大小写
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
-rw-rw-r-- 1 feigu feigu 38 Oct 21 15:00 Test.txt
feigu@slave001:~/linux_test$ ls -l|grep -in 'test' ###同时输出行号
5:-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
6:-rw-rw-r-- 1 feigu feigu 38 Oct 21 15:00 Test.txt

 cut命令
cut是一个选取命令,以行为单位,用指定分隔符将行切分为若干字段,选取所需 要的字段。命令格式为:
cut -d '分隔符' -f 字段 filename
cut -c 字符范围 file
如果不指定file参数,cut 命令将读取标准输入。
使用实例:

$ cat Test.txt ###该文件是以冒号为列分隔符
hadoop:hbase:hive
Spark:RDD:DataFrame
$ cat Test.txt |cut -d ":" -f 2 ###取第2列
hbase
RDD
$ cat Test.txt |cut -d ":" -f 2,3 ###取第2,3列
hbase:hive
RDD:DataFrame
$ cut -c 3 Test.txt ###取第3个字符
d
a
$ cut -c 3- Test.txt ###取第3个及以后的所有字符
doop:hbase:hive
ark:RDD:DataFrame
$ cut -c 3-10 Test.txt ###取第3至10个字符
doop:hba
ark:RDD:

熟悉数据库的朋友或许会联想到,grep命令有点像是查询表中某些行,而cut是取表中一些列。为方便理解我们也不妨把grep理解为横切,cut为纵切。
3.5 正则表达式
表达式这三个字相信我们都比较熟悉,加上正则两字好像有点怪怪的,正则表达式(Regular Express,RE)实际上就是一种匹配模式,利用这种模式有利于我们更精准、更灵活地定位。
正则表达式运用非常广泛,不但Linux有,Python、Java、c、C++、scala,数据库等等都有,学好了可以大大提高你的工作效率,尤其在涉及查找、过滤等方面。正则表达式可进一步细分为基础正则表达式和扩展正则表达式,其中基础正则表达式比较常用。
grep、sed、awk、vi或vim等命令支持正则表达式,查询或匹配某字符或字符串时,往往需要使用正则表达,正则表达式的具体规则请参考下表。
(表3-2 正则表达式)

注:
1、最后4个为扩张正则表达式,需使用egrep命令
2、ls 、find、cp等命令不支持正则表达式,需采用bash通配符,如*(任意多个字符),?(任意一个字段)等。
3.6 数据处理常用工具
数据处理时一项非常重要的工作,据那些搞大数据的大师说,数据挖掘、机器学习等数据处理要占整个工作量的80%,而且这块的工作好坏直接影响数据的质量,没有好的质量,挖出的结果也很难保在上面进行证质量。
其实数据处理也是我们平常经常遇到的问题,如修改一些数据、删除一些数据、新增一些数据等等。在windows中我们要修改数据,通常使用word、txt、execel、UltraEdit、NotePad等编辑工具,用这些工具处理数据一般都要先打开,如果遇到一个有几百万行的文件,能open就不错了,更不用说open处理了!
在Linux中难道可以不打开就修改文件?完全可以,而且只要编写一些简单命令就可处理很多复杂问题、还可以一次修改几十份文件、几百份文件!它有何神器呢?
前几节我们讲了如何在文件中精准查询、精准定位等问题,所需字符或行找到了,再使用一些可增删改的工具,如sed、awk等,问题就好解决了。下面我们介绍Linux中有关命令。
 wc [-lwm]
这个命令可以用来查看文件的记录数、字节数等。
-l ###打印出行数
-w ###打印出单词数(英文)
-m ###打印出字符数

$ ls -l
total 20
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
drwxrwxr-x 2 feigu feigu 4096 Oct 18 14:27 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
-rw-rw-r-- 1 feigu feigu 38 Oct 21 15:00 Test.txt
$ ls -l|wc
6 47 277
[行数] [单词数] [字符数] ###这里的单词以空格分隔
$ ls -l|wc -l
6
$ ls -l|wc -w
47
$ ls -l|wc -m
277

 sed
sed 编辑器逐行处理,处理时,把当前处理的行存储在临时缓冲区中,接着用sed命令处理缓冲区中的内容,处理完成后,
把缓冲区的内容送往屏幕。文件本身并没有改变,如果你要保存修改,可以重定向到另一个文件。不过如果使用-i 参数,将直接修改原文件! 其命令格式为:
sed [-参数] '动作' 文件(或标准输入)
下面我们通过一些实例,来说明sed的使用:
 删除行,使用参数d

sed '3d' file ###删除第三行的内容
sed '3,$d' file ###删除从第3行到最后一行的内容。
sed '$d' file ###删除最后一行的内容

 替换字符(串),使用参数s
sed 's/abc/def/g' file ###把行内的所有abc替换成def,如果没有g,则 ###只替换行内的第一个abc
sed -n 's/abc/def/p' file ###只打印发生替换的那些行
 追加行,使用参数a
sed '/spark/a\python pandas' file ###在包含spark的行后新起一行,写入 ###python pandas
sed '/spark/a\python pandas' file>file01 ###把结果存放到另一个文件
 结合管道及正则表达式

$ ls -l
total 24
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
drwxrwxr-x 2 feigu feigu 4096 Oct 18 14:27 shell_script
-rw-rw-r-- 1 feigu feigu 36 Oct 22 15:34 test11.txt
feigu@slave001:~/linux_test$ ls -l|grep '^d' ###选择目录
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
drwxrwxr-x 2 feigu feigu 4096 Oct 18 14:27 shell_script
feigu@slave001:~/linux_test$ ls -l|grep '^d'|sed 's/.*script$//g' ##置空第2行
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect

feigu@slave001:~/linux_test$ ls -l|grep '^d'|sed 's/.*script$//g'|sed '/^$/d'
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect ##删除空行结果

 awk
sed命令用来定位到行,如果我们想定位到‘列’或‘字段’,有相关命令吗?awk就是。这个命令很强大,而且比较好用,和sed一样,awk也是逐行处理的,通常用来处理一些小数据,其命令格式为:
awk '条件1{动作1}条件2{动作2}...'
注意:awk条件和动作需要放在单引号内,动作放在{}内。

$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
drwxrwxr-x 2 feigu feigu 4096 Oct 18 14:27 shell_script
feigu@slave001:~/linux_test$ ls -l|awk '{print $0}' ###$0表示所有列
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
drwxrwxr-x 2 feigu feigu 4096 Oct 18 14:27 shell_script
feigu@slave001:~/linux_test$ ls -l|awk '{print $1,$9}' ###$1表示第1列
total
-rw-rw-r-- conn_mysql.sh
drwxrwxr-x mydirect
drwxrwxr-x shell_script
feigu@slave001:~/linux_test$ ls -l|awk '{FS=" "}{print $1,$9}' ###列的分隔符为 ###空格
total
-rw-rw-r-- conn_mysql.sh
drwxrwxr-x mydirect
drwxrwxr-x shell_script

【延伸思考】
sed、grep、awk等可以很方便批量处理多个文数据文件,尤其对不一些不很庞大的数据。但如果需要处理几个G、几百上千个G、甚至更大更复杂的数据(如图像、视频等等),该咋办呢?目前是否已有好用又高效处理工具?答案是肯定的!稍大的一些文件可以使用python、pandas、Java等,大数据可使用Hadoop、Hive、HBase、Spark等技术,这些技术都基于Linux,这些技术或工具不但解决了大数据存储问题、大数据处理、大数据计算等问题,还有很好的可靠性,这些技术我们后面将一一介绍。
3.7 shell脚本
shell脚本(shell script)是啥样?为何要使用shell脚本?用了能带来啥好处?不用又会带来哪些不便?
shell脚本简单来说就是由shell命令写成一个程序,有点类似windows中dos下批处理文件(.bat),shell脚本无需编译就可直接运行。
假设哪天要你管理几十台甚至几百几千台Linux服务器,需要在每台服务器上创建很多相同目录、修改很多相同配置文件,你该如何处理呢?在每台服务器上都一个一个命令执行一下?恐怕几天都搞不完,即使完成了,也很难保证在每台服务器上做的都是一样。
如果我们把这些命令写成一个脚本,并在一台服务器上测试好,那么剩下的工作就是把这个脚本部署不同服务器上,运行一下即可(熟练的话,这些部署和运行都可一键搞定),如此不但快、而且质量也高。这就是shell脚本强大之一。
下面我们试着写一个简单的shell脚本,加深大家的理解。

$ cat myfirst.sh ####shell脚本名称,以sh为扩展名
#!/bin/bash ###说明使用哪种shell解释你的程序,这里使用bash

#在界面上显示字符串 ###这一行为注释,shell注释用井号(#)
echo "I like linux!" ###把字符串“I like linux!”输出到窗口

关于编辑shell脚本的几个良好习惯
1、第一行说明用哪种shell解释你的脚本
如:#!/bin/bash
2、说明该脚本的功能、变更历史等。
3、添加必要的注释,方便别人更好理解你的程序,尤其是团体开发时。

脚本写好了,该如何执行呢?很简单哟,shell脚本无需编译,可以直接运行,运行方式大致有:
1、利用sh或bash运行,直接运行,但脚本权限要求不高,有读的权限即可。
如:sh myfirst.sh
2、利用. ./myfirst.sh或source ./myfirst.sh的方式,可直接运行, 有读的权限即可。
注./myfirst.sh前有空格
3、如果脚本有执行权限(即x权限),可采用如下格式:
./myfirst.sh
3.8 控制语句
shell流程控制语句常用的有三种:条件判断语句、选择语句、循环语句。
 条件判断语句
 简单判断语句
if [ 条件判断式 ]; then ###[ 条件判断式 ]中条件判断式前后要有空格,否则会 ###报错!
条件判断成立时的执行语句
fi ###大家注意一个有趣的情况,if反过来写就是fi,shell中类型情况有不少。
 复杂一点的判断语句
if [ 条件判断式 ]; then
条件判断成立时的执行语句
else
条件判断不成立时的执行语句
fi
 更多条件的判断语句
if [ 条件判断式1 ]; then
条件判断1成立时的执行语句
elif [ 条件判断式2 ]; then
条件判断2成立时的执行语句
fi

$ vim myfirst01.sh
#!/bin/bash/

##功能:测试判断语句

##定义变量
a=10
b=20

##根据a、b值的大小,输出不同提示。
if [ $a == $b ];then
echo "a is equal to b"
elif [ $a -gt $b ];then
echo "a is greater to b"
else
echo "a is less to b"
fi

注意,shell中判断整数不是用,>,=,< 之类,而是gt ==,lt 。下表为shell常用判断式: (表3-3 shell常用判断式)

 分支选择语句 如果一个变量有多种情况,可以用上面的if 语句,但如果用分支选择语句逻辑更清晰,分支选择语句的语法格式为: case $var in pattern1) ###模式后需要加上) act1 ;; ###;;表示每段任务执行结束 patern2) act2 ;; *) ###*代表其它情况 act3 ;; esac ####case反过来写就是esac

 $ vim myfirst02.sh #!/bin/bash/ ##功能:测试选择语句 ##显示输入提示 read -p "请输入选择的数据:" var1 case $var1 in 1) echo "你选择第1项" ;; 2) echo "选择了第2项" ;; *) echo "你选择了其它项" ;; esac

 for循环语句 for循环语句常用来已知要循环的次数或循环一个列表,具体语法格式为: for var in con1 con2 con3 ... do act done 或 for ((初始值 ; 条件;步长或运行语句)) do act done

 $ vim myfirst03.sh #!/bin/bash/ ##功能:测试for循环语句 for i in $(seq 5) ###$(seq 5)产生一个序列:1 2 3 4 5 do echo "$i" done

 while循环语句 其语法格式为: while [condition] do act done  until 循环语句 until 循环语句格式为: until [condition] do act done 【延伸思考】 这里我们提到了判断语句、选择语句、循环语句,这些语句无疑在处理一些数据分支或序列方面的问题带来了很大的方便,但也存在一些不足,如果分支很多,如果我们要在循环中又实行判断等等,虽能实现,步骤就比较多了,步骤一多,程序逻辑的清晰度、可读性等就大大下降了。是否有更简洁的方法?如可以把循环与判断结合在一起、甚或不用循环语句?好消息是,有并且很多,如在Python、scala中就有很好的解决方案,这些方案或技术后续我们也会向大家介绍。

第4章 日常管理

4.1 搜索目录或文件
使用Linux时,有时忘记上次编辑的文件存放在哪里?但还记得部分文件名称或大致修改时间等线索,此时我们该如何做呢?这样的需求应该是很平常,就像我们平常百度一样。 Linux中有很多方法可以用来查找目录或文件,如前面提到的ls、pwd、grep命令等,除了这些命令之外,还有which、type、whereis、locate、find等,下面我们就来介绍这些命令的使用。
 which命令
which命令一般用来查询执行文件,而且是从$PATH这个环境变量中查询。使用格式为:
which [-a] 执行文件 -a ###将所有在PATH中能找到的文件

feigu@slave001:~$ which python
/home/feigu/anaconda2/bin/python
feigu@slave001:~$ which -a python
/home/feigu/anaconda2/bin/python
/usr/bin/python
feigu@slave001:~$ which cd
feigu@slave001:~$ ###cd 因时shell内置命令,不在$PATH中

which命令无法查询内置命令,但我们可以用type命令来查找。
 type命令 使用type可以查找shell的外部命令、内部命令等,语法格式为:
type [-a] 文件 -a ###显示所有可能的类型,如有些命令如cd是shell内置命令,也可以是外部命令。

feigu@slave001:~$ type -a cd
cd is a shell builtin
feigu@slave001:~$ type -a ls
ls is aliased to <code>ls --color=auto'
ls is /bin/ls

 whereis 命令
whereis 、locate都是从linux的数据库文件中查询,不是从硬盘查询(如find命令),故查询效率比较高,其语法格式如下: whereis [-msu] 文件
-m ###显示在manual路径下的文件
-s ###显示source源文件
-u ###显示其他特殊文件

feigu@slave001:~$ whereis pwd
pwd: /bin/pwd /usr/include/pwd.h /usr/share/man/man1/pwd.1.gz
feigu@slave001:~$ whereis -u mysql
mysql: /usr/bin/mysql /etc/mysql /usr/lib/mysql /usr/bin/X11/mysql /usr/include/mysql /usr/share/mysql /usr/share/man/man1/mysql.1.gz
feigu@slave001:~$

 locate命令
locate也是从Linux的数据库文件中读取,但比whereis更灵活,其参数可使用正则表达式,其语法格式为:
locate [-ir] 文件或目录
-i ###查询时忽略大小写
-r ###后可接正则表达式

feigu@slave001:~$ locate -n 5 -i ipython ###查看含ipython目录或文件的前5条
/home/feigu/.ipython
/home/feigu/.ipython/README
/home/feigu/.ipython/extensions
/home/feigu/.ipython/nbextensions
/home/feigu/.ipython/profile_default
feigu@slave001:~$ locate -n 5 -r ^/var/lib/dpkg/info ###查看以/var/lib开头的
/var/lib/dpkg/info
/var/lib/dpkg/info/accountsservice.conffiles
/var/lib/dpkg/info/accountsservice.list
/var/lib/dpkg/info/accountsservice.md5sums
/var/lib/dpkg/info/accountsservice.postinst

 find命令
如果用whereis和locate无法查找到我们需要的文件时,可以使用find,但是find是在硬盘上遍历查找,因此将消耗较多资源,而且速度也比较慢,不过find可以根据指定路径或修改时间等进行查找,其语法格式为:
find [路径] [参数] [-print ] [-exec 或-ok command] {} \;
时间参数:
-mtime n ###列出n天之前的1天之内修改过的文件 (或距当前n*24 与(n+1)*24 ###小时)
-mtime +n ###列出n天之前(不含n天本身)修改过的文件
-mtime -n ###将列出n天之内(含n天本身)修改过的文件
名称参数:
-group name ###寻找群组名称为name的文件
-user name ###寻找用户者名称为name的文件
-name file ###寻找文件名为file的文件(可以使用通配符)
-print ###将查找到的文件输出到标准输出
-exec command {} \; ###将查到的文件执行command操作,{} 和 \;之间有空格
-ok ###和-exec相同,只不过在操作前要询用户

###列出1天内修改过的文件
feigu@slave001:~$ find /home/feigu/linux_test/ -mtime -1 ###列出/home/feigu/linux_test目录下文件以myfirst开头的文件
feigu@slave001:~$ find /home/feigu/linux_test/ -name myfirst* -print

4.2 查看资源使用情况
在日常管理中,我们经常需要了解目前系统的资源使用情况,如硬盘使用、内存使用等。查看硬盘使用可用df -m,内存使用情况可用free -m,此外了解系统整体使用情况还可用top等命令。

feigu@slave001:~$ df -m Filesystem 1M-blocks Used Available Use% Mounted on /dev/xvda1 20157 17218 1915 90% / udev 489 1 489 1% /dev tmpfs 100 1 99 1% /run none 5 0 5 0% /run/lock none 498 0 498 0% /run/shm none 1 0 1 0% /sys/fs/cgroup
feigu@slave001:~$ free -m total used free shared buffers cached Mem: 994 709 284 0 17 21 -/+ buffers/cache: 671 322 Swap: 1999 60 1939

4.3 进程管理功能 如何查看当前正在运行的进程?如何停止一些不必要的进程?如何把一些任务放在后台运行?(即使你断开客户端程序能继续运行)如何实现前后台进程的转换?下面我们来介绍这方面的问题的处理方法。
ps -ef ###查看当前所有进程
jobs -r ###列出在后台运行的进程
netstat ###查看当前网络及端口使用情况
pstree [-Ap] ###查看进程树
kill -9 PID ###强行停止进程号为PID的进程
Ctrl+c ###停止终端正在运行的任务,如一个正在运行的find命令
ctrl+d ###暂停终端正在运行的任务,如一个正在运行的find命令

第5章 实例详解
5.1 编写导航菜单
操作Linux一大麻烦就是输入命令,虽然Linux为了提高大家的输入效率,内部做了很多工作,如Tab键的补全功能、把相关shell命令行放在一个脚本文件中,利用脚本文件可以重复使用还可以移植到其他服务器上等等,确实大大减少了工作量、同时也极大的提高了正确率。有些人还想,是否有更简单、更直观的方法,如像windows中的菜单式的方法? 有的,本节我们就来做这方面的一个工作,即生成一个Linux的菜单式界面,然后像操作菜单一样来完成我们的任务。
这里的业务场景就是,通过菜单式的方法来安装大数据多种重要组件,主菜单界面如下:

实现以上界面及功能的代码:

$ vim install_hadoop_mainmenu.sh
#!/bin/bash
#****************************************************************************
#文件名称: hadoop_intall_mainmenu.sh
#功能概述: 通过菜单方式安装hadoop各组件.
#作者姓名: 吴茂贵
#创建日期: 2016-10-20
#修改历史:
#****************************************************************************
##获取系统时间
V_SYSTIME=</code>date +%EY-%m-%d-%H:%M:%S<code>
##进入主界面菜单
##利用一个条件始终为真的while循环
while true
do
clear
echo -e "\n\n \t\t \033[40;32m ======飞谷安装Hadoop主界面======\033[1m \t\t " echo -e "\n"
echo -e "\t"1\)安装Hadoop
echo -e "\t"2\)安装Spark
echo -e "\t"0\)退出 echo -e "\n"
echo "请选择(可单选或多选,多选以逗号分隔,如1,2):"
read choice
#利用选择分支语句,根据不同选择,进行相关操作
case $choice in
0 ) break
cd .
;;
1 )
echo "正在安装Hadoop..."
read menu
continue;;
2 ) echo "正在安装Spark..."
read menu continue;;
1,2 ) echo "正在安装Hadoop和Spark..."
read menu continue;;
esac
done

获取Linux系统日期或时间及加减示例

###获取系统时间
V_SYSTIME=</code>date +%EY-%m-%d-%H:%M:%S<code>
hadoop@master:~$ v_sysdate=</code>date +%Y-%m-%d' '%H:%M:%S<code>
hadoop@master:~$ echo $v_sysdate 2016-12-12 15:42:55
hadoop@master:~$ v_sysdate1=$(date +%Y-%m-%d' '%H:%M:%S)
hadoop@master:~$ echo $v_sysdate1 2016-12-12 15:43:56
###获取系统日期
hadoop@master:~$ v_date=$(date +%Y-%m-%d)
hadoop@master:~$ echo $v_date 2016-12-12
###获取明天日期,可以+,- day,month,year等
hadoop@master:~$ v_date1=$(date +%Y-%m-%d --date="+1 day")
hadoop@master:~$ echo $v_date1 2016-12-13

5.2 Shell处理文件 处理文件或处理数据,是大数据开放、大数据分析、机器学习等一项非常基础、非常重要的任务,而且在整个数据分析生命周期中大约占比70%左右,如果数据比较复杂,可能还要超过这个比例。由此可见其作用和意义了。如何处理文件或数据?需要用哪些命令或工具?下面我们通过一些实例来说明。 业务场景是,有一个文件,共有5行,第5行为垃圾数据需要清理,列之间的分隔符是竖线(|),文件中还有乱字符(^M)需要清理,清理数据后,取出前2列,如果有空行,需要删除空行。

$ cat list.lst
linux|shell|function
python|ipython|pandas
hadoop|yarn|hdfs^M
spark|sparkSQL|sparkML^M
sfssf
$ sed 5d list.lst
linux|shell|function
python|ipython|pandas
hadoop|yarn|hdfs^M
spark|sparkSQL|sparkML^M
####^为特殊符号,需要把取消其特殊含义,故需要用转移字符""
$ sed 5d list.lst |sed 's/\^M//g'
linux|shell|function
python|ipython|pandas
hadoop|yarn|hdfs
spark|sparkSQL|sparkML
$ sed 5d list.lst |sed 's/\^M//g'|awk '{FS="|"}{print $1,$2}'
linux|shell|function
###第一行没有变化,因FS缺省为Tab分割,第一行$0,$1等
###需从第二行开始生效,为此,可加上BEGIN关键字,从第
###一行开始生效
python ipython
hadoop yarn
spark sparkSQL
$ sed 5d list.lst |sed 's/\^M//g'|awk 'BEGIN {FS="|"}{print $1,$2}'
linux shell
python ipython
hadoop yarn
spark sparkSQL
$ sed 5d list.lst |sed 's/\^M//g'|awk 'BEGIN {FS="|"}{print $1,$2}' \ > |sed '/^\s*$/d' ###去由Tab、空格组成的空行删除
linux shell
python ipython
hadoop yarn
spark sparkSQL
$ sed 5d list.lst |sed 's/\^M//g'|awk 'BEGIN {FS="|"}{print $1,$2}' |sed '/^\s*$/d'\ ###最后这个\,表示续行,\s匹配<空格>、 ##
> >clear_list.lst ####把结果重定向到文件中
$ cat clear_list.lst
linux shell
python ipython
hadoop yarn
spark sparkSQL

####从文件随机抽取2000条
shuf -n2000 kddcup.data >kddcup2000.data

【延伸思考】
通过shell命令sed、awk、cut等可以对一些文件进行增删改,这无疑带来很大方便,但这种方式也有很多不足,除操作不方便外、对并发处理、快速定位、保证多人修改数据一致性等等都存在严重不足。这些不足如果是离线或只有个人使用,体现不出来,但如果很多人需要同时使用这个问题,这些不足就非常严重、甚至是致命的。如何克服这些不足,数据库管理思想为我们提供了非常高效的解决之道,至于数据库管理系统是如何实现或解决这些不足的,大家可留意后续章节介绍的MySQL。
5.3 Shell函数
为什么要使用shell函数?不用shell函数是否可以?函数我们平常应该经常打交道,如excel中的求和函数、统计函数、三角函数等等,这些函数给我们带来很大方便,其实shell函数的作用一样,主要是为提高效率及增强程序的可读性和可靠性。是否使用函数,看情况而定,如果你的程序本身就很简单,不用也可以,但如果你的程序比较复杂,其中有些模块需要多次或多地使用,那么此时把这些模块用函数来表示、开发效率将显著提升。
shell函数的创建和调用其实很简单,下面我们来介绍函数的定义、函数调用、函数的返回值等。
函数的定义或语法:
[function]functionname[()]
{
action;
[return value;]
}
几点说明:
1、可以带function fun() 定义,也可以直接fun() 或fun定义,不带任何参数。
2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值,return后跟数值n(0-255),通过$?系统变量获取返回值;如果要返回大于255的数、浮点数和字符串值,最好用函数输出到变量。如在函数最后echo $v1 然后通过V2=funct1()方式捕获函数返回值。
3、函数的调用:
函数调用需放在函数定义后,因shell是由上向下逐步执行的
无参数情况:函数名
有函数情况:函数名 参数1 参数2 ... 参数间用空格分开
4、有关参数变量或位置变量
$# 表示参数总数
$0 代表函数名称
$1 表示第一个参数
$2 表示第二参数,以此类推。
以下我们通过一些实例来进一步说明函数的使用。

feigu@slave001:~$ vim testfun1.sh
#!/bin/bash
###在函数定义前,试图调用函数
fSum 3 2;
function fSum()
{
if [ $# -ne 2 ] ;then
echo "请检查函数$0 的参数 a,b"
#exit
fi
echo $1,$2
###求和,并返回其结果
return $(($1+$2))
}
###调用函数
fSum 10 8
###调用函数,并试图把函数返回值给变量total
total=$(fSum 3 2)
echo "total的值:"$total "\$?的值: "$?
###只传一个参数
#fSum 100
###传入的参数和超过255
total10=</code>fSum 100 160<code>
echo "total10的值:"$total10 "\$?的值: "$?
feigu@slave001:~$ sh testfun1.sh
testfun1.sh: line 4: fSum: command not found
10,8
total的值:3,2 $?的值: 5
total10的值:100,160 $?的值: 4
hadoop@master:~/wumg/linux$ cat f1.sh
#!/bin/bash

f1()
{
a=$1 #第一个参数值放到变量a里面
echo $a #打印变量a的值
return $a #把a的值作为函数的返回值
}
#f1 10
#echo $? #查看上一个命令的返回值
#c=</code>f1 200<code> #反单引号里面的语句将作为命令执行,把执行结果放入变量c里面
#d=$(f1 300) #$()中的内容会自动执行,把执行结果放入变量d中
f1 300
echo $?
#echo $c
#echo $d

【延伸思考】
很多相同或相似的功能,我们可以用函数来模块化、封装化,定义一次,以后就可重复使用千万次,如上面我们定义的求两数的和这个函数。当然,这是一个非常简单的函数,而且输入也很严格,内部也没有做异常识别或处理。但这样业务的需求还是比比皆是,尤其涉及到一些稍大一些的项目,这种模块化的处理方式,不但能极大提高我们的开发效率、更重要的是大大提高整个程序的可靠性、可移植性等等。
沿着这个思路想下去,如果fsum能做到不受输入参数个数的限制,那它的通用性就更好了,如何实现这些方法?开发工具python、java、scala等等都有很好的解决方法。
如果类似的函数有很多,而且除了函数,还有很多变量值,缺省值或枚举值等等,那我们又该如何有效管理呢?我们前辈,也想到了很好的解决之道,那就是对象的引入,一个对象可以包括很多函数或方法,还可以包含很多变量或常量等,这是一个更高一级的模块化。
对象问题Linux没有涉及到,后续Python、java、scala中大家会学到。
5.4 Shell操作数据库
现在企业的数据大部分存储在数据库中,但无法直接对数据库中的数据进行分析、挖掘、机器学习等,因为数据库中没有这些算法,因此,如果要对这些数据进行分析,往往先把这些数据保存到文件中,然后与其他数据结合进行分析、挖掘。
如果要用shell操作数据库,首先必须连接数据库,然后操作数据库,如何连接数据库呢?这里我们以MySQL为例,MySQL数据库是开源免费的,而且通过多年的发展,不但好用,而且还很稳定,所以目前在很多中小企业中运用非常广泛。
如何连接?如何操作?我们还是通过实例来说明。环境是Linux服务器(服务器IP已知)上安装了MySQL数据库,数据库名称为testdb,还知道连接数据库的用户及密码等信息。
有了这些我们就可以着手工作了。

$ vim conn_mysql.sh
#!/bin/bash
#get mysql path
MYSQL=</code>which myql<code>

mysql -h slave02 -u feigu -p testdb <<EOF
show databases;
EOF
if [ $? -eq 0 ]; then
echo "连接数据库--成功!"
else
echo "连接数据库--失败!"
fi
$ sh conn_mysql.sh
Enter password:
Database ###显示目前存在的数据库
information_schema
mysql
performance_schema
testdb

连接数据库--成功!
连接数据后,如果我们想操作一下数据库,如创建表,往表中插入记录,然后验证结果是否与期望的一致等,这些任务用shell如何实现呢?请看下例:

#!/bin/bash

#get mysql path
MYSQL=</code>which myql<code>

mysql -h slave02 -u feigu -p testdb  <<EOF
DROP TABLE IF EXISTS test10;
create table test10 (name varchar(10),age int);
insert into test10 values('zhangfei',88);
insert into test10 values('liuting',26);
select * from test10 \G;
EOF
feigu@slave001:~/linux_test$ sh insert_data.sh
Enter password:
*************************** 1. row ***************************
name: zhangfei
 age: 88
*************************** 2. row ***************************
name: liuting
 age: 26

5.5 定时调度
场景:如果有个任务要求在凌晨1点运行、或从9点到16点每隔1分钟运行一次,我们该如何处理呢?守到凌晨1点运行这个程序?当然大可不必,借助Linux的调度器(crontab),你可轻松搞定这些任务。同时通过定时任务的执行加深对环境变量、登录配置参数等的理解。
crontab 是Linux、Unix等系统下的定时任务触发器,在crontab中定义好要调度的时间、频率及要执行的文件等信息,Linux服务器将按照的要求进行调度和执行。windows是否有呢?当然有,windows在计算机管理=>系统工具=>任务计划程序。

(图5-1 windows定义定时调度任务)

其中,触发器定义调度时间及频率(不过最小单位天,不像Linux下的crontab可以精确到分。);操作部分定义执行程序或发邮件等;应用程序一般指写好批处理bat文件等。
Linux下的定时定义类似,但内容更丰富、功能更强大。调度时间及频率格式如下:
* * * * * 共六段,前五段为时间,第六段是命令或脚本,段之间用空格隔开。
段 含义 取值范围
第一段 代表分钟 0-59
第二段 代表小时 0-23
第三段 代表日期 1-31
第四段 代表月份 1-12
第五段 代表星期几 0-6(0-星期日)
几个示例:
0 * * * 0,6 /usr/lib/sa/sa1 (每星期六、日)
0 8-17 * * 1-5 /usr/lib/sa/sa1 1200 3
15 4 1 * * /usr/lib/acct/monacct > /dev/null 2>&1
0 6-12/2 * * * /usr/lib/acct/monacct > /dev/null 2>&1(6点到12点每隔2小时执行一次命令)
0,30 * * * * /usr/lib/acct/monacct > /dev/null 2>&1(每隔半小时运行一次)
如何查看或修改当前用户的定时调度任务?可用如下命令:
 crontab -l(查看当前用户所有crontab任务)
 crontab -e (编辑当前用户crontab任务)
以下通过实例来说明如何创建crontab及使用crontab可能出现的问题和解决方法。
先用通过crontab执行一个简单语句,把crontab的环境变量导出到一个文件。
注:第一次使用crontab -e 打开时,可能会提示你选择编辑器,建议选择vim
如果不提示,但出现的为不是vim或vi编辑器,可用命令:export EDITOR=vim把当前进程的编辑器指定为vim。

hadoop@master:~$ crontab -e

在打开的crontab后,在最后一行添加如下语句:

0-59/1 * * * * echo </code>env<code>>env_log.txt

以下是env_log.txt文件内容

hadoop@master:~$ cat env_log.txt
LANGUAGE=en_US:en HOME=/home/hadoop LOGNAME=hadoop PATH=/usr/bin:/bin LANG=en_US.UTF-8 SHELL=/bin/sh PWD=/home/hadoop

为了便于测试,我们使它每隔1分钟执行1次。大家把env_log.txt的结果
与在命令行直接运行env这个命令的结果比较一下,看是否一样,如果不一样,为什么?
在3.2节,我们介绍了登录shell与非登录shell的区别,最大的区别就是读取配置文件的不同,从而导致它们的环境变量不一样,环境变量不一样会导致什么结果呢?我们来看一个实例。
这是执行的一个shell脚本,主要功能是连接MySQL数据库,然后创建一个表并往表中插入2条记录。

$ cat insert_sql.sh
#!/bin/bash

#get mysql path
MYSQL=</code>which myql`

mysql -h slave02 -u feigu -p testdb  <<EOF
DROP TABLE IF EXISTS test10;
create table test10 (name varchar(10),age int);
insert into test10 values('zhanghua',22);
insert into test10 values('liuting',26);
EOF

if [ $? -eq 0 ]; then
echo "操作数据库成功"
else
echo "操作数据库失败"
fi

我们直接在命令行执行这个脚本

hadoop@master:~/wumg/linux/cron$ ls -l
total 4
-rw-rw-r-- 1 hadoop hadoop 352 Nov 15 17:25 insert_mysql.sh
hadoop@master:~/wumg/linux/cron$ sh insert_mysql.sh
操作数据库成功       ###执行成功

接下来,我们通过crontab来调度该脚本,为此,在crontab中添加如下一条语句:

0-59/1 * * * *  sh insert_mysql.sh >log.txt 2>&1

我们查看log.txt是

hadoop@master:~$ cat log.txt
sh: 0: Can't open insert_mysql.sh

从以上运行日志可以看出,运行失败,失败的原因是什么呢?请大家看一下env_log.txt日志内容,这说明crontab执行的无登录的shell脚本,环境变量PATH中没有脚本insert_mysql.sh所在的路径,故系统找不到或无法open该脚本,那该如何解决呢?
我们可以在crontab执行命令中添加该脚本的绝对路径,就可以。把执行语句改为:

0-59/1 * * * * sh /home/hadoop/wumg/linux/cron/insert_mysql.sh >log01.txt 2>&1
    过一会儿,我们查看日志文件log01.txt
hadoop@master:~$ cat log01.txt
操作数据库成功

说明这次执行成功了!

【延伸思考】
环境变量问题,除了添加绝对路径外,想一想还有其他方法吗?

5.6 服务器间的ssh无密码传输
【这节为了解内容,后续《hadoop课程》将有更详细介绍。】
场景:假如你正在管理1000台Linux服务器、甚至更多,你需要把一些文件或数据发布或传输到其他服务器上,如果不做处理,每次都要输入密码,当然更不方便写一个代码自动分发。能否写一个代码自动分发传输时无需输入密码?
这就涉及到ssh无密码传输问题,这节就是介绍ssh无密码传输含义及设置方式,配置好以后,使用ssh或scp到另外一台服务器,就无需输入密码了。
环境:A、B两台Linux服务器,实现从A到B的无密码传输。

5.6.1 SSH简介

(1)SSH定义
 SSH 为 Secure Shell 的缩写,由 IETF 的网络工作小组(Network Working Group) 所制定,是建立在应用层和传输层基础上的安全协议。
 SSH是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用SSH 协议可以有效防止远程管理过程中的信息泄露问题。
(2)SSH提供两种安全级别的安全论证
 基于密码
 基于密钥
5.6.2 SSH无密码传输
由master到slave01节点,可以通过以下命令:
登录master节点后,在命令行输入:ssh slave01 即可无密码进入slave01节点。
退回master节点,可在salve01节点,输入exit即可,具体请看以下示例。

hadoop@master:~$ ssh salve01
ssh: Could not resolve hostname salve01: Name or service not known
hadoop@master:~$ ssh slave01
Welcome to Ubuntu 14.04.2 LTS (GNU/Linux 3.13.0-61-generic x86_64)

* Documentation: https://help.ubuntu.com/

System information as of Tue Nov 15 10:54:00 CST 2016

System load: 0.0 Processes: 83
Usage of /: 4.6% of 97.66GB Users logged in: 0
Memory usage: 18% IP address for eth0: 192.168.1.132
Swap usage: 0%

Graph this data and manage this system at:
https://landscape.canonical.com/

148 packages can be updated.
73 updates are security updates.

*** System restart required ***
Last login: Tue Nov 15 10:54:03 2016 from master
hadoop@slave01:~$ ###说明已登录到slave01节点
hadoop@slave01:~$ exit
logout
Connection to slave01 closed.
hadoop@master:~$ ###退回到master节点

由master节点向其它节点传输文件可以采用scp命令,scp命令格式如下:
scp [-r]源文件 username@hostname:目标路径
以下示例的功能为:把hadoop用户当前目录下文件test.txt 传输到slave01节点,登录slave01节点的用户为hadoop,目标路径为/home/hadoop/data(因/home/hadoop是hadoop用户的主目录,故可用波浪号(~)表示)。

hadoop@master:~$ scp test.txt hadoop@slave01:~/data/
test.txt 100% 12 0.0KB/s 00:00
hadoop@master:~$

【课后练习】
一、在目录homework1130/20161130**(这个表示各人学号,每个人的学号不一样)
1、创建2个子目录
一级子目录名称(课程):linux
二级子目录名称(第几次练习):01
最后通过pwd命令应该看到如下格式:
~/homework1130/20161130**/linux/01
2、修改linux目前权限,是其它人只有读(r)的权限,自己和所属组有读写执行权限。
二、在linux目录下,新建一个shell脚本,脚本功能为:
1、定义两个变量,其中一个为你的姓名全拼,并在屏幕上显示;另一个变量传给一个 子shell脚本
2、调用一个shell,并在屏幕上显示该变量,同时把执行子shell的错误日志输出到另一 个文件。
三、编写一个脚本,实现安装linux、mysql、python的导航菜单,菜单可以单选或多选。
四、编写一个shell脚本,脚本中第一个函数,往该函数传入2个整数,输出它们的最大值。
五、查找服务器上含mysql字符串的目录及文件前10条导入到一个文件中,错误信息除外。
六、编写一个脚本,用ls命令及管道技术,把用户主目录下的所有文件(目录不需要)名称导入一个文件中,然后删除导出文件的前2行记录后导入到另一个文件中。
七、操作数据库,编写一个shell脚本,连接数据库testdb,然后创建一个表t_你的姓名全拼,该表至少包含3个字段,如id 为整数,name为varchar类型,birthday 为日期型,然后插入几条记录,最后捕获操作数据库的状态,如果成功,则在屏幕上打印成功字样;否则,打印失败字样。
八、编写一个脚本,然后通过crontab每天23点20分钟启动,并把运行脚本的错误日志输出到一个日志文件中。