Linux系统的Python管理,对


Python对Linux系统的管理

一、OS模块常用功能

1、os模块打开文件

方法如下:

os.open(filename, flag, [,mode])

flag参数说明:

os.O_CREAT # 创建文件
os.O_RDONLY # 只读方式打开
os.O_WRONLY # 只写方式打开
os.O_RDWR   # 读写方式打开

2、os模块对文件进行操作

常用方法如下:

# 读取文件
os.read(fd, buffersize)
# 写入文件
os.write(fd, string)
# 文件指针操作
os.lseek(fd, pos, how)
# 关闭文件
os.close(fd)

代码演示:

文件创建和写入

import os

# 打开文件
fd = os.open("abc.txt", os.O_RDWR | os.O_CREAT)

# 写入字符串
str = "Hello Python!"
ret = os.write(fd, bytes(str, 'UTF-8'))

# 输入返回值
print("写入的位数为: ")
print(ret)

print("写入成功")

# 关闭文件
os.close(fd)
print("关闭文件成功!!")

文件读取

import os

# 打开文件
fd = os.open("abc.txt", os.O_RDWR)

# 读取文本
ret = os.read(fd, 6)
print(ret)

# 关闭文件
os.close(fd)
print("关闭文件成功!!")

3、os模块管理文件和目录

常用方法如下:

os方法 说明
getcwd() 获取当前目录
listdir(path) 返回当前目录下所有文件组成的列表
chdir(path) 切换目录
rename(old, new) 修改文件或者目录名
mkdir(path [,mode]) 创建目录
makedirs(path [,mode]) 创建多级目录
rmdir(path) 删除目录(目录必须为空目录)
removedirs(path) 删除多级目录(目录必须为空目录)
remove(path) 删除文件

代码演示:

# coding=utf-8

import os


print(os.getcwd())  # pwd
print(os.listdir()) # ls
os.chdir('/opt') # cd /opt
os.rename('abc.txt','test.txt') # mv abc.txt test.txt
os.remove('read.py')    # rm -f abc.txt

os.mkdir('test')    # mkdir dir1
os.makedirs('demo/abc') # mkdir -p dir2/dir22
os.rmdir('test')    # 目录必须为空
os.removedirs('demo')   # 目录必须为空

4、os模块管理文件权限

os方法 说明
access(path, mode) 判断该文件权限:F_OK表示该路径存在;
权限:R_OK,W_OK,X_OK
chmod(path, mode) 修改文件权限:0o755
chown(path, uid, gid) 更改文件所有者,如果不修改可以设置为 -1

代码演示:

import os


# 测试路径是否存在:os.F_OK
res = os.access('test.txt',os.F_OK)
print(res)

# 测试当前用户对该文件是否有读的权限
res = os.access('test.txt',os.R_OK)
print(res)

# 测试当前用户对该文件是否有写的权限
res = os.access('test.txt',os.W_OK)
print(res)

# 测试当前用户对该文件是否有执行的权限
res = os.access('test.txt',os.X_OK)
print(res)

# 更改当前用户的权限
os.chmod('test.txt',0o755)

# 更改文件的所有者
os.chown('test.txt', 1001, 1002)  

5、os.path模块管理文件与路径

(1)拆分路径
os.path方法 说明
os.path.split(path) 返回一个二元组,包含文件的路径和文件名
os.path.dirname(path) 返回文件的路径
os.path.basename(path) 返回文件名
os.path.splitext(path) 返回一个去掉文件扩展名的部分和扩展名的二元组

代码演示:

In [10]: os.getcwd()
Out[10]: '/opt/os_demo'

In [11]: os.listdir()
Out[11]: ['os_access.py', 'test.txt']

In [12]: path = '/opt/os_demo/test.txt'

In [13]: os.path.split(path)
Out[13]: ('/opt/os_demo', 'test.txt')

In [14]: os.path.dirname(path)
Out[14]: '/opt/os_demo'

In [15]: os.path.basename(path)
Out[15]: 'test.txt'

