极迅云课(服务端教案)

96
_小老虎_
2018.07.11 23:08* 字数 47961

一、Python简介和环境搭建以及pip的安装

4课时

实验课

主要内容

【Python简介】:

Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。

Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。

Python 是一种解释型语言: 这意味着开发过程中没有了编译这个环节。类似于PHP和Perl语言。

Python 是交互式语言: 这意味着,您可以在一个Python提示符,直接互动执行写你的程序。

Python 是面向对象语言: 这意味着Python支持面向对象的风格或代码封装在对象的编程技术。

Python 是初学者的语言:Python 对初级程序员而言,是一种伟大的语言,它支持广泛的应用程序开发,从简单的文字处理到 WWW 浏览器再到游戏。

Python 是由 Guido van Rossum 在八十年代末和九十年代初,在荷兰国家数学和计算机科学研究所设计出来的。

Python 本身也是由诸多其他语言发展而来的,这包括 ABC、Modula-3、C、C++、Algol-68、SmallTalk、Unix shell 和其他的脚本语言等等。

像 Perl 语言一样,Python 源代码同样遵循 GPL(GNU General Public License)协议。

现在 Python 是由一个核心开发团队在维护,Guido van Rossum 仍然占据着至关重要的作用,指导其进展。

Python 特点

1.易于学习:Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单。

2.易于阅读:Python代码定义的更清晰。

3.易于维护:Python的成功在于它的源代码是相当容易维护的。

4.一个广泛的标准库:Python的最大的优势之一是丰富的库,跨平台的,在UNIX,Windows和Macintosh兼容很好。

5.互动模式:互动模式的支持,您可以从终端输入执行代码并获得结果的语言,互动的测试和调试代码片断。

6.可移植:基于其开放源代码的特性,Python已经被移植(也就是使其工作)到许多平台。

7.可扩展:如果你需要一段运行很快的关键代码,或者是想要编写一些不愿开放的算法,你可以使用C或C++完成那部分程序,然后从你的Python程序中调用。

8.数据库:Python提供所有主要的商业数据库的接口。

9.GUI编程:Python支持GUI可以创建和移植到许多系统调用。

10.可嵌入: 你可以将Python嵌入到C/C++程序,让你的程序的用户获得"脚本化"的能力。

【所需环境】:

Ubuntu操作系统 | Centos操作系统 | Mac OS操作系统

(目前不介绍Windows是因为Windows安装相对于简单一些)

【Ubuntu操作系统下安装Python和Pip】:

python:

第一步,输入 apt-cache search python 检索我们需要的python包

检索我们需要的python包


第二步,安装Python包

apt-get install python2.7  安装2.7版本的Python

安装Python2.7

之后进入Python的安装时间,安装成功之后输入:

which python命令进行查看python路径

并且输入 python --version 查看python的版本信息是否正确。


另外一种方式,从源代码编译安装python:

$ wget -c https://www.python.org/ftp/python/2.7.9/Python-2.7.9.tgz  

$ tar -xzvf Python-2.7.9.tgz  

$ cd Python-2.7.9/  

$ LDFLAGS="-L/usr/lib/x86_64-linux-gnu" ./configure  

$ make  

$ sudo make install  

这种方式是使用编译的形势安装python程序。


pip:

pip 是一个安装和管理 Python 包的工具,python安装包的工具有easy_install, setuptools, pip,distribute。使用这些工具都能下载并安装django。,而pip是easy_install的替代品。在CPython解释器,pypy解释器,可以很好地工作。

步骤一: 在终端上使用以下命令,来保证你系统上所有的包都是最新的。

更新你的包

sudo apt-get update

升级你的包

sudo apt-get upgrade

步骤二: 安装Pip

安装python-pip和你所需要的包:

apt-get install python-pip

检查你所安装Pip的版本:

pip -V

步骤三: 如何使用Pip命令。

在安装python-pip之后,你可以使用pip命令。这里有很多的pip命令是可以使用: 

安装新的python包:

pip install packageName

卸载python包:

pip uninstall packageName

寻找python包:

pip search packageName


【Centos操作系统下安装Python和Pip】:

python & pip:

从pip官网https://pypi.python.org/pypi/pip下载pip的源代码

#解压

tar -zxvf pip-1.5.5.tar.gz

cd pip-1.5.5

安装

python setup.py install

从setuptools官网https://pypi.python.org/pypi/setuptools下载setuptools

#解压

tar -zxvf setuptools-3.6.tar.gz

cd setuptools-3.6

#安装

python setup.py install

再次安装pip就OK了。

加载到python时,在编译时带入zlib选项可以有。如下:

./configure --prefix=/usr/local --with -zlib

sudo make

sudo make install

pip类似RedHat里面的yum,安装软件非常方便。本节详细介绍pip的安装、以及使用方法。

1、pip下载安装

1.1 pip下载

# wget "https://pypi.python.org/packages/source/p/pip/pip-1.5.4.tar.gz#md5=834b2904f92d46aaa333267fb1c922bb" --no-check-certificate

1.2 pip安装

# pip install SomePackage

  [...]

  Successfully installed SomePackage

2. pip使用详解

2.1 pip安装软件

# pip install SomePackage

  [...]

  Successfully installed SomePackage

2.2 pip查看已安装的软件

# pip show --files SomePackage

  Name: SomePackage

  Version: 1.0

  Location: /my/env/lib/pythonx.x/site-packages

  Files:

   ../somepackage/__init__.py

   [...]

2.3 pip检查哪些软件需要更新

# pip list --outdated

  SomePackage (Current: 1.0 Latest: 2.0)

2.4 pip升级软件

# pip install --upgrade SomePackage

  [...]

  Found existing installation: SomePackage 1.0

  Uninstalling SomePackage:

    Successfully uninstalled SomePackage

  Running setup.py install for SomePackage

  Successfully installed SomePackage

2.5 pip卸载软件

$ pip uninstall SomePackage

  Uninstalling SomePackage:

    /my/env/lib/pythonx.x/site-packages/somepackage

  Proceed (y/n)? y

  Successfully uninstalled SomePackage

3. pip使用实例

#3.1 安装redis

# pip install redis

#3.2 卸载redis

# pip uninstall redis

Uninstalling redis:

  /usr/lib/python2.6/site-packages/redis-2.9.1-py2.6.egg-info

.....省略一些内容....

Proceed (y/n)? y

  Successfully uninstalled redis

#3.3 查看待更新软件

pip list --outdate

pygpgme (Current: 0.1 Latest: 0.3)

pycurl (Current: 7.19.0 Latest: 7.19.3.1)

iniparse (Current: 0.3.1 Latest: 0.4)


【Centos操作系统下安装Python和Pip】:

python & pip:

Mac OS上可以使用brew一键安装:

brew install python2.7

brew install pip2





二、Python基础语法

4课时

实验课

主要内容

Python 基础语法

Python 语言与 Perl,C 和 Java 等语言有许多相似之处。但是,也存在一些差异。

第一个 Python 程序

交互式编程

交互式编程不需要创建脚本文件,是通过 Python 解释器的交互模式进来编写代码。

linux上你只需要在命令行中输入 Python 命令即可启动交互式编程,提示窗口如下:

$ pythonPython 2.7.6 (default, Sep  9 2014, 15:04:36) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwinType "help", "copyright", "credits" or "license" for more information.>>>

Window 上在安装 Python 时已经已经安装了默认的交互式编程客户端,提示窗口如下:

在 python 提示符中输入以下文本信息,然后按 Enter 键查看运行效果:

>>> print "Hello, Python!";

在 Python 2.7.6 版本中,以上实例输出结果如下:

Hello, Python!

脚本式编程

通过脚本参数调用解释器开始执行脚本,直到脚本执行完毕。当脚本执行完成后,解释器不再有效。

让我们写一个简单的 Python 脚本程序。所有 Python 文件将以 .py 为扩展名。将以下的源代码拷贝至 test.py 文件中。

print "Hello, Python!";

这里,假设你已经设置了 Python 解释器 PATH 变量。使用以下命令运行程序:

$ python test.py

输出结果:

Hello, Python!

让我们尝试另一种方式来执行 Python 脚本。修改 test.py 文件,如下所示:

#!/usr/bin/pythonprint "Hello, Python!";

这里,假定您的Python解释器在/usr/bin目录中,使用以下命令执行脚本:

$ chmod +x test.py    # 脚本文件添加可执行权限$ ./test.py

输出结果:

Hello, Python!

Python 标识符

在 Python 里,标识符由字母、数字、下划线组成。

在 Python 中,所有标识符可以包括英文、数字以及下划线(_),但不能以数字开头。

Python 中的标识符是区分大小写的。

以下划线开头的标识符是有特殊意义的。以单下划线开头 _foo 的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用 from xxx import * 而导入;

以双下划线开头的 __foo 代表类的私有成员;以双下划线开头和结尾的 __foo__ 代表 Python 里特殊方法专用的标识,如 __init__() 代表类的构造函数。

Python 可以同一行显示多条语句,方法是用分号 ; 分开,如:

>>> print 'hello';print 'runoob';hello

runoob

Python 保留字符

下面的列表显示了在Python中的保留字。这些保留字不能用作常数或变数,或任何其他标识符名称。

所有 Python 的关键字只包含小写字母。

andexecnot

assertfinallyor

breakforpass

classfromprint

continueglobalraise

defifreturn

delimporttry

elifinwhile

elseiswith

exceptlambdayield

行和缩进

学习 Python 与其他语言最大的区别就是,Python 的代码块不使用大括号 {} 来控制类,函数以及其他逻辑判断。python 最具特色的就是用缩进来写模块。

缩进的空白数量是可变的,但是所有代码块语句必须包含相同的缩进空白数量,这个必须严格执行。如下所示:

if True:    print "True"else:  print "False"

以下代码将会执行错误:

#!/usr/bin/python# -*- coding: UTF-8 -*-# 文件名:test.pyif True:    print "Answer"    print "True"else:    print "Answer"    # 没有严格缩进,在执行时会报错  print "False"

执行以上代码,会出现如下错误提醒:

$ python test.py 

  File "test.py", line 10    print "False"                ^IndentationError: unindent does not match any outer indentation level

IndentationError: unindent does not match any outer indentation level错误表明,你使用的缩进方式不一致,有的是 tab 键缩进,有的是空格缩进,改为一致即可。

如果是 IndentationError: unexpected indent 错误, 则 python 编译器是在告诉你"Hi,老兄,你的文件里格式不对了,可能是tab和空格没对齐的问题",所有 python 对格式要求非常严格。

因此,在 Python 的代码块中必须使用相同数目的行首缩进空格数。

建议你在每个缩进层次使用 单个制表符 或 两个空格 或 四个空格 , 切记不能混用

多行语句

Python语句中一般以新行作为语句的结束符。

但是我们可以使用斜杠( \)将一行的语句分为多行显示,如下所示:

total = item_one + \

        item_two + \

        item_three

语句中包含 [], {} 或 () 括号就不需要使用多行连接符。如下实例:

days = ['Monday', 'Tuesday', 'Wednesday',        'Thursday', 'Friday']

Python 引号

Python 可以使用引号( ' )、双引号( " )、三引号( ''' 或 """ ) 来表示字符串,引号的开始与结束必须的相同类型的。

其中三引号可以由多行组成,编写多行文本的快捷语法,常用于文档字符串,在文件的特定地点,被当做注释。

word = 'word'sentence = "这是一个句子。"paragraph = """这是一个段落。

包含了多个语句"""

Python注释

python中单行注释采用 # 开头。

#!/usr/bin/python# -*- coding: UTF-8 -*-# 文件名:test.py# 第一个注释print "Hello, Python!";  # 第二个注释

输出结果:

Hello, Python!

注释可以在语句或表达式行末:

name = "Madisetti" # 这是一个注释

python 中多行注释使用三个单引号(''')或三个双引号(""")。

#!/usr/bin/python# -*- coding: UTF-8 -*-# 文件名:test.py'''

这是多行注释,使用单引号。

这是多行注释,使用单引号。

这是多行注释,使用单引号。