In [16]: os.path.splitext(path) 
Out[16]: ('/opt/os_demo/test', '.txt')
(2)构建路径
os.path方法 说明
os.path.expanduser(path) 展开用户的HOME目录,如 oracle
os.path.abspath(path) 得到文件或路径的绝对路径
os.path.join(path) 根据不同的操作系统平台,使用不同的路径分隔符拼接路径
os.path.isabs(path) 检查一个路径是不是一个绝对路径

代码演示:

In [19]: os.path.expanduser('~') 
Out[19]: '/root'

In [20]: os.path.expanduser('~oracle') 
Out[20]: '/home/oracle'

In [21]: os.path.expanduser('~accp')
Out[21]: '/home/accp'

In [22]: os.path.expanduser('~acp') 
Out[22]: '~acp'    # 错误演示

In [23]: os.path.abspath('.')
Out[23]: '/opt/os_demo'

In [24]: os.path.abspath('..')
Out[24]: '/opt'

In [25]: os.path.join('/opt/os_demo','test.txt')
Out[25]: '/opt/os_demo/test.txt'

In [26]: os.path.isabs('/opt/os_demo/') 
Out[26]: True

In [27]: os.path.isabs('.') 
Out[27]: False
(3)获取文件属性
os.path方法 说明
os.path.getatime(path) 返回最近访问时间(浮点型秒数)
os.path.getmtime(path) 返回最近文件修改时间
os.path.getctime(path) 返回文件 path 创建时间
os.path.getsize(path) 返回文件大小,如果文件不存在就返回错误

代码演示:

In [33]: os.path.getatime(path)
Out[33]: 1587547270.7306058    # 时间戳

In [34]: os.path.getmtime(path)
Out[34]: 1587547270.7306058

In [35]: os.path.getctime(path)
Out[35]: 1587548055.4721448

In [36]: os.path.getsize(path)
Out[36]: 0
(4)判断文件类型
os.path方法 说明
os.path.isfile(path) 判断路径是否为文件
os.path.isdir(path) 判断路径是否为目录
os.path.islink(path) 判断路径是否为链接
os.path.ismount(path) 判断路径是否为挂载点

代码演示:

In [37]: os.path.isfile(path)
Out[37]: True

In [38]: os.path.isdir(path)
Out[38]: False

In [39]: os.path.islink(path)
Out[39]: False

In [40]: os.path.ismount(path)
Out[40]: False

6、os模块执行shell命令

os.system()的作用:

执行shell命令
返回shell命令的返回值
命令的输出会输出到标准输出

代码演示:

os.system(‘cls’)

案例1:编写自动安装Python的脚本

实现步骤:

下载Python版本源码
安装Python需要的依赖库
编译安装Python

伪代码:

1. 判断用户是不是root
2. 如果是,等待用户输入Python版本
3. 执行shell命令下载源码包
4. 安装依赖开发包
5. 编译安装Python

脚本内容如下(基于Python2):

auto_install_python.py

# coding=utf-8
import os

# 判断用户是否是root用户
if os.getuid() == 0:
    pass
else:
    print
    '当前用户不是root用户!'
    SystemExit(1)

# 安装Python依赖库
cmd_module = 'yum -y install wget gcc zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel'
res = os.system(cmd_module)
if res != 0:
    print
    'Python依赖库安装失败,请重新执行该脚本。'
    SystemExit(1)
else:
    print
    'python依赖库安装成功!'

# 输入Python版本,下载Python源码包到本地目录
# wget url
version = raw_input('请输入Python版本:(3.6/3.8)')
if version == '3.6':
    url = 'https://www.python.org/ftp/python/3.6.10/Python-3.6.10.tgz'
else:
    url = 'https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz'
cmd = 'wget ' + url
res = os.system(cmd)
if res != 0:
    print 'Python源码包下载失败!'
    SystemExit(1)
else:
    print('======================>>>Python源码包下载成功!')

# 解压Python源码包
# tar zxvf Python-3.6.10.tgz
if version == '3.6':
    package_name = 'Python-3.6.10'
else:
    package_name = 'Python-3.8.1'
res = os.system('tar zxvf ' + package_name + '.tgz')
if res != 0:
    print '解压失败。。。'
    SystemExit(1)
else:
    print('#########################解压成功!#########################')

# 配置语言
os.system('export LANG=zh_CN.UTF-8')
os.system('export LANGUAGE=zh_CN.UTF-8')

# 切换到Python目录
os.chdir(package_name)
os.system('./configure --prefix=/usr/local/python3')
res = os.system('make && make install')
if res !=0:
    print '源码编译失败。。。'
    SystemExit(1)
else:
    print('####################Python安装成功,请进行验证!####################')

# 修改用户环境变量
os.system('echo "export PYTHON3=/usr/local/python3" >>~/.bash_profile')
os.system('echo "export PATH=$PYTHON3/bin:$PATH" >>~/.bash_profile')
os.system("source ~/.bash_profile")
os.system('cat ~/.bash_profile')
print('####################用户环境变量已修改,请进行验证!####################')
os.system('python3 --version')

7、os.walk函数遍历目录树

os.walk() 方法遍历某个目录及其子目录,对于每一个目录,walk()函数返回一个三元组(dirpath、dirnames、filenames)。其中dirpath保存的是当前目录,dirnames是当前目录下的子目录列表,filenames是当前目录下的文件列表。

# coding=utf-8

import os


for root, dirs, files in os.walk(".", topdown=False):
    for name in files:
        print(os.path.join(root, name))
    for name in dirs:
        print(os.path.join(root, name))

os.walk() 方法是一个简单易用的文件、目录遍历器,可以帮助我们高效的处理文件、目录方面的事情。

案例2:打印最常用的10条Linux命令

import os
from collections import Counter


count = Counter()
with open(os.path.expanduser('~/.bash_history')) as f:
    for line in f:
        cmd = line.strip().split()
        if cmd:
            count[cmd[0]] +=1

print(count.most_common(10))

二、使用ConfigParser类解析配置文件

Python中有ConfigParser类,可以很方便的从配置文件中读取数据(如DB的配置,路径的配置),所以可以自己写一个函数,实现读取config配置。

1、配置文件的格式

节:               [session]
参数(键=值)         name=value

mysql配置文件部分内容如下:

[client]
port        = 3306
user        = mysql
password    = mysql
host        = 127.0.0.1

[mysqld]
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
skip-external-locking

2、ConfigParser类的使用方法

(1)创建configParser对象
In [1]: import configparser

In [2]: cf = configparser.ConfigParser(allow_no_value=True)
(2)读取配置文件内容
In [3]: cf.read('my.inf')
Out[3]: ['my.ini']   
(3)获取配置文件信息
sections: 返回一个包含所有章节的列表
options:返回一个包含章节下所有选项的列表

has_section:判断章节是否存在
has_options:判断某个选项是否存在

items:以元组的形式返回所有的选项
get、getboolean、getint、getfloat:获取选项的值

方法测试:

In [4]: cf.sections()
Out[4]: ['client', 'mysqld']

In [5]: cf.has_section('client')
Out[5]: True

In [6]cf.options('client')
Out[6]: ['port', 'user', 'password', 'host']

In [7]: cf.has_option('client','user')
Out[7]: True

In [8]: cf.get('client','port')
Out[8]: '3306'

In [9]: cf.getint('client','port')
Out[9]: 3306
(4)修改配置文件

常用方法:

remove_section:删除一个章节
add_section:添加一个章节
remove_option:删除一个选项
set:添加一个选项
write:将ConfigParser兑现中的数据保存到文件中

方法测试:

In [11]: cf.remove_section('client')
Out[11]: True

In [12]: cf.write(open('my.ini', 'w'))

In [13]: cf.add_section('client')

In [14]: cf.set('client','port','3306')

In [15]: cf.set('client','user','mysql')

In [16]: cf.set('client','password','mysql')

In [17]: cf.set('client','host','127.0.0.1')

In [18]: cf.write(open('my.ini','w'))