'''"""

这是多行注释,使用双引号。

这是多行注释,使用双引号。

这是多行注释,使用双引号。

"""

Python空行

函数之间或类的方法之间用空行分隔,表示一段新的代码的开始。类和函数入口之间也用一行空行分隔,以突出函数入口的开始。

空行与代码缩进不同,空行并不是Python语法的一部分。书写时不插入空行,Python解释器运行也不会出错。但是空行的作用在于分隔两段不同功能或含义的代码,便于日后代码的维护或重构。

记住:空行也是程序代码的一部分。

等待用户输入

下面的程序执行后就会等待用户输入,按回车键后就会退出:

#!/usr/bin/python# -*- coding: UTF-8 -*-raw_input("按下 enter 键退出,其他任意键显示...\n")

以上代码中 ,\n 实现换行。一旦用户按下 enter(回车) 键退出,其它键显示。

同一行显示多条语句

Python可以在同一行中使用多条语句,语句之间使用分号(;)分割,以下是一个简单的实例:

#!/usr/bin/pythonimport sys; x = 'runoob'; sys.stdout.write(x + '\n')

执行以上代码,输入结果为:

$ python test.py

runoob

Print 输出

print 默认输出是换行的,如果要实现不换行需要在变量末尾加上逗号 ,

#!/usr/bin/python# -*- coding: UTF-8 -*-x="a"y="b"# 换行输出print xprint yprint '---------'# 不换行输出print x,print y,# 不换行输出print x,y

以上实例执行结果为:

a

b---------a b a b

多个语句构成代码组

缩进相同的一组语句构成一个代码块,我们称之代码组。

像if、while、def和class这样的复合语句,首行以关键字开始,以冒号( : )结束,该行之后的一行或多行代码构成代码组。

我们将首行及后面的代码组称为一个子句(clause)。

如下实例:

if expression :

  suite elif expression : 

  suite  else : 

  suite

命令行参数

很多程序可以执行一些操作来查看一些基本信息,Python 可以使用 -h 参数查看各参数帮助信息:

$ python -h

usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ... Options and arguments (and corresponding environment variables): -c cmd : program passed in as string (terminates option list) -d    : debug output from parser (also PYTHONDEBUG=x) -E    : ignore environment variables (such as PYTHONPATH) -h    : print this help message and exit

[ etc. ]




三、Python 变量类型和运算符

4课时

实验课

主要内容

Python 变量类型

变量存储在内存中的值。这就意味着在创建变量时会在内存中开辟一个空间。

基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中。

因此,变量可以指定不同的数据类型,这些变量可以存储整数,小数或字符。

变量赋值

Python 中的变量赋值不需要类型声明。

每个变量在内存中创建,都包括变量的标识,名称和数据这些信息。

每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。

等号(=)用来给变量赋值。

等号(=)运算符左边是一个变量名,等号(=)运算符右边是存储在变量中的值。例如:

实例(Python 2.0+)

#!/usr/bin/python# -*- coding: UTF-8 -*- counter = 100 # 赋值整型变量miles = 1000.0 # 浮点型name = "John" # 字符串 print counterprint milesprint name


以上实例中,100,1000.0和"John"分别赋值给counter,miles,name变量。

执行以上程序会输出如下结果:

1001000.0John

多个变量赋值

Python允许你同时为多个变量赋值。例如:

a = b = c = 1

以上实例,创建一个整型对象,值为1,三个变量被分配到相同的内存空间上。

您也可以为多个对象指定多个变量。例如:

a, b, c = 1, 2, "john"

以上实例,两个整型对象 1 和 2 分别分配给变量 a 和 b,字符串对象 "john" 分配给变量 c。

标准数据类型

在内存中存储的数据可以有多种类型。

例如,一个人的年龄可以用数字来存储,他的名字可以用字符来存储。

Python 定义了一些标准类型,用于存储各种类型的数据。

Python有五个标准的数据类型:

Numbers(数字)

String(字符串)

List(列表)

Tuple(元组)

Dictionary(字典)

Python数字

数字数据类型用于存储数值。

他们是不可改变的数据类型,这意味着改变数字数据类型会分配一个新的对象。

当你指定一个值时,Number对象就会被创建:

var1 = 1

var2 = 10

您也可以使用del语句删除一些对象的引用。

del语句的语法是:

del var1[,var2[,var3[....,varN]]]]

您可以通过使用del语句删除单个或多个对象的引用。例如:

del var

del var_a, var_b

Python支持四种不同的数字类型:

int(有符号整型)

long(长整型[也可以代表八进制和十六进制])

float(浮点型)

complex(复数)

实例

一些数值类型的实例:

int long float complex

1051924361L0.03.14j

100-0x19323L15.2045.j

-7860122L-21.99.322e-36j

0800xDEFABCECBDAECBFBAEl32.3e+18.876j

-0490535633629843L-90.-.6545+0J

-0x260-052318172735L-32.54e1003e+26J

0x69-4721885298529L70.2E-124.53e-7j

长整型也可以使用小写 l,但是还是建议您使用大写 L,避免与数字 1 混淆。Python使用 L 来显示长整型。

Python 还支持复数,复数由实数部分和虚数部分构成,可以用 a + bj,或者 complex(a,b) 表示, 复数的实部 a 和虚部 b 都是浮点型。

Python字符串

字符串或串(String)是由数字、字母、下划线组成的一串字符。

一般记为 :

s="a1a2···an"(n>=0)

它是编程语言中表示文本的数据类型。

python的字串列表有2种取值顺序:

从左到右索引默认0开始的,最大范围是字符串长度少1

从右到左索引默认-1开始的,最大范围是字符串开头

如果你要实现从字符串中获取一段子字符串的话,可以使用变量 [头下标:尾下标],就可以截取相应的字符串,其中下标是从 0 开始算起,可以是正数或负数,下标可以为空表示取到头或尾。

比如:

s = 'ilovepython'

s[1:5]的结果是love。

当使用以冒号分隔的字符串,python返回一个新的对象,结果包含了以这对偏移标识的连续的内容,左边的开始是包含了下边界。

上面的结果包含了s[1]的值l,而取到的最大范围不包括上边界,就是s[5]的值p。

加号(+)是字符串连接运算符,星号(*)是重复操作。如下实例:

实例(Python 2.0+)

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

str = 'Hello World!' print str          

# 输出完整字符串

print str[0]        

# 输出字符串中的第一个字符

print str[2:5]      

# 输出字符串中第三个至第五个之间的字符串

print str[2:]      

 # 输出从第三个字符开始的字符串

print str * 2       

# 输出字符串两次

print str + "TEST"  # 输出连接的字符串

以上实例输出结果:

Hello World!H

llo

llo World!Hello World!Hello World!Hello World!TEST

Python列表

List(列表) 是 Python 中使用最频繁的数据类型。

列表可以完成大多数集合类的数据结构实现。它支持字符,数字,字符串甚至可以包含列表(即嵌套)。

列表用 [ ] 标识,是 python 最通用的复合数据类型。

列表中值的切割也可以用到变量 [头下标:尾下标] ,就可以截取相应的列表,从左到右索引默认 0 开始,从右到左索引默认 -1 开始,下标可以为空表示取到头或尾。

加号 + 是列表连接运算符,星号 * 是重复操作。如下实例:

实例(Python 2.0+)

#!/usr/bin/python# 

-*- coding: UTF-8 -*- 

list = [ 'runoob', 786 , 2.23, 'john', 70.2 ]

tinylist = [123, 'john'] print list              

# 输出完整列表

print list[0]            

# 输出列表的第一个元素

print list[1:3]          

# 输出第二个至第三个元素 

print list[2:]          

 # 输出从第三个开始至列表末尾的所有元素

print tinylist * 2      

 # 输出列表两次

print list + tinylist    

# 打印组合的列表

以上实例输出结果:

['runoob', 786, 2.23, 'john', 70.2]runoob[786, 2.23][2.23, 'john', 70.2][123, 'john', 123, 'john']['runoob', 786, 2.23, 'john', 70.2, 123, 'john']

Python元组

元组是另一个数据类型,类似于List(列表)。

元组用"()"标识。内部元素用逗号隔开。但是元组不能二次赋值,相当于只读列表。

实例(Python 2.0+)

#!/usr/bin/python# 

-*- coding: UTF-8 -*- 

tuple = ( 'runoob', 786 , 2.23, 'john', 70.2 )

tinytuple = (123, 'john') print tuple              # 输出完整元组

print tuple[0]            # 输出元组的第一个元素

print tuple[1:3]          # 输出第二个至第三个的元素 

print tuple[2:]          # 输出从第三个开始至列表末尾的所有元素

print tinytuple * 2      # 输出元组两次

print tuple + tinytuple  # 打印组合的元组

以上实例输出结果:

('runoob', 786, 2.23, 'john', 70.2)runoob(786, 2.23)(2.23, 'john', 70.2)(123, 'john', 123, 'john')('runoob', 786, 2.23, 'john', 70.2, 123, 'john')

以下是元组无效的,因为元组是不允许更新的。而列表是允许更新的:

实例(Python 2.0+)

#!/usr/bin/python# 

-*- coding: UTF-8 -*- 

tuple = ( 'runoob', 786 , 2.23, 'john', 70.2 )

list = [ 'runoob', 786 , 2.23, 'john', 70.2 ]

tuple[2] = 1000    # 元组中是非法应用

list[2] = 1000    # 列表中是合法应用

Python 字典

字典(dictionary)是除列表以外python之中最灵活的内置数据结构类型。列表是有序的对象集合,字典是无序的对象集合。

两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。

字典用"{ }"标识。字典由索引(key)和它对应的值value组成。

实例(Python 2.0+)

#!/usr/bin/python# 

-*- coding: UTF-8 -*- 

dict = {}

dict['one'] = "This is one"

dict[2] = "This is two" 

tinydict = {'name': 'john','code':6734, 'dept': 'sales'}

print dict['one']          # 输出键为'one' 的值

print dict[2]              # 输出键为 2 的值

print tinydict            # 输出完整的字典

print tinydict.keys()      # 输出所有键

print tinydict.values()    # 输出所有值

输出结果为:

This is oneThis is two{'dept': 'sales', 'code': 6734, 'name': 'john'}['dept', 'code', 'name']['sales', 6734, 'john']

Python数据类型转换

有时候,我们需要对数据内置的类型进行转换,数据类型的转换,你只需要将数据类型作为函数名即可。

以下几个内置的函数可以执行数据类型之间的转换。这些函数返回一个新的对象,表示转换的值。

函数描述

int(x [,base])将x转换为一个整数

long(x [,base] )将x转换为一个长整数

float(x)将x转换到一个浮点数

complex(real [,imag])创建一个复数

str(x)将对象 x 转换为字符串

repr(x)将对象 x 转换为表达式字符串

eval(str)用来计算在字符串中的有效Python表达式,并返回一个对象

tuple(s)将序列 s 转换为一个元组

list(s)将序列 s 转换为一个列表

set(s)转换为可变集合

dict(d)创建一个字典。d 必须是一个序列 (key,value)元组。

frozenset(s)转换为不可变集合

chr(x)将一个整数转换为一个字符

unichr(x)将一个整数转换为Unicode字符

ord(x)将一个字符转换为它的整数值

hex(x)将一个整数转换为一个十六进制字符串

oct(x)将一个整数转换为一个八进制字符串



Python算术运算符

以下假设变量: a=10,b=20

运算符描述实例

+加 - 两个对象相加a + b 输出结果 30

-减 - 得到负数或是一个数减去另一个数a - b 输出结果 -10

*乘 - 两个数相乘或是返回一个被重复若干次的字符串a * b 输出结果 200

/除 - x除以yb / a 输出结果 2

%取模 - 返回除法的余数b % a 输出结果 0

**幂 - 返回x的y次幂a**b 为10的20次方, 输出结果 100000000000000000000

//取整除 - 返回商的整数部分(向下取整)9//2 输出结果 4 , 9.0//2.0 输出结果 4.0


实例

#!/usr/bin/python

# -*- coding: UTF-8 -*-

a = 21

b = 10

c = 0

c = a + b

print "1 - c 的值为:", c

c = a - b

print "2 - c 的值为:", c

c = a * b

print "3 - c 的值为:", c

c = a / b

print "4 - c 的值为:", c

c = a % b

print "5 - c 的值为:", c

# 修改变量 a 、b 、c

a = 2

b = 3

c = a**b

print "6 - c 的值为:", c

a = 10

b = 5

c = a//b

print "7 - c 的值为:", c

结果



Python位运算符

按位运算符是把数字看作二进制来进行计算的。Python中的按位运算法则如下:

下表中变量 a 为 60,b 为 13,二进制格式如下:

#!/usr/bin/python

# -*- coding: UTF-8 -*-

a = 60

# 60 = 0011 1100

b = 13

# 13 = 0000 1101

c = 0











四、Python 条件语句和循环语句

4课时

实验课

主要内容

Python条件语句

是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。

可以通过下图来简单了解条件语句的执行过程:

Python程序语言指定任何非0和非空(null)值为true,0 或者 null为false。

Python 编程中 if 语句用于控制程序的执行,基本形式为:

if 判断条件: 

     执行语句……

else: 

     执行语句……

Python 循环语句

本章节将向大家介绍Python的循环语句,程序在一般情况下是按顺序执行的。

编程语言提供了各种控制结构,允许更复杂的执行路径。

循环语句允许我们执行一个语句或语句组多次,下面是在大多数编程语言中的循环语句的一般形式:

Python提供了for循环和while循环(在Python中没有do..while循环):



五、Python中的数据结构:字典

4课时

实验课

主要内容

Python 字典(Dictionary)

字典是另一种可变容器模型,且可存储任意类型对象。

字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,格式如下所示:

d = {key1 : value1, key2 : value2 }

键必须是唯一的,但值则不必。

值可以取任何数据类型,但键必须是不可变的,如字符串,数字或元组。

一个简单的字典实例:

dict = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}

也可如此创建字典:

dict1 = { 'abc': 456 };

dict2 = { 'abc': 123, 98.6: 37 };

访问字典里的值

把相应的键放入熟悉的方括弧,如下实例:

#!/usr/bin/python

dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'};

print "dict['Name']: ", dict['Name'];

print "dict['Age']: ", dict['Age'];

以上实例输出结果:

dict['Name']:  Zara

dict['Age']:  7

如果用字典里没有的键访问数据,会输出错误如下:

#!/usr/bin/python

dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'};

print "dict['Alice']: ", dict['Alice'];

以上实例输出结果:

dict['Zara']:

Traceback (most recent call last):

  File "test.py", line 4, in

    print "dict['Alice']: ", dict['Alice'];

KeyError: 'Alice'

修改字典

向字典添加新内容的方法是增加新的键/值对,修改或删除已有键/值对如下实例:

#!/usr/bin/python

dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'};

dict['Age'] = 8; # update existing entry

dict['School'] = "DPS School"; # Add new entry

print "dict['Age']: ", dict['Age'];

print "dict['School']: ", dict['School'];

以上实例输出结果:

dict['Age']:  8

dict['School']:  DPS School

删除字典元素

能删单一的元素也能清空字典,清空只需一项操作。

显示删除一个字典用del命令,如下实例:

#coding=utf-8

#!/usr/bin/python

dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'};

del dict['Name']; # 删除键是'Name'的条目

dict.clear();    # 清空词典所有条目

del dict ;        # 删除词典

print "dict['Age']: ", dict['Age'];

print "dict['School']: ", dict['School'];

但这会引发一个异常,因为用del后字典不再存在:

dict['Age']:

Traceback (most recent call last):

  File "test.py", line 8, in

    print "dict['Age']: ", dict['Age'];

TypeError: 'type' object is unsubscriptable

注:del()方法后面也会讨论。

字典键的特性

字典值可以没有限制地取任何python对象,既可以是标准的对象,也可以是用户定义的,但键不行。

两个重要的点需要记住:

1)不允许同一个键出现两次。创建时如果同一个键被赋值两次,后一个值会被记住,如下实例:

#!/usr/bin/python

dict = {'Name': 'Zara', 'Age': 7, 'Name': 'Manni'};

print "dict['Name']: ", dict['Name'];

以上实例输出结果:

dict['Name']:  Manni

2)键必须不可变,所以可以用数,字符串或元组充当,所以用列表就不行,如下实例:

#!/usr/bin/python

dict = {['Name']: 'Zara', 'Age': 7};

print "dict['Name']: ", dict['Name'];

以上实例输出结果:

Traceback (most recent call last):

  File "test.py", line 3, in

    dict = {['Name']: 'Zara', 'Age': 7};

TypeError: list objects are unhashable

字典内置函数&方法

Python字典包含了以下内置函数:

序号函数及描述

1cmp(dict1, dict2)

比较两个字典元素。

2len(dict)

计算字典元素个数,即键的总数。

3str(dict)

输出字典可打印的字符串表示。

4type(variable)

返回输入的变量类型,如果变量是字典就返回字典类型。

Python字典包含了以下内置函数:

序号函数及描述

1radiansdict.clear()

删除字典内所有元素

2radiansdict.copy()

返回一个字典的浅复制

3radiansdict.fromkeys()

创建一个新字典,以序列seq中元素做字典的键,val为字典所有键对应的初始值

4radiansdict.get(key, default=None)

返回指定键的值,如果值不在字典中返回default值

5radiansdict.has_key(key)

如果键在字典dict里返回true,否则返回false

6radiansdict.items()

以列表返回可遍历的(键, 值) 元组数组

7radiansdict.keys()

以列表返回一个字典所有的键

8radiansdict.setdefault(key, default=None)

和get()类似, 但如果键不已经存在于字典中,将会添加键并将值设为default

9radiansdict.update(dict2)

把字典dict2的键/值对更新到dict里

10radiansdict.values()

以列表返回字典中的所有值






六、Python的文件IO

4课时

实验课

主要内容

Python 文件I/O

打印到屏幕

最简单的输出方法是用print语句,你可以给它传递零个或多个用逗号隔开的表达式。此函数把你传递的表达式转换成一个字符串表达式,并将结果写到标准输出如下:

#!/usr/bin/python

print "Python is really a great language,", "isn't it?";

你的标准屏幕上会产生以下结果:

Python is really a great language, isn't it?

读取键盘输入

Python提供了两个内置函数从标准输入读入一行文本,默认的标准输入是键盘。如下:

raw_input

input

raw_input函数

raw_input([prompt]) 函数从标准输入读取一个行,并返回一个字符串(去掉结尾的换行符):

#!/usr/bin/python

# -*- coding: UTF-8 -*- 


str = raw_input("请输入:");

print "你输入的内容是: ", str

这将提示你输入任意字符串,然后在屏幕上显示相同的字符串。当我输入"Hello Python!",它的输出如下:

请输入:Hello Python!

你输入的内容是:  Hello Python!

input函数

input([prompt]) 函数和raw_input([prompt]) 函数基本可以互换,但是input会假设你的输入是一个有效的Python表达式,并返回运算结果。

#!/usr/bin/python

str = input("Enter your input: ");

print "Received input is : ", str

这会产生如下的对应着输入的结果:

Enter your input: [x*5 for x in range(2,10,2)]

Recieved input is :  [10, 20, 30, 40]

打开和关闭文件

到现在为止,你已经可以向标准输入和输出进行读写。现在,来看看怎么读写实际的数据文件。

Python提供了必要的函数和方法进行默认情况下的文件基本操作。你可以用file对象做大部分的文件操作。

open函数

你必须先用Python内置的open()函数打开一个文件,创建一个file对象,相关的辅助方法才可以调用它进行读写。

语法:

file object = open(file_name [, access_mode][, buffering])

各个参数的细节如下:

file_name:file_name变量是一个包含了你要访问的文件名称的字符串值。

access_mode:access_mode决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读(r)。

buffering:如果buffering的值被设为0,就不会有寄存。如果buffering的值取1,访问文件时会寄存行。如果将buffering的值设为大于1的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。

不同模式打开文件的完全列表:

模式描述

r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。

rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。

r+打开一个文件用于读写。文件指针将会放在文件的开头。

rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。

w打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

wb以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

w+打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

wb+以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。

ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

File对象的属性

一个文件被打开后,你有一个file对象,你可以得到有关该文件的各种信息。

以下是和file对象相关的所有属性的列表:

属性描述

file.closed如果文件已被关闭返回true,否则返回false。

file.mode返回被打开文件的访问模式。

file.name返回文件的名称。

file.softspace如果用print输出后,必须跟一个空格符,则返回false。否则返回true。

如下实例:

#!/usr/bin/python

# -*- coding: UTF-8 -*-


# 打开一个文件

fo = open("foo.txt", "wb")

print "文件名: ", fo.name

print "是否已关闭 : ", fo.closed

print "访问模式 : ", fo.mode

print "末尾是否强制加空格 : ", fo.softspace

以上实例输出结果:

文件名:  foo.txt

是否已关闭 :  False

访问模式 :  wb

末尾是否强制加空格 :  0

Close()方法

File对象的close()方法刷新缓冲区里任何还没写入的信息,并关闭该文件,这之后便不能再进行写入。

当一个文件对象的引用被重新指定给另一个文件时,Python会关闭之前的文件。用close()方法关闭文件是一个很好的习惯。

语法:

fileObject.close();

例子:

#coding=utf-8

#!/usr/bin/python

# 打开一个文件

fo = open("foo.txt", "wb")

print "文件名: ", fo.name

# 关闭打开的文件

fo.close()

以上实例输出结果:

文件名:  foo.txt

读写文件:

file对象提供了一系列方法,能让我们的文件访问更轻松。来看看如何使用read()和write()方法来读取和写入文件。

Write()方法

Write()方法可将任何字符串写入一个打开的文件。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字。

Write()方法不在字符串的结尾不添加换行符('\n'):

语法:

fileObject.write(string);

在这里,被传递的参数是要写入到已打开文件的内容。

例子:

#coding=utf-8

#!/usr/bin/python

# 打开一个文件

fo = open("/tmp/foo.txt", "wb")

fo.write( "Python is a great language.\nYeah its great!!\n");

# 关闭打开的文件

fo.close()

上述方法会创建foo.txt文件,并将收到的内容写入该文件,并最终关闭文件。如果你打开这个文件,将看到以下内容:

Python is a great language.

Yeah its great!!

read()方法

read()方法从一个打开的文件中读取一个字符串。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字。

语法:

fileObject.read([count]);

在这里,被传递的参数是要从已打开文件中读取的字节计数。该方法从文件的开头开始读入,如果没有传入count,它会尝试尽可能多地读取更多的内容,很可能是直到文件的末尾。

例子:

就用我们上面创建的文件foo.txt。

#coding=utf-8

#!/usr/bin/python

# 打开一个文件

fo = open("/tmp/foo.txt", "r+")

str = fo.read(10);

print "读取的字符串是: ", str

# 关闭打开的文件

fo.close()

以上实例输出结果:

读取的字符串是:  Python is

文件位置:

tell()方法告诉你文件内的当前位置;换句话说,下一次的读写会发生在文件开头这么多字节之后:

seek(offset [,from])方法改变当前文件的位置。Offset变量表示要移动的字节数。From变量指定开始移动字节的参考位置。

如果from被设为0,这意味着将文件的开头作为移动字节的参考位置。如果设为1,则使用当前的位置作为参考位置。如果它被设为2,那么该文件的末尾将作为参考位置。

例子:

就用我们上面创建的文件foo.txt。

#coding=utf-8

#!/usr/bin/python

# 打开一个文件

fo = open("/tmp/foo.txt", "r+")

str = fo.read(10);

print "读取的字符串是: ", str

# 查找当前位置

position = fo.tell();

print "当前文件位置: ", position

# 把指针再次重新定位到文件开头

position = fo.seek(0, 0);

str = fo.read(10);

print "重新读取字符串: ", str

# 关闭打开的文件

fo.close()

以上实例输出结果:

读取的字符串是:  Python is

当前文件位置:  10

重新读取字符串:  Python is

重命名和删除文件

Python的os模块提供了帮你执行文件处理操作的方法,比如重命名和删除文件。

要使用这个模块,你必须先导入它,然后可以调用相关的各种功能。

rename()方法:

rename()方法需要两个参数,当前的文件名和新文件名。

语法:

os.rename(current_file_name, new_file_name)

例子:

下例将重命名一个已经存在的文件test1.txt。

#coding=utf-8

#!/usr/bin/python

import os

# 重命名文件test1.txt到test2.txt。

os.rename( "test1.txt", "test2.txt" )

remove()方法

你可以用remove()方法删除文件,需要提供要删除的文件名作为参数。

语法:

os.remove(file_name)

例子:

下例将删除一个已经存在的文件test2.txt。

#coding=utf-8

#!/usr/bin/python

import os

# 删除一个已经存在的文件test2.txt

os.remove("text2.txt")

Python里的目录:

所有文件都包含在各个不同的目录下,不过Python也能轻松处理。os模块有许多方法能帮你创建,删除和更改目录。

mkdir()方法

可以使用os模块的mkdir()方法在当前目录下创建新的目录们。你需要提供一个包含了要创建的目录名称的参数。

语法:

os.mkdir("newdir")

例子:

下例将在当前目录下创建一个新目录test。

#coding=utf-8

#!/usr/bin/python

import os

# 创建目录test

os.mkdir("test")

chdir()方法

可以用chdir()方法来改变当前的目录。chdir()方法需要的一个参数是你想设成当前目录的目录名称。

语法:

os.chdir("newdir")

例子:

下例将进入"/home/newdir"目录。

#coding=utf-8

#!/usr/bin/python

import os

# 将当前目录改为"/home/newdir"

os.chdir("/home/newdir")

getcwd()方法:

getcwd()方法显示当前的工作目录。

语法:

os.getcwd()

例子:

下例给出当前目录:

#coding=utf-8

#!/usr/bin/python

import os

# 给出当前的目录

os.getcwd()

rmdir()方法

rmdir()方法删除目录,目录名称以参数传递。

在删除这个目录之前,它的所有内容应该先被清除。

语法:

os.rmdir('dirname')

例子:

以下是删除" /tmp/test"目录的例子。目录的完全合规的名称必须被给出,否则会在当前目录下搜索该目录。

#coding=utf-8

#!/usr/bin/python

import os

# 删除”/tmp/test”目录

os.rmdir( "/tmp/test"  )

文件、目录相关的方法

三个重要的方法来源能对Windows和Unix操作系统上的文件及目录进行一个广泛且实用的处理及操控,如下:

File 对象方法: file对象提供了操作文件的一系列方法。

OS 对象方法: 提供了处理文件及目录的一系列方法。






七、Python 面向对象

4课时

实验课

主要内容

Python 面向对象

Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。

如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。

接下来我们先来简单的了解下面向对象的一些基本特征。

面向对象技术简介

类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。

方法重载:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重载。

实例变量:定义在方法中的变量,只作用于当前实例的类。

继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。

实例化:创建一个类的实例,类的具体对象。

方法:类中定义的函数。

对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

创建类

使用class语句来创建一个新类,class之后为类的名称并以冒号结尾,如下实例:

class ClassName:

  '类的帮助信息'  #类文档字符串

  class_suite  #类体

类的帮助信息可以通过ClassName.__doc__查看。

class_suite 由类成员,方法,数据属性组成。

实例

以下是一个简单的Python类实例:

#coding=utf-8

class Employee:

  '所有员工的基类'

  empCount = 0

  def __init__(self, name, salary):

      self.name = name

      self.salary = salary

      Employee.empCount += 1


  def displayCount(self):

    print "Total Employee %d" % Employee.empCount

  def displayEmployee(self):

      print "Name : ", self.name,  ", Salary: ", self.salary

empCount变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用Employee.empCount访问。

第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法

创建实例对象

要创建一个类的实例,你可以使用类的名称,并通过__init__方法接受参数。

"创建 Employee 类的第一个对象"

emp1 = Employee("Zara", 2000)

"创建 Employee 类的第二个对象"

emp2 = Employee("Manni", 5000)

访问属性

您可以使用点(.)来访问对象的属性。使用如下类的名称访问类变量:

emp1.displayEmployee()

emp2.displayEmployee()

print "Total Employee %d" % Employee.empCount

完整实例:

#coding=utf-8

#!/usr/bin/python

class Employee:

  '所有员工的基类'

  empCount = 0

  def __init__(self, name, salary):

      self.name = name

      self.salary = salary

      Employee.empCount += 1


  def displayCount(self):

    print "Total Employee %d" % Employee.empCount

  def displayEmployee(self):

      print "Name : ", self.name,  ", Salary: ", self.salary

"创建 Employee 类的第一个对象"

emp1 = Employee("Zara", 2000)

"创建 Employee 类的第二个对象"

emp2 = Employee("Manni", 5000)

emp1.displayEmployee()

emp2.displayEmployee()

print "Total Employee %d" % Employee.empCount

执行以上代码输出结果如下:

Name :  Zara ,Salary:  2000

Name :  Manni ,Salary:  5000

Total Employee 2

你可以添加,删除,修改类的属性,如下所示:

emp1.age = 7  # 添加一个 'age' 属性

emp1.age = 8  # 修改 'age' 属性

del emp1.age  # 删除 'age' 属性

你也可以使用以下函数的方式来访问属性:

getattr(obj, name[, default]) : 访问对象的属性。

hasattr(obj,name) : 检查是否存在一个属性。

setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。

delattr(obj, name) : 删除属性。

hasattr(emp1, 'age')    # 如果存在 'age' 属性返回 True。

getattr(emp1, 'age')    # 返回 'age' 属性的值

setattr(emp1, 'age', 8) # 添加属性 'age' 值为 8

delattr(empl, 'age')    # 删除属性 'age'

Python内置类属性

__dict__ : 类的属性(包含一个字典,由类的数据属性组成)

__doc__ :类的文档字符串

__name__: 类名

__module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)

__bases__ : 类的所有父类构成元素(包含了以个由所有父类组成的元组)

Python内置类属性调用实例如下:

#coding=utf-8

#!/usr/bin/python

class Employee:

  '所有员工的基类'

  empCount = 0

  def __init__(self, name, salary):

      self.name = name

      self.salary = salary

      Employee.empCount += 1


  def displayCount(self):

    print "Total Employee %d" % Employee.empCount

  def displayEmployee(self):

      print "Name : ", self.name,  ", Salary: ", self.salary

print "Employee.__doc__:", Employee.__doc__

print "Employee.__name__:", Employee.__name__

print "Employee.__module__:", Employee.__module__

print "Employee.__bases__:", Employee.__bases__

print "Employee.__dict__:", Employee.__dict__

执行以上代码输出结果如下:

Employee.__doc__: 所有员工的基类

Employee.__name__: Employee

Employee.__module__: __main__

Employee.__bases__: ()

Employee.__dict__: {'__module__': '__main__', 'displayCount': , 'empCount': 0, 'displayEmployee': , '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\x91\x98\xe5\xb7\xa5\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': }

python对象销毁(垃圾回收)

同Java语言一样,Python使用了引用计数这一简单技术来追踪内存中的对象。

在Python内部记录着所有使用中的对象各有多少引用。

一个内部跟踪变量,称为一个引用计数器。

当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。

a = 40      # 创建对象  <40>

b = a      # 增加引用, <40> 的计数

c = [b]    # 增加引用.  <40> 的计数

del a      # 减少引用 <40> 的计数

b = 100    # 减少引用 <40> 的计数

c[0] = -1  # 减少引用 <40> 的计数

垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。循环引用指的是,两个对象相互引用,但是没有其他变量引用他们。这种情况下,仅使用引用计数是不够的。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些)的对象。 在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环。

实例

析构函数 __del__ ,__del__在对象消逝的时候被调用,当对象不再被使用时,__del__方法运行:

#coding=utf-8

#!/usr/bin/python

class Point:

  def __init( self, x=0, y=0):

      self.x = x

      self.y = y

  def __del__(self):

      class_name = self.__class__.__name__

      print class_name, "destroyed"

pt1 = Point()

pt2 = pt1

pt3 = pt1

print id(pt1), id(pt2), id(pt3) # 打印对象的id

del pt1

del pt2

del pt3

以上实例运行结果如下:

3083401324 3083401324 3083401324

Point destroyed

注意:通常你需要在单独的文件中定义一个类,

类的继承

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。

需要注意的地方:继承语法 class 派生类名(基类名)://... 基类名写作括号里,基本类是在类定义的时候,在元组之中指明的。

在python中继承中的一些特点:

1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。

2:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数

3:Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。

语法:

派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:

class SubClassName (ParentClass1[, ParentClass2, ...]):

  'Optional class documentation string'

  class_suite

实例:

#coding=utf-8

#!/usr/bin/python

class Parent:        # 定义父类

  parentAttr = 100

  def __init__(self):

      print "调用父类构造函数"

  def parentMethod(self):

      print '调用父类方法'

  def setAttr(self, attr):

      Parent.parentAttr = attr

  def getAttr(self):

      print "父类属性 :", Parent.parentAttr

class Child(Parent): # 定义子类

  def __init__(self):

      print "调用子类构造方法"

  def childMethod(self):

      print '调用子类方法 child method'

c = Child()          # 实例化子类

c.childMethod()      # 调用子类的方法

c.parentMethod()    # 调用父类方法

c.setAttr(200)      # 再次调用父类的方法

c.getAttr()          # 再次调用父类的方法

以上代码执行结果如下:

调用子类构造方法

调用子类方法 child method

调用父类方法

父类属性 : 200

你可以继承多个类

class A:        # 定义类 A

.....

class B:        # 定义类 B

.....

class C(A, B):  # 继承类 A 和 B

.....

你可以使用issubclass()或者isinstance()方法来检测。

issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)

isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

方法重写

如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:

实例:

#coding=utf-8

#!/usr/bin/python

class Parent:        # 定义父类

  def myMethod(self):

      print '调用父类方法'

class Child(Parent): # 定义子类

  def myMethod(self):

      print '调用子类方法'

c = Child()          # 子类实例

c.myMethod()        # 子类调用重写方法

执行以上代码输出结果如下:

调用子类方法

基础重载方法

下表列出了一些通用的功能,你可以在自己的类重写:

序号方法, 描述 & 简单的调用

1__init__ ( self [,args...] )

构造函数

简单的调用方法: obj = className(args)

2__del__( self )

析构方法, 删除一个对象

简单的调用方法 : dell obj

3__repr__( self )

转化为供解释器读取的形式

简单的调用方法 : repr(obj)

4__str__( self )

用于将值转化为适于人阅读的形式

简单的调用方法 : str(obj)

5__cmp__ ( self, x )

对象比较

简单的调用方法 : cmp(obj, x)

运算符重载

Python同样支持运算符重载,实例如下:

#!/usr/bin/python

class Vector:

  def __init__(self, a, b):

      self.a = a

      self.b = b

  def __str__(self):

      return 'Vector (%d, %d)' % (self.a, self.b)


  def __add__(self,other):

      return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2,10)

v2 = Vector(5,-2)

print v1 + v2

以上代码执行结果如下所示:

Vector(7,8)

类属性与方法

类的私有属性

__private_attrs:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs

类的方法

在类地内部,使用def关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数self,且为第一个参数

类的私有方法

__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用 self.__private_methods

实例

#coding=utf-8

#!/usr/bin/python

class JustCounter:

__secretCount = 0  # 私有变量

publicCount = 0    # 公开变量

def count(self):

self.__secretCount += 1

self.publicCount += 1

print self.__secretCount

counter = JustCounter()

counter.count()

counter.count()

print counter.publicCount

print counter.__secretCount  # 报错,实例不能访问私有变量

Python 通过改变名称来包含类名:

1

2

2

Traceback (most recent call last):

  File "test.py", line 17, in

    print counter.__secretCount  # 报错,实例不能访问私有变量

AttributeError: JustCounter instance has no attribute '__secretCount'

Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName 访问属性,将如下代码替换以上代码的最后一行代码:

.........................

print counter._JustCounter__secretCount

执行以上代码,执行结果如下:

1

2

2

2





八、python操作mysql数据库

4课时

实验课

主要内容

python操作mysql数据库

Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口。

Python 数据库接口支持非常多的数据库,你可以选择适合你项目的数据库:

GadFly

mSQL

MySQL

PostgreSQL

Microsoft SQL Server 2000

Informix

Interbase

Oracle

Sybase

不同的数据库你需要下载不同的DB API模块,例如你需要访问Oracle数据库和Mysql数据,你需要下载Oracle和MySQL数据库模块。

DB-API 是一个规范. 它定义了一系列必须的对象和数据库存取方式, 以便为各种各样的底层数据库系统和多种多样的数据库接口程序提供一致的访问接口 。

Python的DB-API,为大多数的数据库实现了接口,使用它连接各数据库后,就可以用相同的方式操作各数据库。

Python DB-API使用流程:

引入 API 模块。

获取与数据库的连接。

执行SQL语句和存储过程。

关闭数据库连接。

什么是MySQLdb?

MySQLdb 是用于Python链接Mysql数据库的接口,它实现了 Python 数据库 API 规范 V2.0,基于 MySQL C API 上建立的。

如何安装MySQLdb?

为了用DB-API编写MySQL脚本,必须确保已经安装了MySQL。复制以下代码,并执行:

# encoding: utf-8

#!/usr/bin/python

import MySQLdb

如果执行后的输出结果如下所示,意味着你没有安装 MySQLdb 模块:

Traceback (most recent call last):

  File "test.py", line 3, in

    import MySQLdb

ImportError: No module named MySQLdb

安装MySQLdb,请访问 http://sourceforge.net/projects/mysql-python ,(Linux平台可以访问:https://pypi.python.org/pypi/MySQL-python)从这里可选择适合您的平台的安装包,分为预编译的二进制文件和源代码安装包。

如果您选择二进制文件发行版本的话,安装过程基本安装提示即可完成。如果从源代码进行安装的话,则需要切换到MySQLdb发行版本的顶级目录,并键入下列命令:

$ gunzip MySQL-python-1.2.2.tar.gz

$ tar -xvf MySQL-python-1.2.2.tar

$ cd MySQL-python-1.2.2

$ python setup.py build

$ python setup.py install

注意:请确保您有root权限来安装上述模块。

数据库连接

连接数据库前,请先确认以下事项:

您已经创建了数据库 TESTDB.

在TESTDB数据库中您已经创建了表 EMPLOYEE

EMPLOYEE表字段为 FIRST_NAME, LAST_NAME, AGE, SEX 和 INCOME。

连接数据库TESTDB使用的用户名为 "testuser" ,密码为 "test123",你可以可以自己设定或者直接使用root用户名及其密码,Mysql数据库用户授权请使用Grant命令。

在你的机子上已经安装了 Python MySQLdb 模块。

如果您对sql语句不熟悉,可以访问我们的 SQL基础教程

实例:

以下实例链接Mysql的TESTDB数据库:

# encoding: utf-8

#!/usr/bin/python

import MySQLdb

# 打开数据库连接

db = MySQLdb.connect("localhost","testuser","test123","TESTDB" )

# 使用cursor()方法获取操作游标

cursor = db.cursor()

# 使用execute方法执行SQL语句

cursor.execute("SELECT VERSION()")

# 使用 fetchone() 方法获取一条数据库。

data = cursor.fetchone()

print "Database version : %s " % data

# 关闭数据库连接

db.close()

执行以上脚本输出结果如下:

Database version : 5.0.45

创建数据库表

如果数据库连接存在我们可以使用execute()方法来为数据库创建表,如下所示创建表EMPLOYEE:

# encoding: utf-8

#!/usr/bin/python

import MySQLdb

# 打开数据库连接

db = MySQLdb.connect("localhost","testuser","test123","TESTDB" )

# 使用cursor()方法获取操作游标

cursor = db.cursor()

# 如果数据表已经存在使用 execute() 方法删除表。

cursor.execute("DROP TABLE IF EXISTS EMPLOYEE")

# 创建数据表SQL语句

sql = """CREATE TABLE EMPLOYEE (

        FIRST_NAME  CHAR(20) NOT NULL,

        LAST_NAME  CHAR(20),

        AGE INT, 

        SEX CHAR(1),

        INCOME FLOAT )"""

cursor.execute(sql)

# 关闭数据库连接

db.close()

数据库插入操作

以下实例使用执行 SQL INSERT 语句向表 EMPLOYEE 插入记录:

# encoding: utf-8

#!/usr/bin/python

import MySQLdb

# 打开数据库连接

db = MySQLdb.connect("localhost","testuser","test123","TESTDB" )

# 使用cursor()方法获取操作游标

cursor = db.cursor()

# SQL 插入语句

sql = """INSERT INTO EMPLOYEE(FIRST_NAME,

        LAST_NAME, AGE, SEX, INCOME)

        VALUES ('Mac', 'Mohan', 20, 'M', 2000)"""

try:

  # 执行sql语句

  cursor.execute(sql)

  # 提交到数据库执行

  db.commit()

except:

  # Rollback in case there is any error

  db.rollback()

# 关闭数据库连接

db.close()

以上例子也可以写成如下形式:

# encoding: utf-8

#!/usr/bin/python

import MySQLdb

# 打开数据库连接

db = MySQLdb.connect("localhost","testuser","test123","TESTDB" )

# 使用cursor()方法获取操作游标

cursor = db.cursor()

# SQL 插入语句

sql = "INSERT INTO EMPLOYEE(FIRST_NAME, \

      LAST_NAME, AGE, SEX, INCOME) \

      VALUES ('%s', '%s', '%d', '%c', '%d' )" % \

      ('Mac', 'Mohan', 20, 'M', 2000)

try:

  # 执行sql语句

  cursor.execute(sql)

  # 提交到数据库执行

  db.commit()

except:

  # 发生错误时回滚

  db.rollback()

# 关闭数据库连接

db.close()

实例:

以下代码使用变量向SQL语句中传递参数:

..................................

user_id = "test123"

password = "password"

con.execute('insert into Login values("%s", "%s")' % \

            (user_id, password))

..................................

数据库查询操作

Python查询Mysql使用 fetchone() 方法获取单条数据, 使用fetchall() 方法获取多条数据。

fetchone(): 该方法获取下一个查询结果集。结果集是一个对象

fetchall():接收全部的返回结果行.

rowcount: 这是一个只读属性,并返回执行execute()方法后影响的行数。

实例:

查询EMPLOYEE表中salary(工资)字段大于1000的所有数据:

# encoding: utf-8

#!/usr/bin/python

import MySQLdb

# 打开数据库连接

db = MySQLdb.connect("localhost","testuser","test123","TESTDB" )

# 使用cursor()方法获取操作游标

cursor = db.cursor()

# SQL 查询语句

sql = "SELECT * FROM EMPLOYEE \

      WHERE INCOME > '%d'" % (1000)

try:

  # 执行SQL语句

  cursor.execute(sql)

  # 获取所有记录列表

  results = cursor.fetchall()

  for row in results:

      fname = row[0]

      lname = row[1]

      age = row[2]

      sex = row[3]

      income = row[4]

      # 打印结果

      print "fname=%s,lname=%s,age=%d,sex=%s,income=%d" % \

            (fname, lname, age, sex, income )

except:

  print "Error: unable to fecth data"

# 关闭数据库连接

db.close()

以上脚本执行结果如下:

fname=Mac, lname=Mohan, age=20, sex=M, income=2000

数据库更新操作

更新操作用于更新数据表的的数据,以下实例将 TESTDB表中的 SEX 字段全部修改为 'M',AGE 字段递增1:

# encoding: utf-8#!/usr/bin/pythonimport MySQLdb# 打开数据库连接db = MySQLdb.connect("localhost","testuser","test123","TESTDB" )# 使用cursor()方法获取操作游标 cursor = db.cursor()# SQL 更新语句sql = "UPDATE EMPLOYEE SET AGE = AGE + 1 \

WHERE SEX = '%c'" % ('M')try:  # 执行SQL语句  cursor.execute(sql)  # 提交到数据库执行  db.commit()except:  # 发生错误时回滚  db.rollback()# 关闭数据库连接db.close()

执行事务

事务机制可以确保数据一致性。

事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。

原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。

一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。

隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

Python DB API 2.0 的事务提供了两个方法 commit 或 rollback。

实例:

# SQL删除记录语句

sql = "DELETE FROM EMPLOYEE WHERE AGE > '%d'" % (20)

try:

  # 执行SQL语句

  cursor.execute(sql)

  # 向数据库提交

  db.commit()

except:

  # 发生错误时回滚

  db.rollback()

对于支持事务的数据库, 在Python数据库编程中,当游标建立之时,就自动开始了一个隐形的数据库事务。

commit()方法游标的所有更新操作,rollback()方法回滚当前游标的所有操作。每一个方法都开始了一个新的事务。

错误处理

DB API中定义了一些数据库操作的错误及异常,下表列出了这些错误和异常:

异常描述

Warning当有严重警告时触发,例如插入数据是被截断等等。必须是 StandardError 的子类。

Error警告以外所有其他错误类。必须是 StandardError 的子类。

InterfaceError当有数据库接口模块本身的错误(而不是数据库的错误)发生时触发。 必须是Error的子类。

DatabaseError和数据库有关的错误发生时触发。 必须是Error的子类。

DataError当有数据处理时的错误发生时触发,例如:除零错误,数据超范围等等。 必须是DatabaseError的子类。

OperationalError指非用户控制的,而是操作数据库时发生的错误。例如:连接意外断开、 数据库名未找到、事务处理失败、内存分配错误等等操作数据库是发生的错误。 必须是DatabaseError的子类。

IntegrityError完整性相关的错误,例如外键检查失败等。必须是DatabaseError子类。

InternalError数据库的内部错误,例如游标(cursor)失效了、事务同步失败等等。 必须是DatabaseError子类。

ProgrammingError程序错误,例如数据表(table)没找到或已存在、SQL语句语法错误、 参数数量错误等等。必须是DatabaseError的子类。

NotSupportedError不支持错误,指使用了数据库不支持的函数或API等。例如在连接对象上 使用.rollback()函数,然而数据库并不支持事务或者事务已关闭。 必须是DatabaseError的子类。





九、Python 网络编程

4课时

实验课

主要内容

Python 网络编程

Python 提供了两个级别访问的网络服务:

低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。

高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。

什么是 Socket?

Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。

socket()函数

Python 中,我们用 socket()函数来创建套接字,语法格式如下:

socket.socket([family[, type[, proto]]])

参数

family: 套接字家族可以使AF_UNIX或者AF_INET

type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM

protocol: 一般不填默认为0.

Socket 对象(内建)方法

函数描述

服务器端套接字

s.bind()绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。

s.listen()开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。

s.accept()被动接受TCP客户端连接,(阻塞式)等待连接的到来

客户端套接字

s.connect()主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

s.connect_ex()connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数

s.recv()接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。

s.send()发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。

s.sendall()完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

s.recvform()接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

s.sendto()发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。

s.close()关闭套接字

s.getpeername()返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

s.getsockname()返回套接字自己的地址。通常是一个元组(ipaddr,port)

s.setsockopt(level,optname,value)设置给定套接字选项的值。

s.getsockopt(level,optname[.buflen])返回套接字选项的值。

s.settimeout(timeout)设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())

s.gettimeout()返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。

s.fileno()返回套接字的文件描述符。

s.setblocking(flag)如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。

s.makefile()创建一个与该套接字相关连的文件

简单实例

服务端

我们使用 socket 模块的 socket 函数来创建一个 socket 对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。

现在我们可以通过调用 bind(hostname, port) 函数来指定服务的 port(端口)

接着,我们调用 socket 对象的 accept 方法。该方法等待客户端的连接,并返回 connection 对象,表示已连接到客户端。

完整代码如下:

#!/usr/bin/python

# -*- coding: UTF-8 -*-

# 文件名:server.py

import socket              # 导入 socket 模块

s = socket.socket()        # 创建 socket 对象

host = socket.gethostname() # 获取本地主机名

port = 12345                # 设置端口

s.bind((host, port))        # 绑定端口

s.listen(5)                # 等待客户端连接

while True:

    c, addr = s.accept()    # 建立客户端连接。

    print '连接地址:', addr

    c.send('欢迎访问W3Cschool教程!')

    c.close()                # 关闭连接

客户端

接下来我们写一个简单的客户端实例连接到以上创建的服务。端口号为 12345。

socket.connect(hosname, port ) 方法打开一个 TCP 连接到主机为 hostname 端口为 port 的服务商。连接后我们就可以从服务端后期数据,记住,操作完成后需要关闭连接。

完整代码如下:

#!/usr/bin/python

# -*- coding: UTF-8 -*-

# 文件名:client.py

import socket              # 导入 socket 模块

s = socket.socket()        # 创建 socket 对象

host = socket.gethostname() # 获取本地主机名

port = 12345                # 设置端口好

s.connect((host, port))

print s.recv(1024)

s.close() 

现在我们打开连个终端,第一个终端执行 server.py 文件:

$ python server.py

第二个终端执行 client.py 文件:

$ python client.py

欢迎访问W3Cschool教程!

这是我们再打开第一个终端,就会看到有以下信息输出:

连接地址: ('192.168.0.118', 62461)

Python Internet 模块

以下列出了 Python 网络编程的一些重要模块:

协议功能用处端口号Python 模块

HTTP网页访问80httplib, urllib, xmlrpclib

NNTP阅读和张贴新闻文章,俗称为"帖子"119nntplib

FTP文件传输20ftplib, urllib

SMTP发送邮件25smtplib

POP3接收邮件110poplib

IMAP4获取邮件143imaplib

Telnet命令行23telnetlib

Gopher信息查找70gopherlib, urllib







十、Python 多线程

4课时

实验课

主要内容

Python 多线程

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

使用线程可以把占据长时间的程序中的任务放到后台去处理。

用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度

程序的运行速度可能加快

在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。

指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

线程可以被抢占(中断)。

在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。

开始学习Python线程

Python中使用线程有两种方式:函数或者用类来包装线程对象。

函数式:调用thread模块中的start_new_thread()函数来产生新线程。语法如下:

thread.start_new_thread ( function, args[, kwargs] )

参数说明:

function - 线程函数。

args - 传递给线程函数的参数,他必须是个tuple类型。

kwargs - 可选参数。

实例:

#!/usr/bin/python

# -*- coding: UTF-8 -*-

import thread

import time

# 为线程定义一个函数

def print_time( threadName, delay):

   count = 0

   while count < 5:

      time.sleep(delay)

      count += 1

      print "%s: %s" % ( threadName, time.ctime(time.time()) )

# 创建两个线程

try:

   thread.start_new_thread( print_time, ("Thread-1", 2, ) )

   thread.start_new_thread( print_time, ("Thread-2", 4, ) )

except:

   print "Error: unable to start thread"

while 1:

   pass

执行以上程序输出结果如下:

Thread-1: Thu Jan 22 15:42:17 2009

Thread-1: Thu Jan 22 15:42:19 2009

Thread-2: Thu Jan 22 15:42:19 2009

Thread-1: Thu Jan 22 15:42:21 2009

Thread-2: Thu Jan 22 15:42:23 2009

Thread-1: Thu Jan 22 15:42:23 2009

Thread-1: Thu Jan 22 15:42:25 2009

Thread-2: Thu Jan 22 15:42:27 2009

Thread-2: Thu Jan 22 15:42:31 2009

Thread-2: Thu Jan 22 15:42:35 2009

线程的结束一般依靠线程函数的自然结束;也可以在线程函数中调用thread.exit(),他抛出SystemExit exception,达到退出线程的目的。

线程模块

Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。

thread 模块提供的其他方法:

threading.currentThread(): 返回当前的线程变量。

threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:

run(): 用以表示线程活动的方法。

start():启动线程活动。

join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。

isAlive(): 返回线程是否活动的。

getName(): 返回线程名。

setName(): 设置线程名。

使用Threading模块创建线程

使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法:

#coding=utf-8

#!/usr/bin/python

import threading

import time

exitFlag = 0

class myThread (threading.Thread):  #继承父类threading.Thread

    def __init__(self, threadID, name, counter):

        threading.Thread.__init__(self)

        self.threadID = threadID

        self.name = name

        self.counter = counter

    def run(self):                  #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数

        print "Starting " + self.name

        print_time(self.name, self.counter, 5)

        print "Exiting " + self.name

def print_time(threadName, delay, counter):

    while counter:

        if exitFlag:

            thread.exit()

        time.sleep(delay)

        print "%s: %s" % (threadName, time.ctime(time.time()))

        counter -= 1

# 创建新线程

thread1 = myThread(1, "Thread-1", 1)

thread2 = myThread(2, "Thread-2", 2)

# 开启线程

thread1.start()

thread2.start()

print "Exiting Main Thread"

以上程序执行结果如下;

Starting Thread-1

Starting Thread-2

Exiting Main Thread

Thread-1: Thu Mar 21 09:10:03 2013

Thread-1: Thu Mar 21 09:10:04 2013

Thread-2: Thu Mar 21 09:10:04 2013

Thread-1: Thu Mar 21 09:10:05 2013

Thread-1: Thu Mar 21 09:10:06 2013

Thread-2: Thu Mar 21 09:10:06 2013

Thread-1: Thu Mar 21 09:10:07 2013

Exiting Thread-1

Thread-2: Thu Mar 21 09:10:08 2013

Thread-2: Thu Mar 21 09:10:10 2013

Thread-2: Thu Mar 21 09:10:12 2013

Exiting Thread-2

线程同步

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。如下:

多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。

考虑这样一种情况:一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。

那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。

锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。

经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。

实例:

#coding=utf-8

#!/usr/bin/python

import threading

import time

class myThread (threading.Thread):

    def __init__(self, threadID, name, counter):

        threading.Thread.__init__(self)

        self.threadID = threadID

        self.name = name

        self.counter = counter

    def run(self):

        print "Starting " + self.name

      # 获得锁,成功获得锁定后返回True

      # 可选的timeout参数不填时将一直阻塞直到获得锁定

      # 否则超时后将返回False

        threadLock.acquire()

        print_time(self.name, self.counter, 3)

        # 释放锁

        threadLock.release()

def print_time(threadName, delay, counter):

    while counter:

        time.sleep(delay)

        print "%s: %s" % (threadName, time.ctime(time.time()))

        counter -= 1

threadLock = threading.Lock()

threads = []

# 创建新线程

thread1 = myThread(1, "Thread-1", 1)

thread2 = myThread(2, "Thread-2", 2)

# 开启新线程

thread1.start()

thread2.start()

# 添加线程到线程列表

threads.append(thread1)

threads.append(thread2)

# 等待所有线程完成

for t in threads:

    t.join()

print "Exiting Main Thread"

线程优先级队列( Queue)

Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。

Queue模块中的常用方法:

Queue.qsize() 返回队列的大小

Queue.empty() 如果队列为空,返回True,反之False

Queue.full() 如果队列满了,返回True,反之False

Queue.full 与 maxsize 大小对应

Queue.get([block[, timeout]])获取队列,timeout等待时间

Queue.get_nowait() 相当Queue.get(False)

Queue.put(item) 写入队列,timeout等待时间

Queue.put_nowait(item) 相当Queue.put(item, False)

Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号

Queue.join() 实际上意味着等到队列为空,再执行别的操作

实例:

#coding=utf-8

#!/usr/bin/python

import Queue

import threading

import time

exitFlag = 0

class myThread (threading.Thread):

    def __init__(self, threadID, name, q):

        threading.Thread.__init__(self)

        self.threadID = threadID

        self.name = name

        self.q = q

    def run(self):

        print "Starting " + self.name

        process_data(self.name, self.q)

        print "Exiting " + self.name

def process_data(threadName, q):

    while not exitFlag:

        queueLock.acquire()

        if not workQueue.empty():

            data = q.get()

            queueLock.release()

            print "%s processing %s" % (threadName, data)

        else:

            queueLock.release()

        time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]

nameList = ["One", "Two", "Three", "Four", "Five"]

queueLock = threading.Lock()

workQueue = Queue.Queue(10)

threads = []

threadID = 1

# 创建新线程

for tName in threadList:

    thread = myThread(threadID, tName, workQueue)

    thread.start()

    threads.append(thread)

    threadID += 1

# 填充队列

queueLock.acquire()

for word in nameList:

    workQueue.put(word)

queueLock.release()

# 等待队列清空

while not workQueue.empty():

    pass

# 通知线程是时候退出

exitFlag = 1

# 等待所有线程完成

for t in threads:

    t.join()

print "Exiting Main Thread"

以上程序执行结果:

Starting Thread-1

Starting Thread-2

Starting Thread-3

Thread-1 processing One

Thread-2 processing Two

Thread-3 processing Three

Thread-1 processing Four

Thread-2 processing Five

Exiting Thread-3

Exiting Thread-1

Exiting Thread-2

Exiting Main Thread




十一、Linux 入门和安装

4课时

实验课

主要内容

Linux 简介

Linux内核最初只是由芬兰人李纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的。

Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。

Linux能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

Linux的发行版

Linux的发行版说简单点就是将Linux内核与应用软件做一个打包。

目前市面上较知名的发行版有:Ubuntu、RedHat、CentOS、Debian、Fedora、SuSE、OpenSUSE、TurboLinux、BluePoint、RedFlag、Xterm、SlackWare等。

Linux应用领域

今天各种场合都有使用各种Linux发行版,从嵌入式设备到超级计算机,并且在服务器领域确定了地位,通常服务器使用LAMP(Linux + Apache + MySQL + PHP)或LNMP(Linux + Nginx+ MySQL + PHP)组合。

目前Linux不仅在家庭与企业中使用,并且在政府中也很受欢迎。

巴西联邦政府由于支持Linux而世界闻名。

有新闻报道俄罗斯军队自己制造的Linux发布版的,做为G.H.ost项目已经取得成果.

印度的Kerala联邦计划在向全联邦的高中推广使用Linux。

中华人民共和国为取得技术独立,在龙芯过程中排他性地使用Linux。

在西班牙的一些地区开发了自己的Linux发布版,并且在政府与教育领域广泛使用,如Extremadura地区的gnuLinEx和Andalusia地区的Guadalinex。

葡萄牙同样使用自己的Linux发布版Caixa Mágica,用于Magalh?es笔记本电脑和e-escola政府软件。

法国和德国同样开始逐步采用Linux。

Linux vs Window

目前国内Linux更多的是应用于服务器上,而桌面操作系统更多使用的是Window。主要区别如下:

比较WindowsLinux

界面界面统一,外壳程序固定所有Windows程序菜单几乎一致,快捷键也几乎相同图形界面风格依发布版不同而不同,可能互不兼容。GNU/Linux的终端机是从UNIX传承下来,基本命令和操作方法也几乎一致。

驱动程序驱动程序丰富,版本更新频繁。默认安装程序里面一般包含有该版本发布时流行的硬件驱动程序,之后所出的新硬件驱动依赖于硬件厂商提供。对于一些老硬件,如果没有了原配的驱动有时很难支持。另外,有时硬件厂商未提供所需版本的Windows下的驱动,也会比较头痛。由志愿者开发,由Linux核心开发小组发布,很多硬件厂商基于版权考虑并未提供驱动程序,尽管多数无需手动安装,但是涉及安装则相对复杂,使得新用户面对驱动程序问题(是否存在和安装方法)会一筹莫展。但是在开源开发模式下,许多老硬件尽管在Windows下很难支持的也容易找到驱动。HP、Intel、AMD等硬件厂商逐步不同程度支持开源驱动,问题正在得到缓解。

使用使用比较简单,容易入门。图形化界面对没有计算机背景知识的用户使用十分有利。图形界面使用简单,容易入门。文字界面,需要学习才能掌握。

学习系统构造复杂、变化频繁,且知识、技能淘汰快,深入学习困难。系统构造简单、稳定,且知识、技能传承性好,深入学习相对容易。

软件每一种特定功能可能都需要商业软件的支持,需要购买相应的授权。大部分软件都可以自由获取,同样功能的软件选择较少。


Linux 安装

centos6.4 为例。

centos6.4 下载地址:

网易镜像:http://mirrors.163.com/centos/6/isos/

搜狐镜像:http://mirrors.sohu.com/centos/6/isos/

注:建议安装64位Linux系统。

接下来你需要将下载的Linux系统刻录成光盘或U盘。

注:你也可以在Window上安装VMware虚拟机来安装Linux系统。

Linux 安装步骤

1、首先,使用光驱或U盘或你下载的Linux ISO文件进行安装。

界面说明:

Install or upgrade an existing system 安装或升级现有的系统

install system with basic video driver 安装过程中采用基本的显卡驱动

Rescue installed system 进入系统修复模式

Boot from local drive   退出安装从硬盘启动

Memory test  内存检测

注:用联想E49安装时选择第一项安装时会出现屏幕显示异常的问题,后改用第二项安装时就没有出现问题

2、介质直接"skip"就可以了

3、出现引导界面,点击"next"

4、选中"English(English)"否则会有部分乱码问题

5、键盘布局选择"U.S.English"

6、选择"Basic Storage Devies"点击"Next"

7、询问是否忽略所有数据,新电脑安装系统选择"Yes,discard any data"

8、Hostname填写格式"英文名.姓"

9、网络设置安装图示顺序点击就可以了

10、时区可以在地图上点击,选择"shanghai"并取消System clock uses UTC前面的对勾

11、设置root的密码

12、硬盘分区,一定要按照图示点选

13、调整分区,必须要有/home这个分区,如果没有这个分区,安装部分软件会出现不能安装的问题

14、询问是否格式化分区

15、将更改写入到硬盘

16、引导程序安装位置

17、最重要的一步,也是本教程最关键的一步,也是其他教程没有提及的一步,按图示顺序点击

18、取消以下内容的所有选项

Applications

Base System

Servers

并对Desktops进行如下设置

即取消如下选项:

Desktop Debugging and Performance Tools

Desktop Platform

Remote Desktop Clients

Input Methods中仅保留ibus-pinyin-1.3.8-1.el6.x86_64,其他的全部取消

19、选中Languages,并选中右侧的Chinese Support然后点击红色区域

20、调整完成后如下图所示

21、至此,一个最精简的桌面环境就设置完成了,

22、安装完成,重启

23、重启之后,的License Information

24、Create User

Username:填写您的英文名(不带.姓)

Full Name:填写您的英文名.姓(首字母大写)

25、"Date and Time" 选中 "Synchronize data and time over the network"

Finsh之后系统将重启

26、第一次登录,登录前不要做任何更改,这个很重要!!!登录之后紧接着退出

第二次登录,选择语言,在红色区域选择下拉小三角,选other,选中"汉语(中国)"

27、登录之后,请一定按照如下顺序点击!

至此,CentOS安装完成,如有其他问题,请随时与我联系!!




十二、Linux系统启动过程和目录结构

4课时

实验课

主要内容

Linux 系统启动过程

linux启动时我们会看到许多启动信息。

Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段:

内核的引导。

运行init。

系统初始化。

建立终端 。

用户登录系统。

内核引导

当计算机打开电源后,首先是BIOS开机自检,按照BIOS中设置的启动设备(通常是硬盘)来启动。

操作系统接管硬件以后,首先读入 /boot 目录下的内核文件。

运行init

init 进程是系统所有进程的起点,你可以把它比拟成系统所有进程的老祖宗,没有这个进程,系统中任何进程都不会启动。

init 程序首先是需要读取配置文件 /etc/inittab。

运行级别

许多程序需要开机启动。它们在Windows叫做"服务"(service),在Linux就叫做"守护进程"(daemon)。

init进程的一大任务,就是去运行这些开机启动的程序。

但是,不同的场合需要启动不同的程序,比如用作服务器时,需要启动Apache,用作桌面就不需要。

Linux允许为不同的场合,分配不同的开机启动程序,这就叫做"运行级别"(runlevel)。也就是说,启动时根据"运行级别",确定要运行哪些程序。

Linux系统有7个运行级别(runlevel):

运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动

运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆

运行级别2:多用户状态(没有NFS)

运行级别3:完全的多用户状态(有NFS),登陆后进入控制台命令行模式

运行级别4:系统未使用,保留

运行级别5:X11控制台,登陆后进入图形GUI模式

运行级别6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动

系统初始化

在init的配置文件中有这么一行: si::sysinit:/etc/rc.d/rc.sysinit 它调用执行了/etc/rc.d/rc.sysinit,而rc.sysinit是一个bash shell的脚本,它主要是完成一些系统初始化的工作,rc.sysinit是每一个运行级别都要首先运行的重要脚本。

它主要完成的工作有:激活交换分区,检查磁盘,加载硬件模块以及其它一些需要优先执行任务。

l5:5:wait:/etc/rc.d/rc 5

这一行表示以5为参数运行/etc/rc.d/rc,/etc/rc.d/rc是一个Shell脚本,它接受5作为参数,去执行/etc/rc.d/rc5.d/目录下的所有的rc启动脚本,/etc/rc.d/rc5.d/目录中的这些启动脚本实际上都是一些连接文件,而不是真正的rc启动脚本,真正的rc启动脚本实际上都是放在/etc/rc.d/init.d/目录下。

而这些rc启动脚本有着类似的用法,它们一般能接受start、stop、restart、status等参数。

/etc/rc.d/rc5.d/中的rc启动脚本通常是K或S开头的连接文件,对于以以S开头的启动脚本,将以start参数来运行。

而如果发现存在相应的脚本也存在K打头的连接,而且已经处于运行态了(以/var/lock/subsys/下的文件作为标志),则将首先以stop为参数停止这些已经启动了的守护进程,然后再重新运行。

这样做是为了保证是当init改变运行级别时,所有相关的守护进程都将重启。

至于在每个运行级中将运行哪些守护进程,用户可以通过chkconfig或setup中的"System Services"来自行设定。

建立终端

rc执行完毕后,返回init。这时基本系统环境已经设置好了,各种守护进程也已经启动了。

init接下来会打开6个终端,以便用户登录系统。在inittab中的以下6行就是定义了6个终端:

1:2345:respawn:/sbin/mingetty tty1

2:2345:respawn:/sbin/mingetty tty2

3:2345:respawn:/sbin/mingetty tty3

4:2345:respawn:/sbin/mingetty tty4

5:2345:respawn:/sbin/mingetty tty5

6:2345:respawn:/sbin/mingetty tty6

从上面可以看出在2、3、4、5的运行级别中都将以respawn方式运行mingetty程序,mingetty程序能打开终端、设置模式。

同时它会显示一个文本登录界面,这个界面就是我们经常看到的登录界面,在这个登录界面中会提示用户输入用户名,而用户输入的用户将作为参数传给login程序来验证用户的身份。

用户登录系统

一般来说,用户的登录方式有三种:

(1)命令行登录

(2)ssh登录

(3)图形界面登录

对于运行级别为5的图形方式用户来说,他们的登录是通过一个图形化的登录界面。登录成功后可以直接进入KDE、Gnome等窗口管理器。

而本文主要讲的还是文本方式登录的情况:当我们看到mingetty的登录界面时,我们就可以输入用户名和密码来登录系统了。

Linux的账号验证程序是login,login会接收mingetty传来的用户名作为用户名参数。

然后login会对用户名进行分析:如果用户名不是root,且存在/etc/nologin文件,login将输出nologin文件的内容,然后退出。

这通常用来系统维护时防止非root用户登录。只有/etc/securetty中登记了的终端才允许root用户登录,如果不存在这个文件,则root可以在任何终端上登录。

/etc/usertty文件用于对用户作出附加访问限制,如果不存在这个文件,则没有其他限制。

图形模式与文字模式的切换方式

Linux预设提供了六个命令窗口终端机让我们来登录。

默认我们登录的就是第一个窗口,也就是tty1,这个六个窗口分别为tty1,tty2 … tty6,你可以按下Ctrl + Alt + F1 ~ F6 来切换它们。

如果你安装了图形界面,默认情况下是进入图形界面的,此时你就可以按Ctrl + Alt + F1 ~ F6来进入其中一个命令窗口界面。

当你进入命令窗口界面后再返回图形界面只要按下Ctrl + Alt + F7 就回来了。

如果你用的vmware 虚拟机,命令窗口切换的快捷键为 Alt + Space + F1~F6. 如果你在图形界面下请按Alt + Shift + Ctrl + F1~F6 切换至命令窗口。

Linux 关机

在linux领域内大多用在服务器上,很少遇到关机的操作。毕竟服务器上跑一个服务是永无止境的,除非特殊情况下,不得已才会关机。

正确的关机流程为:sync > shutdown > reboot > halt

关机指令为:shutdown ,你可以man shutdown 来看一下帮助文档。

例如你可以运行如下命令关机:

sync 将数据由内存同步到硬盘中。

shutdown 关机指令,你可以man shutdown 来看一下帮助文档。例如你可以运行如下命令关机:

shutdown –h 10 ‘This server will shutdown after 10 mins’ 这个命令告诉大家,计算机将在10分钟后关机,并且会显示在登陆用户的当前屏幕中。

Shutdown –h now 立马关机

Shutdown –h 20:25 系统会在今天20:25关机

Shutdown –h +10 十分钟后关机

Shutdown –r now 系统立马重启

Shutdown –r +10 系统十分钟后重启

reboot 就是重启,等同于 shutdown –r now

halt 关闭系统,等同于shutdown –h now 和 poweroff

最后总结一下,不管是重启系统还是关闭系统,首先要运行sync命令,把内存中的数据写到磁盘中。

关机的命令有 shutdown –h now halt poweroff 和 init 0 , 重启系统的命令有 shutdown –r now , reboot 和 init 6.


Linux 系统目录结构

登录系统后,在当前命令窗口下输入命令:

 ls / 

你会看到如下图所示:

树状目录结构:

以下是对这些目录的解释:

/bin

bin是Binary的缩写, 这个目录存放着最经常使用的命令。

/boot:

这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。

/dev :

dev是Device(设备)的缩写, 该目录下存放的是Linux的外部设备,在Linux中访问设备的方式和访问文件的方式是相同的。

/etc:

这个目录用来存放所有的系统管理所需要的配置文件和子目录。

/home

用户的主目录,在Linux中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的。

/lib

这个目录里存放着系统最基本的动态连接共享库,其作用类似于Windows里的DLL文件。几乎所有的应用程序都需要用到这些共享库。

/lost+found

这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件。

/media linux系统会自动识别一些设备,例如U盘、光驱等等,当识别后,linux会把识别的设备挂载到这个目录下。

/mnt

系统提供该目录是为了让用户临时挂载别的文件系统的,我们可以将光驱挂载在/mnt/上,然后进入该目录就可以查看光驱里的内容了。

/opt

这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。

/proc

这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息。

这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件,比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器:

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

/root

该目录为系统管理员,也称作超级权限者的用户主目录。

/sbin

s就是Super User的意思,这里存放的是系统管理员使用的系统管理程序。

/selinux

这个目录是Redhat/CentOS所特有的目录,Selinux是一个安全机制,类似于windows的防火墙,但是这套机制比较复杂,这个目录就是存放selinux相关的文件的。

/srv

该目录存放一些服务启动之后需要提取的数据。

/sys

这是linux2.6内核的一个很大的变化。该目录下安装了2.6内核中新出现的一个文件系统 sysfs 。

sysfs文件系统集成了下面3种文件系统的信息:针对进程信息的proc文件系统、针对设备的devfs文件系统以及针对伪终端的devpts文件系统。

该文件系统是内核设备树的一个直观反映。

当一个内核对象被创建的时候,对应的文件和目录也在内核对象子系统种被创建。

/tmp

这个目录是用来存放一些临时文件的。

/usr

这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似与windows下的program files目录。

/usr/bin:

系统用户使用的应用程序。

/usr/sbin:

超级用户使用的比较高级的管理程序和系统守护程序。

/usr/src:内核源代码默认的放置目录。

/var

这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。

在linux系统中,有几个目录是比较重要的,平时需要注意不要误删除或者随意更改内部文件。

/etc: 上边也提到了,这个是系统中的配置文件,如果你更改了该目录下的某个文件可能会导致系统不能启动。

/bin, /sbin, /usr/bin, /usr/sbin: 这是系统预设的执行文件的放置目录,比如 ls 就是在/bin/ls 目录下的。

值得提出的是,/bin, /usr/bin 是给系统用户使用的指令(除root外的通用户),而/sbin, /usr/sbin 则是给root使用的指令。

/var: 这是一个非常重要的目录,系统上跑了很多程序,那么每个程序都会有相应的日志产生,而这些日志就被记录到这个目录下,具体在/var/log 目录下,另外mail的预设放置也是在这里。




十三、Linux 文件与目录管理

4课时

实验课

主要内容

Linux 文件与目录管理

我们知道Linux的目录结构为树状结构,最顶级的目录为根目录 /。

其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们。

在开始本教程前我们需要先知道什么是绝对路径与相对路径。

绝对路径:

路径的写法,由根目录 / 写起,例如: /usr/share/doc 这个目录。

相对路径:

路径的写法,不是由 / 写起,例如由 /usr/share/doc 要到 /usr/share/man 底下时,可以写成: cd ../man 这就是相对路径的写法啦!

处理目录的常用命令

接下来我们就来看几个常见的处理目录的命令吧:

ls: 列出目录

cd:切换目录

pwd:显示目前的目录

mkdir:创建一个新的目录

rmdir:删除一个空的目录

cp: 复制文件或目录

rm: 移除文件或目录

mv: 移动文件与目录、文件重命名

你可以使用 man [命令] 来查看各个命令的使用文档,如 :man cp。

ls (列出目录)

在Linux系统当中, ls 命令可能是最常被运行的。

语法:

[root@www ~]# ls [-aAdfFhilnrRSt] 目录名称

[root@www ~]# ls [--color={never,auto,always}] 目录名称

[root@www ~]# ls [--full-time] 目录名称

选项与参数:

-a :全部的文件,连同隐藏档( 开头为 . 的文件) 一起列出来(常用)

-d :仅列出目录本身,而不是列出目录内的文件数据(常用)

-l :长数据串列出,包含文件的属性与权限等等数据;(常用)

将家目录下的所有文件列出来(含属性与隐藏档)

[root@www ~]# ls -al ~

cd (切换目录)

cd是Change Directory的缩写,这是用来变换工作目录的命令。

语法:

cd [相对路径或绝对路径]

#使用 mkdir 命令创建w3cschool.cn目录

[root@www ~]# mkdir w3cschool.cn

#使用绝对路径切换到w3cschool.cn目录

[root@www ~]# cd /root/w3cschool.cn/

#使用相对路径切换到w3cschool.cn目录

[root@www ~]# cd ./w3cschool.cn/

# 表示回到自己的家目录,亦即是 /root 这个目录

[root@www w3cschool.cn]# cd ~

# 表示去到目前的上一级目录,亦即是 /root 的上一级目录的意思;

[root@www ~]# cd ..

接下来大家多操作几次应该就可以很好的理解 cd 命令的。

pwd (显示目前所在的目录)

pwd是Print Working Directory的缩写,也就是显示目前所在目录的命令。

[root@www ~]# pwd [-P]

选项与参数:

-P  :显示出确实的路径,而非使用连结 (link) 路径。

范例:单纯显示出目前的工作目录:

[root@www ~]# pwd

/root  <== 显示出目录啦~ 

范例:显示出实际的工作目录,而非连结档本身的目录名而已

[root@www ~]# cd /var/mail  <==注意,/var/mail是一个连结档

[root@www mail]# pwd

/var/mail        <==列出目前的工作目录

[root@www mail]# pwd -P

/var/spool/mail  <==怎么回事?有没有加 -P 差很多~

[root@www mail]# ls -ld /var/mail

lrwxrwxrwx 1 root root 10 Sep  4 17:54 /var/mail -> spool/mail

# 看到这里应该知道为啥了吧?因为 /var/mail 是连结档,连结到 /var/spool/mail

# 所以,加上 pwd -P 的选项后,会不以连结档的数据显示,而是显示正确的完整路径啊!

mkdir (创建新目录)

如果想要创建新的目录的话,那么就使用mkdir (make directory)吧。

语法:

mkdir [-mp] 目录名称

选项与参数:

-m :配置文件的权限喔!直接配置,不需要看默认权限 (umask) 的脸色~

-p :帮助你直接将所需要的目录(包含上一级目录)递回创建起来!

范例:请到/tmp底下尝试创建数个新目录看看:

[root@www ~]# cd /tmp

[root@www tmp]# mkdir test    <==创建一名为 test 的新目录

[root@www tmp]# mkdir test1/test2/test3/test4

mkdir: cannot create directory `test1/test2/test3/test4': 

No such file or directory      <== 没办法直接创建此目录啊!

[root@www tmp]# mkdir -p test1/test2/test3/test4

加了这个 -p 的选项,可以自行帮你创建多层目录!

范例:创建权限为rwx--x--x的目录

[root@www tmp]# mkdir -m 711 test2

[root@www tmp]# ls -l

drwxr-xr-x  3 root  root 4096 Jul 18 12:50 test

drwxr-xr-x  3 root  root 4096 Jul 18 12:53 test1

drwx--x--x  2 root  root 4096 Jul 18 12:54 test2

上面的权限部分,如果没有加上 -m 来强制配置属性,系统会使用默认属性。

如果我们使用 -m ,如上例我们给予 -m 711 来给予新的目录 drwx--x--x 的权限。

rmdir (删除空的目录)

语法:

rmdir [-p] 目录名称

选项与参数:

-p :连同上一级『空的』目录也一起删除

删除 w3cschool.cn 目录

[root@www tmp]# rmdir w3cschool.cn/

范例:将於mkdir范例中创建的目录(/tmp底下)删除掉!

[root@www tmp]# ls -l  <==看看有多少目录存在?

drwxr-xr-x  3 root  root 4096 Jul 18 12:50 test

drwxr-xr-x  3 root  root 4096 Jul 18 12:53 test1

drwx--x--x  2 root  root 4096 Jul 18 12:54 test2

[root@www tmp]# rmdir test  <==可直接删除掉,没问题

[root@www tmp]# rmdir test1  <==因为尚有内容,所以无法删除!

rmdir: `test1': Directory not empty

[root@www tmp]# rmdir -p test1/test2/test3/test4

[root@www tmp]# ls -l        <==您看看,底下的输出中test与test1不见了!

drwx--x--x  2 root  root 4096 Jul 18 12:54 test2

利用 -p 这个选项,立刻就可以将 test1/test2/test3/test4 一次删除。

不过要注意的是,这个 rmdir 仅能删除空的目录,你可以使用 rm 命令来删除非空目录。

cp (复制文件或目录)

cp 即拷贝文件和目录。

语法:

[root@www ~]# cp [-adfilprsu] 来源档(source) 目标档(destination)

[root@www ~]# cp [options] source1 source2 source3 .... directory

选项与参数:

-a :相当於 -pdr 的意思,至於 pdr 请参考下列说明;(常用)

-d :若来源档为连结档的属性(link file),则复制连结档属性而非文件本身;

-f :为强制(force)的意思,若目标文件已经存在且无法开启,则移除后再尝试一次;

-i :若目标档(destination)已经存在时,在覆盖时会先询问动作的进行(常用)

-l :进行硬式连结(hard link)的连结档创建,而非复制文件本身;

-p :连同文件的属性一起复制过去,而非使用默认属性(备份常用);

-r :递回持续复制,用於目录的复制行为;(常用)

-s :复制成为符号连结档 (symbolic link),亦即『捷径』文件;

-u :若 destination 比 source 旧才升级 destination !

用root身份,将家目录下的 .bashrc 复制到 /tmp 下,并更名为 bashr

[root@www ~]# cp ~/.bashrc /tmp/bashrc

[root@www ~]# cp -i ~/.bashrc /tmp/bashrc

cp: overwrite `/tmp/bashrc'? n  <==n不覆盖,y为覆盖

rm (移除文件或目录)

语法:

rm [-fir] 文件或目录

选项与参数:

-f :就是 force 的意思,忽略不存在的文件,不会出现警告信息;

-i :互动模式,在删除前会询问使用者是否动作

-r :递回删除啊!最常用在目录的删除了!这是非常危险的选项!!!

将刚刚在 cp 的范例中创建的 bashrc 删除掉!

[root@www tmp]# rm -i bashrc

rm: remove regular file `bashrc'? y

如果加上 -i 的选项就会主动询问喔,避免你删除到错误的档名!

mv (移动文件与目录,或修改名称)

语法:

[root@www ~]# mv [-fiu] source destination

[root@www ~]# mv [options] source1 source2 source3 .... directory

选项与参数:

-f :force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖;

-i :若目标文件 (destination) 已经存在时,就会询问是否覆盖!

-u :若目标文件已经存在,且 source 比较新,才会升级 (update)

复制一文件,创建一目录,将文件移动到目录中

[root@www ~]# cd /tmp

[root@www tmp]# cp ~/.bashrc bashrc

[root@www tmp]# mkdir mvtest

[root@www tmp]# mv bashrc mvtest

将某个文件移动到某个目录去,就是这样做!

将刚刚的目录名称更名为 mvtest2

[root@www tmp]# mv mvtest mvtest2

Linux 文件内容查看

Linux系统中使用以下命令来查看文件的内容:

cat  由第一行开始显示文件内容

tac  从最后一行开始显示,可以看出 tac 是 cat 的倒著写!

nl   显示的时候,顺道输出行号!

more 一页一页的显示文件内容

less 与 more 类似,但是比 more 更好的是,他可以往前翻页!

head 只看头几行

tail 只看尾巴几行

你可以使用 man [命令]来查看各个命令的使用文档,如 :man cp。

cat

由第一行开始显示文件内容

语法:

cat [-AbEnTv]

选项与参数:

-A :相当於 -vET 的整合选项,可列出一些特殊字符而不是空白而已;

-b :列出行号,仅针对非空白行做行号显示,空白行不标行号!

-E :将结尾的断行字节 $ 显示出来;

-n :列印出行号,连同空白行也会有行号,与 -b 的选项不同;

-T :将 [tab] 按键以 ^I 显示出来;

-v :列出一些看不出来的特殊字符

检看 /etc/issue 这个文件的内容:

[root@www ~]# cat /etc/issue

CentOS release 6.4 (Final)

Kernel \r on an \m

tac

tac与cat命令刚好相反,文件内容从最后一行开始显示,可以看出 tac 是 cat 的倒着写!如:

[root@www ~]# tac /etc/issue

Kernel \r on an \m

CentOS release 6.4 (Final)

nl

显示行号

语法:

nl [-bnw] 文件

选项与参数:

-b :指定行号指定的方式,主要有两种:

-b a :表示不论是否为空行,也同样列出行号(类似 cat -n);

-b t :如果有空行,空的那一行不要列出行号(默认值);

-n :列出行号表示的方法,主要有三种:

-n ln :行号在萤幕的最左方显示;

-n rn :行号在自己栏位的最右方显示,且不加 0 ;

-n rz :行号在自己栏位的最右方显示,且加 0 ;

-w :行号栏位的占用的位数。

范例一:用 nl 列出 /etc/issue 的内容

[root@www ~]# nl /etc/issue

    1  CentOS release 6.4 (Final)

    2  Kernel \r on an \m

more

一页一页翻动

[root@www ~]# more /etc/man.config

#

# Generated automatically from man.conf.in by the

# configure script.

#

# man.conf from man-1.6d

....(中间省略)....

--More--(28%)  <== 重点在这一行喔!你的光标也会在这里等待你的命令

在 more 这个程序的运行过程中,你有几个按键可以按的:

空白键 (space):代表向下翻一页;

Enter         :代表向下翻『一行』;

/字串         :代表在这个显示的内容当中,向下搜寻『字串』这个关键字;

:f            :立刻显示出档名以及目前显示的行数;

q             :代表立刻离开 more ,不再显示该文件内容。

b 或 [ctrl]-b :代表往回翻页,不过这动作只对文件有用,对管线无用。

less

一页一页翻动,以下实例输出/etc/man.config文件的内容:

[root@www ~]# less /etc/man.config

#

# Generated automatically from man.conf.in by the

# configure script.

#

# man.conf from man-1.6d

....(中间省略)....

:  <== 这里可以等待你输入命令!

less运行时可以输入的命令有:

空白键    :向下翻动一页;

[pagedown]:向下翻动一页;

[pageup]  :向上翻动一页;

/字串     :向下搜寻『字串』的功能;

?字串     :向上搜寻『字串』的功能;

n         :重复前一个搜寻 (与 / 或 ? 有关!)

N         :反向的重复前一个搜寻 (与 / 或 ? 有关!)

q         :离开 less 这个程序;

head

取出文件前面几行

语法:

head [-n number] 文件

选项与参数:

-n :后面接数字,代表显示几行的意思

[root@www ~]# head /etc/man.config

默认的情况中,显示前面 10 行!若要显示前 20 行,就得要这样:

[root@www ~]# head -n 20 /etc/man.config

tail

取出文件后面几行

语法:

tail [-n number] 文件

选项与参数:

-n :后面接数字,代表显示几行的意思

-f :表示持续侦测后面所接的档名,要等到按下[ctrl]-c才会结束tail的侦测

[root@www ~]# tail /etc/man.config

# 默认的情况中,显示最后的十行!若要显示最后的 20 行,就得要这样:

[root@www ~]# tail -n 20 /etc/man.config




十四、Linux用户与用户组管理

4课时

实验课

主要内容

Linux 用户和用户组管理

Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。

用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪,并控制他们对系统资源的访问;另一方面也可以帮助用户组织文件,并为用户提供安全性保护。

每个用户账号都拥有一个惟一的用户名和各自的口令。

用户在登录时键入正确的用户名和口令后,就能够进入系统和自己的主目录。

实现用户账号的管理,要完成的工作主要有如下几个方面:

用户账号的添加、删除与修改。

用户口令的管理。

用户组的管理。

一、Linux系统用户账号的管理

用户账号的管理工作主要涉及到用户账号的添加、修改和删除。

添加用户账号就是在系统中创建一个新账号,然后为新账号分配用户号、用户组、主目录和登录Shell等资源。刚添加的账号是被锁定的,无法使用。

1、添加新的用户账号使用useradd命令,其语法如下:

useradd 选项 用户名

参数说明:

选项:

-c comment 指定一段注释性描述。

-d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。

-g 用户组 指定用户所属的用户组。

-G 用户组,用户组 指定用户所属的附加组。

-s Shell文件 指定用户的登录Shell。

-u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。

用户名:

指定新账号的登录名。

实例1

# useradd –d /usr/sam -m sam

此命令创建了一个用户sam,其中-d和-m选项用来为登录名sam产生一个主目录/usr/sam(/usr为默认的用户主目录所在的父目录)。

实例2

# useradd -s /bin/sh -g group –G adm,root gem

此命令新建了一个用户gem,该用户的登录Shell是 /bin/sh,它属于group用户组,同时又属于adm和root用户组,其中group用户组是其主组。

这里可能新建组:#groupadd group及groupadd adm

增加用户账号就是在/etc/passwd文件中为新用户增加一条记录,同时更新其他系统文件如/etc/shadow, /etc/group等。

Linux提供了集成的系统管理工具userconf,它可以用来对用户账号进行统一管理。

3、删除帐号

如果一个用户的账号不再使用,可以从系统中删除。删除用户账号就是要将/etc/passwd等系统文件中的该用户记录删除,必要时还删除用户的主目录。

删除一个已有的用户账号使用userdel命令,其格式如下:

userdel 选项 用户名

常用的选项是-r,它的作用是把用户的主目录一起删除。

例如:

# userdel sam

此命令删除用户sam在系统文件中(主要是/etc/passwd, /etc/shadow, /etc/group等)的记录,同时删除用户的主目录。

4、修改帐号

修改用户账号就是根据实际情况更改用户的有关属性,如用户号、主目录、用户组、登录Shell等。

修改已有用户的信息使用usermod命令,其格式如下:

usermod 选项 用户名

常用的选项包括-c, -d, -m, -g, -G, -s, -u以及-o等,这些选项的意义与useradd命令中的选项一样,可以为用户指定新的资源值。

另外,有些系统可以使用选项:-l 新用户名

这个选项指定一个新的账号,即将原来的用户名改为新的用户名。

例如:

# usermod -s /bin/ksh -d /home/z –g developer sam

此命令将用户sam的登录Shell修改为ksh,主目录改为/home/z,用户组改为developer。

5、用户口令的管理

用户管理的一项重要内容是用户口令的管理。用户账号刚创建时没有口令,但是被系统锁定,无法使用,必须为其指定口令后才可以使用,即使是指定空口令。

指定和修改用户口令的Shell命令是passwd。超级用户可以为自己和其他用户指定口令,普通用户只能用它修改自己的口令。命令的格式为:

passwd 选项 用户名

可使用的选项:

-l 锁定口令,即禁用账号。

-u 口令解锁。

-d 使账号无口令。

-f 强迫用户下次登录时修改口令。

如果默认用户名,则修改当前用户的口令。

例如,假设当前用户是sam,则下面的命令修改该用户自己的口令:

$ passwd

Old password:******

New password:*******

Re-enter new password:*******

如果是超级用户,可以用下列形式指定任何用户的口令:

# passwd sam

New password:*******

Re-enter new password:*******

普通用户修改自己的口令时,passwd命令会先询问原口令,验证后再要求用户输入两遍新口令,如果两次输入的口令一致,则将这个口令指定给用户;而超级用户为用户指定口令时,就不需要知道原口令。

为了系统安全起见,用户应该选择比较复杂的口令,例如最好使用8位长的口令,口令中包含有大写、小写字母和数字,并且应该与姓名、生日等不相同。

为用户指定空口令时,执行下列形式的命令:

# passwd -d sam

此命令将用户sam的口令删除,这样用户sam下一次登录时,系统就不再询问口令。

passwd命令还可以用-l(lock)选项锁定某一用户,使其不能登录,例如:

# passwd -l sam

二、Linux系统用户组的管理

每个用户都有一个用户组,系统可以对一个用户组中的所有用户进行集中管理。不同Linux 系统对用户组的规定有所不同,如Linux下的用户属于与它同名的用户组,这个用户组在创建用户时同时创建。

用户组的管理涉及用户组的添加、删除和修改。组的增加、删除和修改实际上就是对/etc/group文件的更新。

1、增加一个新的用户组使用groupadd命令。其格式如下:

groupadd 选项 用户组

可以使用的选项有:

-g GID 指定新用户组的组标识号(GID)。

-o 一般与-g选项同时使用,表示新用户组的GID可以与系统已有用户组的GID相同。

实例1:

# groupadd group1

此命令向系统中增加了一个新组group1,新组的组标识号是在当前已有的最大组标识号的基础上加1。

实例2:

# groupadd -g 101 group2

此命令向系统中增加了一个新组group2,同时指定新组的组标识号是101。

2、如果要删除一个已有的用户组,使用groupdel命令,其格式如下:

groupdel 用户组

例如:

# groupdel group1

此命令从系统中删除组group1。

3、修改用户组的属性使用groupmod命令。其语法如下:

groupmod 选项 用户组

常用的选项有:

-g GID 为用户组指定新的组标识号。

-o 与-g选项同时使用,用户组的新GID可以与系统已有用户组的GID相同。

-n新用户组 将用户组的名字改为新名字

实例1:

# groupmod -g 102 group2

此命令将组group2的组标识号修改为102。

实例2:

# groupmod –g 10000 -n group3 group2

此命令将组group2的标识号改为10000,组名修改为group3。

4、如果一个用户同时属于多个用户组,那么用户可以在用户组之间切换,以便具有其他用户组的权限。

用户可以在登录后,使用命令newgrp切换到其他用户组,这个命令的参数就是目的用户组。例如:

$ newgrp root

这条命令将当前用户切换到root用户组,前提条件是root用户组确实是该用户的主组或附加组。类似于用户账号的管理,用户组的管理也可以通过集成的系统管理工具来完成。

三、与用户账号有关的系统文件

完成用户管理的工作有许多种方法,但是每一种方法实际上都是对有关的系统文件进行修改。

与用户和用户组相关的信息都存放在一些系统文件中,这些文件包括/etc/passwd, /etc/shadow, /etc/group等。

下面分别介绍这些文件的内容。

1、/etc/passwd文件是用户管理工作涉及的最重要的一个文件。

Linux系统中的每个用户都在/etc/passwd文件中有一个对应的记录行,它记录了这个用户的一些基本属性。

这个文件对所有用户都是可读的。它的内容类似下面的例子:

# cat /etc/passwd

root:x:0:0:Superuser:/:

daemon:x:1:1:System daemons:/etc:

bin:x:2:2:Owner of system commands:/bin:

sys:x:3:3:Owner of system files:/usr/sys:

adm:x:4:4:System accounting:/usr/adm:

uucp:x:5:5:UUCP administrator:/usr/lib/uucp:

auth:x:7:21:Authentication administrator:/tcb/files/auth:

cron:x:9:16:Cron daemon:/usr/spool/cron:

listen:x:37:4:Network daemon:/usr/net/nls:

lp:x:71:18:Printer administrator:/usr/spool/lp:

sam:x:200:50:Sam san:/usr/sam:/bin/sh

从上面的例子我们可以看到,/etc/passwd中一行记录对应着一个用户,每行记录又被冒号(:)分隔为7个字段,其格式和具体含义如下:

用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录Shell

1)"用户名"是代表用户账号的字符串。

通常长度不超过8个字符,并且由大小写字母和/或数字组成。登录名中不能有冒号(:),因为冒号在这里是分隔符。

为了兼容起见,登录名中最好不要包含点字符(.),并且不使用连字符(-)和加号(+)打头。

2)“口令”一些系统中,存放着加密后的用户口令字。

虽然这个字段存放的只是用户口令的加密串,不是明文,但是由于/etc/passwd文件对所有用户都可读,所以这仍是一个安全隐患。因此,现在许多Linux 系统(如SVR4)都使用了shadow技术,把真正的加密后的用户口令字存放到/etc/shadow文件中,而在/etc/passwd文件的口令字段中只存放一个特殊的字符,例如“x”或者“*”。

3)“用户标识号”是一个整数,系统内部用它来标识用户。

一般情况下它与用户名是一一对应的。如果几个用户名对应的用户标识号是一样的,系统内部将把它们视为同一个用户,但是它们可以有不同的口令、不同的主目录以及不同的登录Shell等。

通常用户标识号的取值范围是0~65 535。0是超级用户root的标识号,1~99由系统保留,作为管理账号,普通用户的标识号从100开始。在Linux系统中,这个界限是500。

4)“组标识号”字段记录的是用户所属的用户组。

它对应着/etc/group文件中的一条记录。

5)“注释性描述”字段记录着用户的一些个人情况。

例如用户的真实姓名、电话、地址等,这个字段并没有什么实际的用途。在不同的Linux 系统中,这个字段的格式并没有统一。在许多Linux系统中,这个字段存放的是一段任意的注释性描述文字,用做finger命令的输出。

6)“主目录”,也就是用户的起始工作目录。

它是用户在登录到系统之后所处的目录。在大多数系统中,各用户的主目录都被组织在同一个特定的目录下,而用户主目录的名称就是该用户的登录名。各用户对自己的主目录有读、写、执行(搜索)权限,其他用户对此目录的访问权限则根据具体情况设置。

7)用户登录后,要启动一个进程,负责将用户的操作传给内核,这个进程是用户登录到系统后运行的命令解释器或某个特定的程序,即Shell。

Shell是用户与Linux系统之间的接口。Linux的Shell有许多种,每种都有不同的特点。常用的有sh(Bourne Shell), csh(C Shell), ksh(Korn Shell), tcsh(TENEX/TOPS-20 type C Shell), bash(Bourne Again Shell)等。

系统管理员可以根据系统情况和用户习惯为用户指定某个Shell。如果不指定Shell,那么系统使用sh为默认的登录Shell,即这个字段的值为/bin/sh。

用户的登录Shell也可以指定为某个特定的程序(此程序不是一个命令解释器)。

利用这一特点,我们可以限制用户只能运行指定的应用程序,在该应用程序运行结束后,用户就自动退出了系统。有些Linux 系统要求只有那些在系统中登记了的程序才能出现在这个字段中。

8)系统中有一类用户称为伪用户(psuedo users)。

这些用户在/etc/passwd文件中也占有一条记录,但是不能登录,因为它们的登录Shell为空。它们的存在主要是方便系统管理,满足相应的系统进程对文件属主的要求。

常见的伪用户如下所示:

伪 用 户 含 义

bin 拥有可执行的用户命令文件

sys 拥有系统文件

adm 拥有帐户文件

uucp UUCP使用

lp lp或lpd子系统使用

nobody NFS使用

拥有帐户文件

1、除了上面列出的伪用户外,还有许多标准的伪用户,例如:audit, cron, mail, usenet等,它们也都各自为相关的进程和文件所需要。

由于/etc/passwd文件是所有用户都可读的,如果用户的密码太简单或规律比较明显的话,一台普通的计算机就能够很容易地将它破解,因此对安全性要求较高的Linux系统都把加密后的口令字分离出来,单独存放在一个文件中,这个文件是/etc/shadow文件。 有超级用户才拥有该文件读权限,这就保证了用户密码的安全性。

2、/etc/shadow中的记录行与/etc/passwd中的一一对应,它由pwconv命令根据/etc/passwd中的数据自动产生

它的文件格式与/etc/passwd类似,由若干个字段组成,字段之间用":"隔开。这些字段是:

登录名:加密口令:最后一次修改时间:最小时间间隔:最大时间间隔:警告时间:不活动时间:失效时间:标志

"登录名"是与/etc/passwd文件中的登录名相一致的用户账号

"口令"字段存放的是加密后的用户口令字,长度为13个字符。如果为空,则对应用户没有口令,登录时不需要口令;如果含有不属于集合 { ./0-9A-Za-z }中的字符,则对应的用户不能登录。

"最后一次修改时间"表示的是从某个时刻起,到用户最后一次修改口令时的天数。时间起点对不同的系统可能不一样。例如在SCO Linux 中,这个时间起点是1970年1月1日。

"最小时间间隔"指的是两次修改口令之间所需的最小天数。

"最大时间间隔"指的是口令保持有效的最大天数。

"警告时间"字段表示的是从系统开始警告用户到用户密码正式失效之间的天数。

"不活动时间"表示的是用户没有登录活动但账号仍能保持有效的最大天数。

"失效时间"字段给出的是一个绝对的天数,如果使用了这个字段,那么就给出相应账号的生存期。期满后,该账号就不再是一个合法的账号,也就不能再用来登录了。

下面是/etc/shadow的一个例子:

# cat /etc/shadow

root:Dnakfw28zf38w:8764:0:168:7:::

daemon:*::0:0::::

bin:*::0:0::::

sys:*::0:0::::

adm:*::0:0::::

uucp:*::0:0::::

nuucp:*::0:0::::

auth:*::0:0::::

cron:*::0:0::::

listen:*::0:0::::

lp:*::0:0::::

sam:EkdiSECLWPdSa:9740:0:0::::

3、用户组的所有信息都存放在/etc/group文件中。

将用户分组是Linux 系统中对用户进行管理及控制访问权限的一种手段。

每个用户都属于某个用户组;一个组中可以有多个用户,一个用户也可以属于不同的组。

当一个用户同时是多个组中的成员时,在/etc/passwd文件中记录的是用户所属的主组,也就是登录时所属的默认组,而其他组称为附加组。

用户要访问属于附加组的文件时,必须首先使用newgrp命令使自己成为所要访问的组中的成员。

用户组的所有信息都存放在/etc/group文件中。此文件的格式也类似于/etc/passwd文件,由冒号(:)隔开若干个字段,这些字段有:

组名:口令:组标识号:组内用户列表

"组名"是用户组的名称,由字母或数字构成。与/etc/passwd中的登录名一样,组名不应重复。

"口令"字段存放的是用户组加密后的口令字。一般Linux 系统的用户组都没有口令,即这个字段一般为空,或者是*。

"组标识号"与用户标识号类似,也是一个整数,被系统内部用来标识组。

"组内用户列表"是属于这个组的所有用户的列表/b],不同用户之间用逗号(,)分隔。这个用户组可能是用户的主组,也可能是附加组。

/etc/group文件的一个例子如下:

root::0:root

bin::2:root,bin

sys::3:root,uucp

adm::4:root,adm

daemon::5:root,daemon

lp::7:root,lp

users::20:root,sam

四、批量添加用户

添加和删除用户对每位Linux系统管理员都是轻而易举的事,比较棘手的是如果要添加几十个、上百个甚至上千个用户时,我们不太可能还使用useradd一个一个地添加,必然要找一种简便的创建大量用户的方法。Linux系统提供了创建大量用户的工具,可以让您立即创建大量用户,方法如下:

(1)先编辑一个文本用户文件。

每一列按照/etc/passwd密码文件的格式书写,要注意每个用户的用户名、UID、宿主目录都不可以相同,其中密码栏可以留做空白或输入x号。一个范例文件user.txt内容如下:

user001::600:100:user:/home/user001:/bin/bash

user002::601:100:user:/home/user002:/bin/bash

user003::602:100:user:/home/user003:/bin/bash

user004::603:100:user:/home/user004:/bin/bash

user005::604:100:user:/home/user005:/bin/bash

user006::605:100:user:/home/user006:/bin/bash

(2)以root身份执行命令 /usr/sbin/newusers,从刚创建的用户文件user.txt中导入数据,创建用户:

# newusers < user.txt

然后可以执行命令 vipw 或 vi /etc/passwd 检查 /etc/passwd 文件是否已经出现这些用户的数据,并且用户的宿主目录是否已经创建。

(3)执行命令/usr/sbin/pwunconv。

将 /etc/shadow 产生的 shadow 密码解码,然后回写到 /etc/passwd 中,并将/etc/shadow的shadow密码栏删掉。这是为了方便下一步的密码转换工作,即先取消 shadow password 功能。

# pwunconv

(4)编辑每个用户的密码对照文件。

范例文件 passwd.txt 内容如下:

user001:密码

user002:密码

user003:密码

user004:密码

user005:密码

user006:密码

(5)以root身份执行命令 /usr/sbin/chpasswd。

创建用户密码,chpasswd 会将经过 /usr/bin/passwd 命令编码过的密码写入 /etc/passwd 的密码栏。

# chpasswd < passwd.txt

(6)确定密码经编码写入/etc/passwd的密码栏后。

执行命令 /usr/sbin/pwconv 将密码编码为 shadow password,并将结果写入 /etc/shadow。

# pwconv

这样就完成了大量用户的创建了,之后您可以到/home下检查这些用户宿主目录的权限设置是否都正确,并登录验证用户密码是否正确。






十五、Linux VI VIM

4课时

实验课

主要内容

所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在。

但是目前我们使用比较多的是 vim 编辑器。

vim 具有程序编辑的能力,可以主动的以字体颜色辨别语法的正确性,方便程序设计。

什么是 vim?

Vim是从 vi 发展出来的一个文本编辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。

简单的来说, vi 是老式的字处理器,不过功能已经很齐全了,但是还是有可以进步的地方。 vim 则可以说是程序开发者的一项很好用的工具。 连 vim 的官方网站 (http://www.vim.org) 自己也说 vim 是一个程序开发工具而不是文字处理软件。 

vim 键盘图:

vi/vim 的使用

基本上 vi/vim 共分为三种模式,分别是命令模式(Command mode)输入模式(Insert mode)底线命令模式(Last line mode)。 这三种模式的作用分别是:

命令模式:

用户刚刚启动 vi/vim,便进入了命令模式。

此状态下敲击键盘动作会被Vim识别为命令,而非输入字符。比如我们此时按下i,并不会输入一个字符,i被当作了一个命令。

以下是常用的几个命令:

i 切换到输入模式,以输入字符。

x 删除当前光标所在处的字符。

: 切换到底线命令模式,以在最底一行输入命令。

若想要编辑文本:启动Vim,进入了命令模式,按下i,切换到输入模式。

命令模式只有一些最基本的命令,因此仍要依靠底线命令模式输入更多命令。

输入模式

在命令模式下按下i就进入了输入模式。

在输入模式中,可以使用以下按键:

字符按键以及Shift组合,输入字符

ENTER,回车键,换行

BACK SPACE,退格键,删除光标前一个字符

DEL,删除键,删除光标后一个字符

方向键,在文本中移动光标

HOME/END,移动光标到行首/行尾

Page Up/Page Down,上/下翻页

Insert,切换光标为输入/替换模式,光标将变成竖线/下划线

ESC,退出输入模式,切换到命令模式

底线命令模式

在命令模式下按下:(英文冒号)就进入了底线命令模式。

底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。

在底线命令模式中,基本的命令有(已经省略了冒号):

q 退出程序

w 保存文件

按ESC键可随时退出底线命令模式。

简单的说,我们可以将这三个模式想成底下的图标来表示:

vi/vim 使用实例

使用 vi/vim 进入命令模式

如果你想要使用 vi 来建立一个名为 test.txt 的文件时,你可以这样做:

[root@www ~]# vi test.txt

直接输入 vi 文件名 就能够进入 vi 的命令模式了。请注意,记得 vi 后面一定要加文件名,不管该文件存在与否!

按下 i 进入输入模式,开始编辑文字

在命令模式之中,只要按下 i, o, a 等字符就可以进入输入模式了!

在输入模式当中,你可以发现在左下角状态栏中会出现 –INSERT- 的字样,那就是可以输入任意字符的提示。

这个时候,键盘上除了 [Esc] 这个按键之外,其他的按键都可以视作为一般的输入按钮了,所以你可以进行任何的编辑。

按下 [ESC] 按钮回到命令模式

好了,假设我已经按照上面的样式给他编辑完毕了,那么应该要如何退出呢?是的!没错!就是给他按下 [Esc] 这个按钮即可!马上你就会发现画面左下角的 – INSERT – 不见了!

在命令模式中按下 :wq 储存后离开 vi

OK,我们要存档了,存盘并离开的指令很简单,输入『:wq』即可保存离开!

OK! 这样我们就成功创建了一个 test.txt 的文件。是不是很简单。

vi/vim 按键说明

除了上面简易范例的 i, [Esc], :wq 之外,其实 vim 还有非常多的按键可以使用。

第一部份:命令模式可用的按钮说明,光标移动、复制贴上、搜寻取代等

移动光标的方法

h 或 向左箭头键(←)光标向左移动一个字符

j 或 向下箭头键(↓)光标向下移动一个字符

k 或 向上箭头键(↑)光标向上移动一个字符

l 或 向右箭头键(→)光标向右移动一个字符

如果你将右手放在键盘上的话,你会发现 hjkl 是排列在一起的,因此可以使用这四个按钮来移动光标。 如果想要进行多次移动的话,例如向下移动 30 行,可以使用 "30j" 或 "30↓" 的组合按键, 亦即加上想要进行的次数(数字)后,按下动作即可!

[Ctrl] + [f]屏幕『向下』移动一页,相当于 [Page Down]按键 (常用)

[Ctrl] + [b]屏幕『向上』移动一页,相当于 [Page Up] 按键 (常用)

[Ctrl] + [d]屏幕『向下』移动半页

[Ctrl] + [u]屏幕『向上』移动半页

+光标移动到非空格符的下一列

-光标移动到非空格符的上一列

n那个 n 表示『数字』,例如 20 。按下数字后再按空格键,光标会向右移动这一行的 n 个字符。例如 20 则光标会向后面移动 20 个字符距离。

0 或功能键[Home]这是数字『 0 』:移动到这一行的最前面字符处 (常用)

$ 或功能键[End]移动到这一行的最后面字符处(常用)

H光标移动到这个屏幕的最上方那一行的第一个字符

M光标移动到这个屏幕的中央那一行的第一个字符

L光标移动到这个屏幕的最下方那一行的第一个字符

G移动到这个档案的最后一行(常用)

nGn 为数字。移动到这个档案的第 n 行。例如 20G 则会移动到这个档案的第 20 行(可配合 :set nu)

gg移动到这个档案的第一行,相当于 1G 啊! (常用)

nn 为数字。光标向下移动 n 行(常用)

搜寻与取代

/word向光标之下寻找一个名称为 word 的字符串。例如要在档案内搜寻 vbird 这个字符串,就输入 /vbird 即可! (常用)

?word向光标之上寻找一个字符串名称为 word 的字符串。

n这个 n 是英文按键。代表重复前一个搜寻的动作。举例来说, 如果刚刚我们执行 /vbird 去向下搜寻 vbird 这个字符串,则按下 n 后,会向下继续搜寻下一个名称为 vbird 的字符串。如果是执行 ?vbird 的话,那么按下 n 则会向上继续搜寻名称为 vbird 的字符串!

N这个 N 是英文按键。与 n 刚好相反,为『反向』进行前一个搜寻动作。 例如 /vbird 后,按下 N 则表示『向上』搜寻 vbird 。

使用 /word 配合 n 及 N 是非常有帮助的!可以让你重复的找到一些你搜寻的关键词!

:n1,n2s/word1/word2/gn1 与 n2 为数字。在第 n1 与 n2 行之间寻找 word1 这个字符串,并将该字符串取代为 word2 !举例来说,在 100 到 200 行之间搜寻 vbird 并取代为 VBIRD 则:

『:100,200s/vbird/VBIRD/g』。(常用)

:1,$s/word1/word2/g从第一行到最后一行寻找 word1 字符串,并将该字符串取代为 word2 !(常用)

:1,$s/word1/word2/gc从第一行到最后一行寻找 word1 字符串,并将该字符串取代为 word2 !且在取代前显示提示字符给用户确认 (confirm) 是否需要取代!(常用)

删除、复制与贴上

x, X在一行字当中,x 为向后删除一个字符 (相当于 [del] 按键), X 为向前删除一个字符(相当于 [backspace] 亦即是退格键) (常用)

nxn 为数字,连续向后删除 n 个字符。举例来说,我要连续删除 10 个字符, 『10x』。

dd删除游标所在的那一整行(常用)

nddn 为数字。删除光标所在的向下 n 行,例如 20dd 则是删除 20 行 (常用)

d1G删除光标所在到第一行的所有数据

dG删除光标所在到最后一行的所有数据

d$删除游标所在处,到该行的最后一个字符

d0那个是数字的 0 ,删除游标所在处,到该行的最前面一个字符

yy复制游标所在的那一行(常用)

nyyn 为数字。复制光标所在的向下 n 列,例如 20yy 则是复制 20 列(常用)

y1G复制游标所在列到第一列的所有数据

yG复制游标所在列到最后一列的所有数据

y0复制光标所在的那个字符到该行行首的所有数据

y$复制光标所在的那个字符到该行行尾的所有数据

p, Pp 为将已复制的数据在光标下一行贴上,P 则为贴在游标上一行! 举例来说,我目前光标在第 20 行,且已经复制了 10 行数据。则按下 p 后, 那 10 行数据会贴在原本的 20 行之后,亦即由 21 行开始贴。但如果是按下 P 呢? 那么原本的第 20 行会被推到变成 30 行。 (常用)

J将光标所在列与下一列的数据结合成同一列

c重复删除多个数据,例如向下删除 10 行,[ 10cj ]

u复原前一个动作。(常用)

[Ctrl]+r重做上一个动作。(常用)

这个 u 与 [Ctrl]+r 是很常用的指令!一个是复原,另一个则是重做一次~ 利用这两个功能按键,你的编辑,嘿嘿!很快乐的啦!

.不要怀疑!这就是小数点!意思是重复前一个动作的意思。 如果你想要重复删除、重复贴上等等动作,按下小数点『.』就好了! (常用)

第二部份:命令模式切换到输入模式的可用的按钮说明

进入输入或取代的编辑模式

i, I进入输入模式(Insert mode):

i 为『从目前光标所在处输入』, I 为『在目前所在行的第一个非空格符处开始输入』。 (常用)

a, A进入输入模式(Insert mode):

a 为『从目前光标所在的下一个字符处开始输入』, A 为『从光标所在行的最后一个字符处开始输入』。(常用)

o, O进入输入模式(Insert mode):

这是英文字母 o 的大小写。o 为『在目前光标所在的下一行处输入新的一行』; O 为在目前光标所在处的上一行输入新的一行!(常用)

r, R进入取代模式(Replace mode):

r 只会取代光标所在的那一个字符一次;R会一直取代光标所在的文字,直到按下 ESC 为止;(常用)

上面这些按键中,在 vi 画面的左下角处会出现『--INSERT--』或『--REPLACE--』的字样。 由名称就知道该动作了吧!!特别注意的是,我们上面也提过了,你想要在档案里面输入字符时, 一定要在左下角处看到 INSERT 或 REPLACE 才能输入喔!

[Esc]退出编辑模式,回到命令模式中(常用)

第三部份:命令模式切换到底线命令模式的可用的按钮说明

底线命令模式的储存、离开等指令

:w将编辑的数据写入硬盘档案中(常用)

:w!若文件属性为『只读』时,强制写入该档案。不过,到底能不能写入, 还是跟你对该档案的档案权限有关啊!

:q离开 vi (常用)

:q!若曾修改过档案,又不想储存,使用 ! 为强制离开不储存档案。

注意一下啊,那个惊叹号 (!) 在 vi 当中,常常具有『强制』的意思~

:wq储存后离开,若为 :wq! 则为强制储存后离开 (常用)

ZZ这是大写的 Z 喔!若档案没有更动,则不储存离开,若档案已经被更动过,则储存后离开!

:w [filename]将编辑的数据储存成另一个档案(类似另存新档)

:r [filename]在编辑的数据中,读入另一个档案的数据。亦即将 『filename』 这个档案内容加到游标所在行后面

:n1,n2 w [filename]将 n1 到 n2 的内容储存成 filename 这个档案。

:! command暂时离开 vi 到指令列模式下执行 command 的显示结果!例如

『:! ls /home』即可在 vi 当中察看 /home 底下以 ls 输出的档案信息!

vim 环境的变更

:set nu显示行号,设定之后,会在每一行的前缀显示该行的行号

:set nonu与 set nu 相反,为取消行号!

特别注意,在 vi/vim 中,数字是很有意义的!数字通常代表重复做几次的意思! 也有可能是代表去到第几个什么什么的意思。

举例来说,要删除 50 行,则是用 『50dd』 对吧! 数字加在动作之前,如我要向下移动 20 行呢?那就是『20j』或者是『20↓』即可。





十六、Shell 变量

2课时

实验课

主要内容

Shell 变量

定义变量时,变量名不加美元符号($,PHP语言中变量需要),如:

your_name="w3cschool.cn"

注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:

首个字符必须为字母(a-z,A-Z)。

中间不能有空格,可以使用下划线(_)。

不能使用标点符号。

不能使用bash里的关键字(可用help命令查看保留关键字)。

除了显式地直接赋值,还可以用语句给变量赋值,如:

for file in `ls /etc`

以上语句将 /etc 下目录的文件名循环出来。

使用变量

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

your_name="qinjx"

echo $your_name

echo ${your_name}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

for skill in Ada Coffe Action Java do

    echo "I am good at ${skill}Script"

done

如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

推荐给所有变量加上花括号,这是个好的编程习惯。

已定义的变量,可以被重新定义,如:

your_name="tom"

echo $your_name

your_name="alibaba"

echo $your_name

这样写是合法的,但注意,第二次赋值的时候不能写$your_name="alibaba",使用变量的时候才加美元符($)。

Shell 字符串

字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。

单引号

str='this is a string'

单引号字符串的限制:

单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;

单引号字串中不能出现单引号(对单引号使用转义符后也不行)。

双引号

your_name='qinjx'

str="Hello, I know your are \"$your_name\"! \n"

双引号的优点:

双引号里可以有变量

双引号里可以出现转义字符

拼接字符串

your_name="qinjx"

greeting="hello, "$your_name" !"

greeting_1="hello, ${your_name} !"

echo $greeting $greeting_1

获取字符串长度

string="abcd"

echo ${#string} #输出 4

提取子字符串

string="alibaba is a great company"

echo ${string:1:4} #输出liba

查找子字符串

string="alibaba is a great company"

echo `expr index "$string" is`

注意: 以上脚本中 "`" 是反引号,而不是单引号 "'",不要看错了哦。

Shell 数组

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。

类似与C语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。

定义数组

在Shell中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:

数组名=(值1 值2 ... 值n)

例如:

array_name=(value0 value1 value2 value3)

或者

array_name=(

value0

value1

value2

value3

)

还可以单独定义数组的各个分量:

array_name[0]=value0

array_name[1]=value1

array_name[n]=valuen

可以不使用连续的下标,而且下标的范围没有限制。

读取数组

读取数组元素值的一般格式是:

${数组名[下标]}

例如:

valuen=${array_name[n]}

使用@符号可以获取数组中的所有元素,例如:

echo ${array_name[@]}

获取数组的长度

获取数组长度的方法与获取字符串长度的方法相同,例如:

# 取得数组元素的个数

length=${#array_name[@]}

# 或者

length=${#array_name[*]}

# 取得数组单个元素的长度

lengthn=${#array_name[n]}

Shell 注释

以"#"开头的行就是注释,会被解释器忽略。

sh里没有多行注释,只能每一行加一个#号。只能像这样:

#--------------------------------------------

# 这是一个自动打ipa的脚本,基于webfrogs的ipa-build书写:

# https://github.com/webfrogs/xcode_shell/blob/master/ipa-build

# 功能:自动为etao ios app打包,产出物为14个渠道的ipa包

# 特色:全自动打包,不需要输入任何参数

#--------------------------------------------

##### 用户配置区 开始 #####

#

#

# 项目根目录,推荐将此脚本放在项目的根目录,这里就不用改了

# 应用名,确保和Xcode里Product下的target_name.app名字一致

#

##### 用户配置区 结束  #####

如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?

每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。




十七、Shell 传递参数

4课时

实验课

主要内容

Shell 传递参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$nn 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……

实例

以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名:

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

echo "Shell 传递参数实例!";

echo "执行的文件名:$0";

echo "第一个参数为:$1";

echo "第二个参数为:$2";

echo "第三个参数为:$3";

为脚本设置可执行权限,并执行脚本,输出结果如下所示:

$ chmod +x test.sh

$ ./test.sh 1 2 3

Shell 传递参数实例!

执行的文件名:test.sh

第一个参数为:1

第二个参数为:2

第三个参数为:3

另外,还有几个特殊字符用来处理参数:

参数处理说明

$#传递到脚本的参数个数

$*以一个单字符串显示所有向脚本传递的参数。

如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。

$$脚本运行的当前进程ID号

$!后台运行的最后一个进程的ID号

$@与$*相同,但是使用时加引号,并在引号中返回每个参数。

如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。

$-显示Shell使用的当前选项,与set命令功能相同。

$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

echo "Shell 传递参数实例!";

echo "第一个参数为:$1";

echo "参数个数为:$#";

echo "传递的参数作为一个字符串显示:$*";

执行脚本,输出结果如下所示:

$ chmod +x test.sh

$ ./test.sh 1 2 3

Shell 传递参数实例!

第一个参数为:1

参数个数为:3

传递的参数作为一个字符串显示:1 2 3

$* 与 $@ 区别:

相同点:都是引用所有参数。

不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 " * " 等价于 "1 2 3"(传递了一个参数),而 "@" 等价于 "1" "2" "3"(传递了三个参数)。

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

echo "-- \$* 演示 ---"

for i in "$*"; do

    echo $i

done

echo "-- \$@ 演示 ---"

for i in "$@"; do

    echo $i

done

执行脚本,输出结果如下所示:

$ chmod +x test.sh

$ ./test.sh 1 2 3

-- $* 演示 ---

1 2 3

-- $@ 演示 ---

1

2

3





十八、Shell 运算符

4课时

实验课

主要内容

Shell 基本运算符

Shell 和其他编程语言一样,支持多种运算符,包括:

算数运算符

关系运算符

布尔运算符

字符串运算符

文件测试运算符

expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

例如,两个数相加(注意使用的是反引号 ` 而不是单引号 '):

#!/bin/bash

val=`expr 2 + 2`

echo "两数之和为 : $val"

执行脚本,输出结果如下所示:

两数之和为 : 4

两点注意:

表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。

完整的表达式要被 ` ` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

算术运算符

下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例

+加法`expr $a + $b` 结果为 30。

-减法`expr $a - $b` 结果为 -10。

*乘法`expr $a \* $b` 结果为  200。

/除法`expr $b / $a` 结果为 2。

%取余`expr $b % $a` 结果为 0。

=赋值a=$b 将把变量 b 的值赋给 a。

==相等。用于比较两个数字,相同则返回 true。[ $a == $b ] 返回 false。

!=不相等。用于比较两个数字,不相同则返回 true。[ $a != $b ] 返回 true。

注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]。

实例

算术运算符实例如下:

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

a=10

b=20

val=`expr $a + $b`

echo "a + b : $val"

val=`expr $a - $b`

echo "a - b : $val"

val=`expr $a \* $b`

echo "a * b : $val"

val=`expr $b / $a`

echo "b / a : $val"

val=`expr $b % $a`

echo "b % a : $val"

if [ $a == $b ]

then

  echo "a 等于 b"

fi

if [ $a != $b ]

then

  echo "a 不等于 b"

fi

执行脚本,输出结果如下所示:

a + b : 30

a - b : -10

a * b : 200

b / a : 2

b % a : 0

a 不等于 b

注意:乘号(*)前边必须加反斜杠(\)才能实现乘法运算;if...then...fi 是条件语句,后续将会讲解。在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 "\" 。

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例

-eq检测两个数是否相等,相等返回 true。[ $a -eq $b ] 返回 false。

-ne检测两个数是否相等,不相等返回 true。[ $a -ne $b ] 返回 true。

-gt检测左边的数是否大于右边的,如果是,则返回 true。[ $a -gt $b ] 返回 false。

-lt检测左边的数是否小于右边的,如果是,则返回 true。[ $a -lt $b ] 返回 true。

-ge检测左边的数是否大于等于右边的,如果是,则返回 true。[ $a -ge $b ] 返回 false。

-le检测左边的数是否小于等于右边的,如果是,则返回 true。[ $a -le $b ] 返回 true。

实例

关系运算符实例如下:

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

a=10

b=20

if [ $a -eq $b ]

then

  echo "$a -eq $b : a 等于 b"

else

  echo "$a -eq $b: a 不等于 b"

fi

if [ $a -ne $b ]

then

  echo "$a -ne $b: a 不等于 b"

else

  echo "$a -ne $b : a 等于 b"

fi

if [ $a -gt $b ]

then

  echo "$a -gt $b: a 大于 b"

else

  echo "$a -gt $b: a 不大于 b"

fi

if [ $a -lt $b ]

then

  echo "$a -lt $b: a 小于 b"

else

  echo "$a -lt $b: a 不小于 b"

fi

if [ $a -ge $b ]

then

  echo "$a -ge $b: a 大于或等于 b"

else

  echo "$a -ge $b: a 小于 b"

fi

if [ $a -le $b ]

then

  echo "$a -le $b: a 小于或等于 b"

else

  echo "$a -le $b: a 大于 b"

fi

执行脚本,输出结果如下所示:

10 -eq 20: a 不等于 b

10 -ne 20: a 不等于 b

10 -gt 20: a 不大于 b

10 -lt 20: a 小于 b

10 -ge 20: a 小于 b

10 -le 20: a 小于或等于 b

布尔运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例

!非运算,表达式为 true 则返回 false,否则返回 true。[ ! false ] 返回 true。

-o或运算,有一个表达式为 true 则返回 true。[ $a -lt 20 -o $b -gt 100 ] 返回 true。

-a与运算,两个表达式都为 true 才返回 true。[ $a -lt 20 -a $b -gt 100 ] 返回 false。

实例

布尔运算符实例如下:

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

a=10

b=20

if [ $a != $b ]

then

  echo "$a != $b : a 不等于 b"

else

  echo "$a != $b: a 等于 b"

fi

if [ $a -lt 100 -a $b -gt 15 ]

then

  echo "$a -lt 100 -a $b -gt 15 : 返回 true"

else

  echo "$a -lt 100 -a $b -gt 15 : 返回 false"

fi

if [ $a -lt 100 -o $b -gt 100 ]

then

  echo "$a -lt 100 -o $b -gt 100 : 返回 true"

else

  echo "$a -lt 100 -o $b -gt 100 : 返回 false"

fi

if [ $a -lt 5 -o $b -gt 100 ]

then

  echo "$a -lt 5 -o $b -gt 100 : 返回 true"

else

  echo "$a -lt 5 -o $b -gt 100 : 返回 false"

fi

执行脚本,输出结果如下所示:

10 != 20 : a 不等于 b

10 -lt 100 -a 20 -gt 15 : 返回 true

10 -lt 100 -o 20 -gt 100 : 返回 true

10 -lt 5 -o 20 -gt 100 : 返回 false

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例

&&逻辑的 AND[[ $a -lt 100 && $b -gt 100 ]] 返回 false

||逻辑的 OR[[ $a -lt 100 || $b -gt 100 ]] 返回 true

实例

逻辑运算符实例如下:

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

a=10

b=20

if [[ $a -lt 100 && $b -gt 100 ]]

then

  echo "返回 true"

else

  echo "返回 false"

fi

if [[ $a -lt 100 || $b -gt 100 ]]

then

  echo "返回 true"

else

  echo "返回 false"

fi

执行脚本,输出结果如下所示:

返回 false

返回 true

字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 "abc",变量 b 为 "efg":

运算符说明举例

=检测两个字符串是否相等,相等返回 true。[ $a = $b ] 返回 false。

!=检测两个字符串是否相等,不相等返回 true。[ $a != $b ] 返回 true。

-z检测字符串长度是否为0,为0返回 true。[ -z $a ] 返回 false。

-n检测字符串长度是否为0,不为0返回 true。[ -n $a ] 返回 true。

str检测字符串是否为空,不为空返回 true。[ $a ] 返回 true。

实例

字符串运算符实例如下:

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

a="abc"

b="efg"

if [ $a = $b ]

then

  echo "$a = $b : a 等于 b"

else

  echo "$a = $b: a 不等于 b"

fi

if [ $a != $b ]

then

  echo "$a != $b : a 不等于 b"

else

  echo "$a != $b: a 等于 b"

fi

if [ -z $a ]

then

  echo "-z $a : 字符串长度为 0"

else

  echo "-z $a : 字符串长度不为 0"

fi

if [ -n $a ]

then

  echo "-n $a : 字符串长度不为 0"

else

  echo "-n $a : 字符串长度为 0"

fi

if [ $a ]

then

  echo "$a : 字符串不为空"

else

  echo "$a : 字符串为空"

fi

执行脚本,输出结果如下所示:

abc = efg: a 不等于 b

abc != efg : a 不等于 b

-z abc : 字符串长度不为 0

-n abc : 字符串长度不为 0

abc : 字符串不为空

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

属性检测描述如下:

操作符说明举例

-b file检测文件是否是块设备文件,如果是,则返回 true。[ -b $file ] 返回 false。

-c file检测文件是否是字符设备文件,如果是,则返回 true。[ -c $file ] 返回 false。

-d file检测文件是否是目录,如果是,则返回 true。[ -d $file ] 返回 false。

-f file检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。[ -f $file ] 返回 true。

-g file检测文件是否设置了 SGID 位,如果是,则返回 true。[ -g $file ] 返回 false。

-k file检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。[ -k $file ] 返回 false。

-p file检测文件是否是有名管道,如果是,则返回 true。[ -p $file ] 返回 false。

-u file检测文件是否设置了 SUID 位,如果是,则返回 true。[ -u $file ] 返回 false。

-r file检测文件是否可读,如果是,则返回 true。[ -r $file ] 返回 true。

-w file检测文件是否可写,如果是,则返回 true。[ -w $file ] 返回 true。

-x file检测文件是否可执行,如果是,则返回 true。[ -x $file ] 返回 true。

-s file检测文件是否为空(文件大小是否大于0),不为空返回 true。[ -s $file ] 返回 true。

-e file检测文件(包括目录)是否存在,如果是,则返回 true。[ -e $file ] 返回 true。

实例

变量 file 表示文件"/var/www/w3cschool/test.sh",它的大小为100字节,具有 rwx 权限。下面的代码,将检测该文件的各种属性:

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

file="/var/www/w3cschool/test.sh"

if [ -r $file ]

then

  echo "文件可读"

else

  echo "文件不可读"

fi

if [ -w $file ]

then

  echo "文件可写"

else

  echo "文件不可写"

fi

if [ -x $file ]

then

  echo "文件可执行"

else

  echo "文件不可执行"

fi

if [ -f $file ]

then

  echo "文件为普通文件"

else

  echo "文件为特殊文件"

fi

if [ -d $file ]

then

  echo "文件是个目录"

else

  echo "文件不是个目录"

fi

if [ -s $file ]

then

  echo "文件不为空"

else

  echo "文件为空"

fi

if [ -e $file ]

then

  echo "文件存在"

else

  echo "文件不存在"

fi

执行脚本,输出结果如下所示:

文件可读文件可写文件可执行文件为普通文件文件不是个目录文件不为空文件存在。




十九、Shell 函数

2课时

实验课

主要内容

Shell 函数

linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。

shell中函数的定义格式如下:

[ function ] funname [()]

{

    action;

    [return int;]

}

说明:

1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。

2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255

下面的例子定义了一个函数并进行调用:

#!/bin/bash

demoFun(){

    echo "这是我的第一个 shell 函数!"

}

echo "-----函数开始执行-----"

demoFun

echo "-----函数执行完毕-----"

输出结果:

-----函数开始执行-----

这是我的第一个 shell 函数!

-----函数执行完毕-----

下面定义一个带有return语句的函数:

#!/bin/bash

funWithReturn(){

    echo "这个函数会对输入的两个数字进行相加运算..."

    echo "输入第一个数字: "

    read aNum

    echo "输入第二个数字: "

    read anotherNum

    echo "两个数字分别为 $aNum 和 $anotherNum !"

    return $(($aNum+$anotherNum))

}

funWithReturn

echo "输入的两个数字之和为 $? !"

输出类似下面:

这个函数会对输入的两个数字进行相加运算...

输入第一个数字:

1

输入第二个数字:

2

两个数字分别为 1 和 2 !

输入的两个数字之和为 3 !

函数返回值在调用该函数后通过 $? 来获得。

注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。

函数参数

在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...

带参数的函数示例:

#!/bin/bash

funWithParam(){

    echo "第一个参数为 $1 !"

    echo "第二个参数为 $2 !"

    echo "第十个参数为 $10 !"

    echo "第十个参数为 ${10} !"

    echo "第十一个参数为 ${11} !"

    echo "参数总数有 $# 个!"

    echo "作为一个字符串输出所有参数 $* !"

}

funWithParam 1 2 3 4 5 6 7 8 9 34 73

输出结果:

第一个参数为 1 !

第二个参数为 2 !

第十个参数为 10 !

第十个参数为 34 !

第十一个参数为 73 !

参数总数有 11 个!

作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !

注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

另外,还有几个特殊字符用来处理参数:

参数处理说明

$#传递到脚本的参数个数

$*以一个单字符串显示所有向脚本传递的参数

$$脚本运行的当前进程ID号

$!后台运行的最后一个进程的ID号

$@与$*相同,但是使用时加引号,并在引号中返回每个参数。

$-显示Shell使用的当前选项,与set命令功能相同。

$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。




二十、Shell 输入/输出重定向

2课时

实验课

主要内容

Shell 输入/输出重定向

大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回​​到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。

重定向命令列表如下:

命令说明

command > file将输出重定向到 file。

command < file将输入重定向到 file。

command >> file将输出以追加的方式重定向到 file。

n > file将文件描述符为 n 的文件重定向到 file。

n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。

n >& m将输出文件 m 和 n 合并。

n <& m将输入文件 m 和 n 合并。

<< tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。

需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

输出重定向

重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:

command1 > file1

上面这个命令执行command1然后将输出的内容存入file1。

注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。

实例

执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):

$ who > users

执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。

你可以使用 cat 命令查看文件内容:

$ cat users

_mbsetupuser console  Oct 31 17:35

laolan    console  Oct 31 17:35

laolan    ttys000  Dec  1 11:33

输出重定向会覆盖文件内容,请看下面的例子:

$ echo "W3Cschool教程:www.w3cschool.cn" > users

$ cat users

W3Cschool教程:www.w3cschool.cn

$

如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:

$ echo "W3Cschool教程:www.w3cschool.cn" >> users

$ cat users

W3Cschool教程:www.w3cschool.cn

W3Cschool教程:www.w3cschool.cn

$

输入重定向

和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:

command1 < file1

这样,本来需要从键盘获取输入的命令会转移到文件读取内容。

注意:输出重定向是大于号(>),输入重定向是小于号(<)。

实例

接着以上实例,我们需要统计 users 文件的行数,执行以下命令:

$ wc -l users

      2 users

也可以将输入重定向到 users 文件:

$  wc -l < users

      2

注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。

command1 < infile > outfile

同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。

重定向深入讲解

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。

标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。

标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

如果希望 stderr 重定向到 file,可以这样写:

$ command 2 > file

如果希望 stderr 追加到 file 文件末尾,可以这样写:

$ command 2 >> file

2 表示标准错误文件(stderr)。

如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:

$ command > file 2>&1

或者

$ command >> file 2>&1

如果希望对 stdin 和 stdout 都重定向,可以这样写:

$ command < file1 >file2

command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。

Here Document

Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。

它的基本的形式如下:

command << delimiter

    document

delimiter

它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。

注意:

结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。

开始的delimiter前后的空格会被忽略掉。

实例

在命令行中通过 wc -l 命令计算 Here Document 的行数:

$ wc -l << EOF

    欢迎来到

    W3Cschool教程

    www.w3cschool.cn

EOF

3          # 输出结果为 3 行

$

我们也可以将 Here Document 用在脚本中,例如:

#!/bin/bash

# author:W3Cschool教程

# url:www.w3cschool.cn

cat << EOF

欢迎来到

W3Cschool教程

www.w3cschool.cn

EOF

执行以上脚本,输出结果:

欢迎来到

W3Cschool教程

www.w3cschool.cn

/dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

$ command > /dev/null

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。

如果希望屏蔽 stdout 和 stderr,可以这样写:

$ command > /dev/null 2>&1

注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。








【FOOTER】

日记本
Gupao