In [19]: cf.remove_option('client','host')
Out[19: True

In [20]: cf.write(open('my.ini','w'))

三、查找文件

1、使用fnmatch找到特定文件

fnmatch.fnmatch()函数一次只能处理一个文件

import os
import fnmatch

for item in os.listdir('.'):
    if os.path.isfile(item):
        # if fnmatch.fnmatch(item, '*.jpg'):
        if fnmatch.fnmatch(item, '[a-e]*'):
        # if fnmatch.fnmatch(item, '[a-g]?.txt'):
        # if fnmatch.fnmatch(item, '[!a-c]*'):
            print(item)

fnmatch.filter()函数一次可以处理多个文件

import os
import fnmatch

items = os.listdir('.')
files = fnmatch.filter(items, '[a-c]*')
print(files)

2、使用glob找到特定文件

标准库glob的作用相当于os.listdir()加上fnmatch。使用glob以后,不需要调用os.listdir()获取文件列表,直接通过模式匹配即可。如下所示:

import glob

files = glob.glob('*.jpg')
print(files)

案例3:找到目录下最大(或最老)的10个文件

四、高级文件处理接口shutil

1、复制文件和文件夹

shutil.copy(file1,file2)
shuti.copytree(dir1, dir2)

2、文件和文件夹的移动与重命名

shutil.move(file1, file2)
shutil.move(file, dir)

3、删除目录

shutil.rmtree(dir)
os.unlink(file)

五、文件内容管理

1、目录和文件对比

filecmp模块包含了比较目录和文件的操作。

目录结构如下,其中,a.txt和c.txt内容是一样的,a_copy.txt是a.txt的拷贝。

├─dir1
│  │  a.txt
│  │  a_copy.txt
│  │  b.txt
│  │  c.txt
│  └─subdir1
│          sa.txt
└─dir2
    │  a.txt
    │  b.txt
    │  c.txt
    ├─subdir1
    │      sb.txt
    └─subdir2

(1)比较两个文件

使用filecmp模块的cmp函数比较两个文件是否相同,如果文件相同则返回True,否则返回False。

In [1]: import filecmp

In [2]: cd dir1
E:\git-project\python_project\cloud33\Python常用模块\compare\dir1

In [3]: filecmp.cmp('a.txt','b.txt')
Out[3]: False

In [4]: filecmp.cmp('a.txt','c.txt')
Out[4]: True

In [5]: filecmp.cmp('a.txt','a_copy.txt')
Out[5]: True

(2)比较多个文件

filecmp目录下还有一个名为cmpfiles的函数,该函数用来同时比较两个不同的目录下的多个文件,并且返回一个三元组,分别包含相同的文件、不同的文件和无法比较的文件。示例如下:

In [6]: filecmp.cmpfiles('dir1','dir2',['a.txt','b.txt','c.txt','a_copy.txt'])
Out[6]: (['a.txt', 'b.txt', 'c.txt'], [], ['a_copy.txt'])

cmpfiles函数同时用来比较两个目录下的文件,也可以使用该函数比较两个目录。但是,在比较两个目录时,需要通过参数指定可能的文件,因此比较繁琐。

(3)比较目录

filecmp中还有一个名为dircmp的函数,用来比较两个目录。调用dircmp函数以后,会返回一个dircmp类的对象,该对象保存了诸多属性,我们可以通过查看这些属性获取目录之间的差异。如下所示:

In [7]: d = filecmp.dircmp('dir1','dir2')

In [8]: d.report()
diff dir1 dir2
Only in dir1 : ['a_copy.txt']
Only in dir2 : ['subdir2']
Identical files : ['c.txt']
Differing files : ['a.txt', 'b.txt']
Common subdirectories : ['subdir1']

In [9]: d.left_list
Out[9]: ['a.txt', 'a_copy.txt', 'b.txt', 'c.txt', 'subdir1']

In [10]: d.right_list
Out[10]: ['a.txt', 'b.txt', 'c.txt', 'subdir1', 'subdir2']

In [11]: d.left_only
Out[11]: ['a_copy.txt']

In [12]: d.right_only
Out[12]: ['subdir2']

2、MD5校验和比较

校验码是通过散列函数计算而成,是一种从任何数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,便于进行比较。MD5是目前使用最广泛的散列算法。

MD5哈希一般用于检查文件的完整性,尤其常用于检查文件传输、磁盘错误或其他情况下文件的正确性。

Linux下计算一个文件的MD5校验码,如下所示:

[root@192 demo]# md5sum a.txt
d41d8cd98f00b204e9800998ecf8427e  a.txt

在Python中计算文件的MD5校验码也非常简单,使用标准库hashlib模块即可。如下所示:

import hashlib

d = hashlib.md5()
with open('b.txt') as f:
    for line in f:
        d.update(line.encode('utf-8'))
print(d.hexdigest())

# 或者可以这样(最常见的写法,常用于图片的命名)
>>> hashlib.md5(b'123').hexdigest()
'202cb962ac59075b964b07152d234b70'

# 也可以使用hash.new()这个一般方法,hashlib.new(name[, data]),name传入的是哈希加密算法的名称,如md5
>>> hashlib.new('md5', b'123').hexdigest()
'202cb962ac59075b964b07152d234b70'

六、使用Python管理压缩包

1、tarfile

(1)读取tar包
import tarfile

with tarfile.open('cmake-3.17.0.tar.gz') as t:
    for member in t.getmembers():
        print(member.name)

with tarfile.open('cmake-3.17.0.tar.gz') as t:
t.extractall()
t.extract('cmake-3.17.0/Help','a')

常用方法说明:

getmembers():获取tar包中的文件列表
member.name:获取tar包中文件的文件名
extract(member, path):提取单个文件
extractall(path, memebers):提取所有的文件
(2)创建tar包
import tarfile

with tarfile.open('readme.tar', mode='w') as out:
    out.add('read.txt')
(3)读取与创建压缩包
import tarfile

with tarfile.open('tarfile_add.tar',mode='r:gz') as out:
    pass

with tarfile.open('tarfile_add.tar',mode='r:bz2') as out:
    pass

案例4:备份指定文件到压缩包中

import os
import fnmatch
import tarfile
import datetime


def is_file_math(filename, patterns):
    '''查找特定类型的文件'''
    for pattern in patterns:
        if fnmatch.fnmatch(filename, pattern):
            return True
        return False


def find_files(root, patterns=['*']):
    for root, dirnames, filenames in os.walk(root):
        for filename in filenames:
            if is_file_math(filename, patterns):
                yield os.path.join(root, filename)


patterns = ['*.txt','*.md']
now = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S')
filename = 'backup_all_file_{0}.tar.gz'.format(now)
with tarfile.open(filename, 'w') as f:
    for item in find_files('.', patterns):
        f.add(item)

2、zipfile

(1)读取zip文件
import zipfile

demo_zip = zipfile.ZipFile('read.zip')
print(demo_zip.namelist())
demo_zip.extractall('1')
demo_zip.extract('a.jpg','2')

常用方法说明:

namelist():返回zip文件中包含的所有文件和文件夹的字符串列表
extract(filename, path):从zip文件中提取单个文件
extractall(path):从zip文件中提取所有文件
(2)创建zip文件
import zipfile

newZip = zipfile.ZipFile('new.zip',mode='w')
newZip.write('a.jpg')
newZip.close()
(3)Python命令行调用zipfile

zipfile模块提供的命令行接口包含的选项:

-l:显示zip格式压缩包中的文件列表
-e:提取zip格式的压缩包
-c:创建zip格式的压缩包
-t:验证文件是不是一个有效的zip格式压缩包

示例如下所示:

# 创建zip文件
python -m zipfile -c new1.zip archive_tar.py

# 查看zip文件列表
python -m zipfile -l new1.zip
File Name                                             Modified             Size
archive_tar.py                                 2020-04-26 16:32:54          239

# 提取zip文件到指定目录
python -m zipfile -e new1.zip new_dir

3、shutil创建和读取压缩包

shutil支持的格式如下:

import shutil

print(shutil.get_archive_formats())

[('bztar', "bzip2'ed tar-file"), ('gztar', "gzip'ed tar-file"), ('tar', 'uncompressed tar file'), ('xztar', "xz'ed tar-file"), ('zip', 'ZIP file')]
(1)创建压缩包
# 参数1:生成的压缩包文件名
# 参数2:压缩包的格式
# 参数3:压缩的目录
shutil.make_archive('a.jpg','gztar', 'ddd')
(2)解压
# 参数1:需要解压的压缩包
# 参数2:解压的目录
print(shutil.unpack_archive('a.jpg.tar.gz','jpg'))

七、Python执行外部命令

1、subprocess模块简介

这个模块用来创建和管理子进程。它提供了高层次的接口,用来替换os.system()、os.spawn*()、os.popen*()、os.popen2.*()和commands.*等模块和函数。

subprocess提供了一个名为Popen的类启动和设置子进程的参数,由于这个类比较复杂,subprosess还提供了若干便利的函数,这些函数都是对Popen类的封装。

2、subprocess模块的便利函数

(1)call

call函数的定义如下

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)

示例如下:

In [1]: import subprocess                                                                     

In [2]: subprocess.call(['ls','-l'])                                                          
总用量 8
-rw-------. 1 root root 2049 811 2019 anaconda-ks.cfg
-rw-r--r--. 1 root root 2077 811 2019 initial-setup-ks.cfg
drwxr-xr-x. 2 root root   21 422 16:32 os
drwxr-xr-x. 2 root root    6 811 2019 公共
drwxr-xr-x. 2 root root    6 811 2019 模板
drwxr-xr-x. 2 root root    6 811 2019 视频
drwxr-xr-x. 2 root root    6 811 2019 图片
drwxr-xr-x. 2 root root    6 811 2019 文档
drwxr-xr-x. 2 root root    6 811 2019 下载
drwxr-xr-x. 2 root root    6 811 2019 音乐
drwxr-xr-x. 2 root root    6 811 2019 桌面
Out[2]: 0

In [3]: subprocess.call('exit 1', shell=True)                                             
Out[3]: 1 
(2)check_call

check_all函数的作用与call函数类似,区别在于异常情况下返回的形式不同。

对于call函数,工程师通过捕获call命令的返回值判断命令是否执行成功,如果成功则返回0,否则的话返回非0。对于check_call函数,如果执行成功,返回0,如果执行失败,抛出subprocess.CallProseccError异常,示例如下所示:

In [6]: subprocess.check_call(['ls','-l'])                                                    
总用量 8
-rw-------. 1 root root 2049 811 2019 anaconda-ks.cfg
-rw-r--r--. 1 root root 2077 811 2019 initial-setup-ks.cfg
drwxr-xr-x. 2 root root   21 422 16:32 os
drwxr-xr-x. 2 root root    6 811 2019 公共
drwxr-xr-x. 2 root root    6 811 2019 模板
drwxr-xr-x. 2 root root    6 811 2019 视频
drwxr-xr-x. 2 root root    6 811 2019 图片
drwxr-xr-x. 2 root root    6 811 2019 文档
drwxr-xr-x. 2 root root    6 811 2019 下载
drwxr-xr-x. 2 root root    6 811 2019 音乐
drwxr-xr-x. 2 root root    6 811 2019 桌面
Out[6]: 0

In [7]: subprocess.check_call('lsljdl', shell=True)                                           
/bin/sh: lsljdl: 未找到命令
---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
<ipython-input-7-885ea94380a9> in <module>
----> 1 subprocess.check_call('lsljdl', shell=True)

/usr/local/python3/lib/python3.8/subprocess.py in check_call(*popenargs, **kwargs)
    362         if cmd is None:
    363             cmd = popenargs[0]
--> 364         raise CalledProcessError(retcode, cmd)
    365     return 0
    366 

CalledProcessError: Command 'lsljdl' returned non-zero exit status 127.
(3)check_output
In [8]: output = subprocess.check_output(['df','-h'])                                         

In [9]: print(output.decode())                                                               
文件系统             容量  已用  可用 已用% 挂载点
/dev/mapper/cl-root   37G  5.1G   32G   14% /
devtmpfs             897M     0  897M    0% /dev
tmpfs                912M   84K  912M    1% /dev/shm
tmpfs                912M  9.0M  903M    1% /run
tmpfs                912M     0  912M    0% /sys/fs/cgroup
/dev/sda1           1014M  173M  842M   18% /boot
tmpfs                183M   16K  183M    1% /run/user/42
tmpfs                183M     0  183M    0% /run/user/0


In [10]: lines = output.decode().split('\n')                                                  

In [11]: lines                                                                           
Out[11]: 
['文件系统             容量  已用  可用 已用% 挂载点',
 '/dev/mapper/cl-root   37G  5.1G   32G   14% /',
 'devtmpfs             897M     0  897M    0% /dev',
 'tmpfs                912M   84K  912M    1% /dev/shm',
 'tmpfs                912M  9.0M  903M    1% /run',
 'tmpfs                912M     0  912M    0% /sys/fs/cgroup',
 '/dev/sda1           1014M  173M  842M   18% /boot',
 'tmpfs                183M   16K  183M    1% /run/user/42',
 'tmpfs                183M     0  183M    0% /run/user/0',
 '']

In [12]: for line in lines[1:-1]: 
    ...:     if line: 
    ...:         print(line.split()[-2]) 
    ...:                                                                                 t
14%
0%
1%
1%
0%
18%
1%
0%

3、subprocess模块的Popen类

# coding=utf-8
import subprocess


def execute_cmd(cmd):
    p = subprocess.Popen(cmd, shell=True,
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    stdout, stderr = p.communicate()

    if p.returncode != 0:
        return p.returncode, stderr
    return p.returncode, stdout


execute_cmd('ls')

八、综合案例:使用Python部署MongoDB

看一个综合案例,使用Python不俗MongoDB数据库。在这个例子中,将会用到各种与系统管理相关的标准库,包括os、os.path、shutil、tarfile和subprocess模块。

假设当前目录下存在一个MongoDB安装包,我们的Python程序需要将他解压到当前目录的mongo目录下,并且当前目录创建一个mongodata目录用老保存MongoDB的数据库文件。

在部署MongoDB数据库之前,当前目录下的文件结构如下:

.
├── auto_install_mongodb.py
└── ./mongodb-linux-x86_64-rhel70-4.2.3.tgz

程序部署完成后,当前目录的文件结构大致如下:

.
├── ./auto_install_mongodb.py
├── ./mongo
│   ├── ./mongo/bin
│   │   ├── ./mongo/bin/bsondump
│   │   ├── ./mongo/bin/install_compass
│   │   ├── ./mongo/bin/mongo
│   │   ├── ./mongo/bin/mongod
│   │   ├── ./mongo/bin/mongodump
│   │   ├── ./mongo/bin/mongoexport
│   │   ├── ./mongo/bin/mongofiles
│   │   ├── ./mongo/bin/mongoimport
│   │   ├── ./mongo/bin/mongoreplay
│   │   ├── ./mongo/bin/mongorestore
│   │   ├── ./mongo/bin/mongos
│   │   ├── ./mongo/bin/mongostat
│   │   └── ./mongo/bin/mongotop
│   ├── ./mongo/LICENSE-Community.txt
│   ├── ./mongo/MPL-2
│   ├── ./mongo/README
│   ├── ./mongo/THIRD-PARTY-NOTICES
│   └── ./mongo/THIRD-PARTY-NOTICES.gotools
├── ./mongodata
│   ├── ./mongodata/collection-0-4813754152483353608.wt
│   ├── ./mongodata/collection-2-4813754152483353608.wt
│   ├── ./mongodata/collection-4-4813754152483353608.wt
│   ├── ./mongodata/diagnostic.data
│   │   ├── ./mongodata/diagnostic.data/metrics.2020-04-27T10-17-57Z-00000
│   │   └── ./mongodata/diagnostic.data/metrics.interim
│   ├── ./mongodata/index-1-4813754152483353608.wt
│   ├── ./mongodata/index-3-4813754152483353608.wt
│   ├── ./mongodata/index-5-4813754152483353608.wt
│   ├── ./mongodata/index-6-4813754152483353608.wt
│   ├── ./mongodata/journal
│   │   ├── ./mongodata/journal/WiredTigerLog.0000000001
│   │   ├── ./mongodata/journal/WiredTigerPreplog.0000000001
│   │   └── ./mongodata/journal/WiredTigerPreplog.0000000002
│   ├── ./mongodata/_mdb_catalog.wt
│   ├── ./mongodata/mongod.lock
│   ├── ./mongodata/mongod.log
│   ├── ./mongodata/sizeStorer.wt
│   ├── ./mongodata/storage.bson
│   ├── ./mongodata/WiredTiger
│   ├── ./mongodata/WiredTigerLAS.wt
│   ├── ./mongodata/WiredTiger.lock
│   ├── ./mongodata/WiredTiger.turtle
│   └── ./mongodata/WiredTiger.wt
└── ./mongodb-linux-x86_64-rhel70-4.2.3.tgz

MongoDB下载地址如下:

wget https://fastdl.mongodb.org/linux/mongodb-linux-s390x-rhel72-4.3.3.tgz

MongoDB是当下最流行的文档数据库,具有很好的易用性。启动一个MongoDB数据库实例,只需要执行一下几条shell命令即可:

tar -zxf mongodb-linux-x86_64-rhel70-4.2.3.tgz
mv mongodb-linux-x86_64-rhel70-4.2.3 mongo
mkdir mongodata
mongo/bin/mongod --fork --logpath mongodata/mongod.log --dbpath mongodata

这里给出的shell命令,只是为了便于不熟悉MongoDB的人了解MongoDB数据库的启动过程,还有很多的情况没有考虑。例如,要将当前目录下的MongoDB安装包解压到当前目录下的mongo目录中,但是当前目录下已经存在一个名为mongo的目录,则会报错

下面的程序时使用Python部署MongoDB数据库的完整代码,这段程序综合应用了很多与系统管理相关的模块。如下所示:

# coding=utf-8
import subprocess
import os
import shutil
import tarfile


def execute_cmd(cmd):
    '''执行shell命令'''
    p = subprocess.Popen(cmd, shell=True,
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    stdout, stderr = p.communicate()
    if p.returncode != 0:
        return p.returncode, stderr
    return p.returncode, stdout


def unpackage_mongo(package, package_dir):
    unpackage_dir = os.path.splitext(package)[0]
    if os.path.exists(unpackage_dir):
        shutil.rmtree(unpackage_dir)
    if os.path.exists(package_dir):
        shutil.rmtree(package_dir)
    # 解压
    t = tarfile.open(package, 'r:gz')
    t.extractall('.')
    print('tar is ok.')
    # 重命名mongodb-linux-x86_64-rhel70-4.2.3为mongo
    shutil.move(unpackage_dir, 'mongo')


def create_datadir(data_dir):
    if os.path.exists(data_dir):
        shutil.rmtree(data_dir)
    os.mkdir(data_dir)


def format_mongod_commamd(package_dir, data_dir, logfile):
    mongod = os.path.join(package_dir, 'bin', 'mongod')
    mongod_format = """{0} --fork --dbpath {1} --logpath {2}"""
    return mongod_format.format(mongod, data_dir, logfile)


def start_mongod(cmd):
    returncode, out = execute_cmd(cmd)
    if returncode != 0:
        raise SystemExit('execute {0} error:{1}'.format(cmd, out.decode()))
    else:
        print('execute {0} sucessfully.'.format(cmd))


def main():
    package = 'mongodb-linux-x86_64-rhel70-4.2.3.tgz'
    cur_dir = os.path.abspath('.')
    package_dir = os.path.join(cur_dir, 'mongo')
    data_dir = os.path.join(cur_dir, 'mongodata')
    logfile = os.path.join(data_dir, 'mongod.log')

    if not os.path.exists(package):
        raise SystemExit('{0} not found.'.format(package))

    unpackage_mongo(package, package_dir)
    create_datadir(data_dir)
    start_mongod(format_mongod_commamd(package_dir, data_dir, logfile))


main()

代码说明:

在这段过程中,我们首先在main函数中定义了几个变量,包括当前目录的路径,MongoDB二进制文件所在的路径、MongoDB数据目录所在的路径,以及MongoDB的日志问紧啊

随后,我们判断MongoDB的安装包是否存在,如果不存在,则通过抛出SystemExit异常的方式结束程序

在unpackage_mongo函数中,我们通过Python程序得到MongoDB安装包解压以后的目录。如果目录已经存在,则删除该目录。随后,我们使用tarfile解压MongoDB数据库,解压完成后,将命令重命名为mongo目录

在create_datadir目录红,我们首先判断MongoDB数据库目录是否存在,如果存在,则删除该目录,随后在创建MongoDB数据库目录

在start_mongod函数中,我们执行MongoDB数据库的启动命令启动MongoDB数据库。为了在Python代码中执行shell命令,我们使用了subprocess库,我们将subprocess库执行shell命令的瑞吉封装成execute_cmd函数,在执行shell命令时,直接调用该函数即可