Python(十三)脚本编程与系统管理

Posted HT . WANG

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python(十三)脚本编程与系统管理相关的知识,希望对你有一定的参考价值。

1.通过重定向/管道/文件接受输入

Python内置的 fileinput 模块将命令行的输出通过管道传递给该脚本、 重定向文件到该脚本,或在命令行中传递一个文件名或文件名列表给该脚本

#!/usr/bin/env python3
#-----------------
#filein.py
#-----------------
import fileinput

with fileinput.input() as f_input:
    for line in f_input:
        print(line, end='')


$ ls | ./filein.py          # 打印当前目录下文件列表进行输出显示
$ ./filein.py /etc/passwd   # 读取passwd文件内容显示
$ ./filein.py < /etc/passwd # 读取passwd文件内容重定向到终端窗口显示

2.终止程序并给出错误信息

想向标准错误打印一条消息并返回某个非零状态码来终止程序运行

import sys
sys.stderr.write('It failed!\\n') #打印错误信息
raise SystemExit(1) #返回状态码退出程序

直接将消息作为参数传给 SystemExit() ,那么你可以省略其他步骤

raise SystemExit('It failed!')

抛出一个 SystemExit 异常,使用错误消息作为参数,它会将消息在 sys.stderr 中打印,然后程序以状态码1退出

3.解析命令行选项

如何通过程序能够解析命令行选项

import argparse
parser = argparse.ArgumentParser(description='Search some files')

parser.add_argument(dest='filenames',metavar='filename', nargs='*')

parser.add_argument('-p', '--pat',metavar='pattern', required=True,
                    dest='patterns', action='append',
                    help='text pattern to search for') #允许某个参数重复出现多次,并将它们追加到一个列表中去

parser.add_argument('-v', dest='verbose', action='store_true',
                    help='verbose mode') #根据参数是否存在来设置一个 Boolean 标志

parser.add_argument('-o', dest='outfile', action='store',
                    help='output file') #接受一个单独值并将其存储为一个字符串

parser.add_argument('--speed', dest='speed', action='store',
                    choices='slow','fast', default='slow',
                    help='search speed')#接受一个值将其和可能的选择值做比较,以检测其合法性

args = parser.parse_args()

# Output the collected arguments
print(args.filenames)
print(args.patterns)
print(args.verbose)
print(args.outfile)


bash % python3 search.py -v -p spam --pat=eggs foo.txt bar.txt
filenames = ['foo.txt', 'bar.txt']
patterns  = ['spam', 'eggs']
verbose   = True
outfile   = None
speed     = slow

bash % python3 search.py -v -p spam --pat=eggs foo.txt bar.txt -o results
filenames = ['foo.txt', 'bar.txt']
patterns  = ['spam', 'eggs']
verbose   = True
outfile   = results
speed     = slow

bash % python3 search.py -v -p spam --pat=eggs foo.txt bar.txt -o results --speed=fast
filenames = ['foo.txt', 'bar.txt']
patterns  = ['spam', 'eggs']
verbose   = True
outfile   = results
speed     = fast
print(args.speed)

4.运行时弹出密码输入提示

写了个脚本,运行时需要一个密码。此脚本是交互式的,因此不能将密码在脚本中硬编码, 而是需要弹出一个密码输入提示,让用户自己输入。

import getpass

user = getpass.getuser() #该函数不会弹出用户名的输入提示。 它会根据该用户的shell环境或者会依据本地系统的密码库(支持 pwd 模块的平台)来使用当前用户的登录名,
passwd = getpass.getpass()

if svc_login(user, passwd):    # 处理密码的函数 处理过程自定义(解码 匹配)
   print('YES!')
else:
   print('NO!')

5.获取终端的大小

需要知道当前终端的大小以便正确的格式化输出。

>>> import os
>>> sz = os.get_terminal_size()
>>> sz
#结果显示 终端的行数 和 列数
os.terminal_size(columns=80, lines=24)

>>> sz.columns
80
>>> sz.lines
24
>>>

6.执行外部命令并获取它的输出

想执行一个外部命令并以Python字符串的形式获取执行结果

import subprocess
out_bytes = subprocess.check_output(['netstat','-a']) #将执行结果以一个字节字符串的形式返回

out_text = out_bytes.decode('utf-8') #将执行结果以文本形式返回


out_bytes = subprocess.check_output(['cmd','arg1','arg2'],stderr=subprocess.STDOUT) #同时收集标准输出和错误输出,使用 stderr 参数

7.复制或者移动文件和目录


import shutil

# 复制源文件到目标路径
shutil.copy(src, dst)

# 复制过程中保留源文件或目录的路径即不跟随源文件中的符号链接
shutil.copy2(src, dst)

# 递归复制 将指定目录下的所有文件与子目录一并处理
shutil.copytree(src, dst)

# 移动
shutil.move(src, dst)

注意:

使用 copytree() 复制文件夹 。在复制过程中,函数可能会碰到损坏的符号链接,因为权限无法访问文件的问题等等

如果提供关键字参数 ignore_dangling_symlinks=True , 这时候 copytree() 会忽略掉无效符号链接

8.创建和解压归档文件

>>> import shutil
>>> shutil.unpack_archive('Python-3.3.0.tgz') #解压

>>> shutil.make_archive('py33','zip','Python-3.3.0') #压缩文件名py33  zip格式  需要被压缩文件夹python-3.3.0
'/Users/beazley/Downloads/py33.zip'
>>>

9.通过文件名查找文件

查找特定的文件名并返回所有符合条件的文件全路径

#!/usr/bin/env python3.3
import os

def findfile(start, name):指定初始查找目录 以及查找文件名字作为位置参数
    for relpath, dirs, files in os.walk(start):
        if name in files:
            full_path = os.path.join(start, relpath, name) #根据查找到信息进行目录拼接
            print(os.path.normpath(os.path.abspath(full_path)))

if __name__ == '__main__':
    findfile(sys.argv[1], sys.argv[2])

os.walk() 方法为我们遍历目录树, 每次进入一个目录,它会返回一个三元组,包含:

  • 相对于查找目录的相对路径
  • 该目录下的目录名列表
  • 对应目录下面的文件名列表

10.读取配置文件

有如下配置文件:

; config.ini
; Sample configuration file

[installation]
library=%(prefix)s/lib
include=%(prefix)s/include
bin=%(prefix)s/bin
prefix=/usr/local

# Setting related to debug configuration
[debug]
log_errors=true
show_warnings=False

[server]
port: 8080
nworkers: 32
pid-file=/tmp/spam.pid
root=/www/root
signature:
    =================================
    Brought to you by the Python Cookbook
    =================================

configparser 模块能被用来读取配置文件

>>> from configparser import ConfigParser
>>> cfg = ConfigParser()
>>> cfg.read('config.ini')
['config.ini']
>>> cfg.sections() #获取各章节头部
['installation', 'debug', 'server'] 
>>> cfg.get('installation','library') #获取对应章节头部字段下对应字段值
'/usr/local/lib'
>>> cfg.getboolean('debug','log_errors')

True
>>> cfg.getint('server','port') #getint获取int类型数值
8080
>>> cfg.getint('server','nworkers')
32
>>> print(cfg.get('server','signature'))

\\=================================
Brought to you by the Python Cookbook
\\=================================
>>>

注意:配置文件中的名字是不区分大小写的 因此在读取配置文件信息时 对应字段可以不用区分大小写

11.给简单脚本增加日志功能

import logging

def main():
    # Configure the logging system
    logging.basicConfig(
        filename='app.log',
        level=logging.ERROR #设置错误等级的最低等级 限制错误输出
    )

    # Variables (to make the calls that follow work)
    hostname = 'www.python.org'
    item = 'spam'
    filename = 'data.csv'
    mode = 'r'

    # Example logging calls (insert into your program)
    logging.critical('Host %s unknown', hostname)#以降序方式表示不同错误的严重级别
    logging.error("Couldn't find %r", item)
    logging.warning('Feature is deprecated')
    logging.info('Opening file %r, mode=%r', filename, mode)
    logging.debug('Got here')

if __name__ == '__main__':
    main()

#结果---------------
CRITICAL:root:Host www.python.org unknown
ERROR:root:Could not find 'spam'

改变输出等级

import logging

def main():
    # Configure the logging system
    logging.basicConfig(
     filename='app.log',
     level=logging.WARNING,#更改错误等级的最低等级为WARNING 限制错误输出
     format='%(levelname)s:%(asctime)s:%(message)s') 
    )

    # Variables (to make the calls that follow work)
    hostname = 'www.python.org'
    item = 'spam'
    filename = 'data.csv'
    mode = 'r'

    # Example logging calls (insert into your program)
    logging.critical('Host %s unknown', hostname)#以降序方式表示不同错误的严重级别
    logging.error("Couldn't find %r", item)
    logging.warning('Feature is deprecated')
    logging.info('Opening file %r, mode=%r', filename, mode)
    logging.debug('Got here')

if __name__ == '__main__':
    main()

#结果---------------
CRITICAL:2012-11-20 12:27:13,595:Host www.python.org unknown
ERROR:2012-11-20 12:27:13,595:Could not find 'spam'
WARNING:2012-11-20 12:27:13,595:Feature is deprecated

12.给函数库增加日志功能

想给某个函数库增加日志功能,但是又不能影响到那些不使用日志功能的程序


import logging
log = logging.getLogger(__name__)
log.addHandler(logging.NullHandler())

# Example function (for testing)
def func():
    log.critical('A Critical Error!')
    log.debug('A debug message')

>>> import logging
>>> logging.basicConfig() #配置日志系统
>>> somelib.func() #调用打印日志信息
CRITICAL:somelib:A Critical Error!
>>>

13.实现一个计时器

记录程序执行多个任务所花费的时间

import time

class Timer:
    def __init__(self, func=time.perf_counter):
        self.elapsed = 0.0
        self._func = func
        self._start = None

    def start(self):
        if self._start is not None:
            raise RuntimeError('Already started')
        self._start = self._func()

    def stop(self):
        if self._start is None:
            raise RuntimeError('Not started')
        end = self._func()
        self.elapsed += end - self._start  #在 elapsed 属性中记录整个消耗时间
        self._start = None

    def reset(self):
        self.elapsed = 0.0

    @property
    def running(self):
        return self._start is not None

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, *args):
        self.stop()
def countdown(n):
    while n > 0:
        n -= 1

# Use 1: Explicit start/stop
t = Timer() #构建定时器对象
t.start() #定时器启动
countdown(1000000)
t.stop()  #定时器关闭
print(t.elapsed)

# Use 2: As a context manager
with t: #开启会话 会自动调用enter和exit接口 实现 进行会话后调用开启函数 执行完成离开会话后调用关闭函数
    countdown(1000000)

print(t.elapsed)

with Timer() as t2:
    countdown(1000000)
print(t2.elapsed)

注意:述代码中由 Timer 类记录的时间是钟表时间,并包含了所有休眠时间。 如果你只想计算该进程所花费的CPU时间,应该使用 time.process_time() 来代替

t = Timer(time.process_time)
with t:
    countdown(1000000)
print(t.elapsed)

14.限制内存和CPU的使用量

对在Unix系统上面运行的程序设置内存或CPU的使用限制

import signal
import resource
import os

def time_exceeded(signo, frame):
    print("Time's up!")
    raise SystemExit(1)

def set_max_runtime(seconds): #限制运行时间
    soft, hard = resource.getrlimit(resource.RLIMIT_CPU)
    resource.setrlimit(resource.RLIMIT_CPU, (seconds, hard))
    signal.signal(signal.SIGXCPU, time_exceeded)

def limit_memory(maxsize): #限制内存 设置了内存限制后,程序运行到没有多余内存时会抛出 MemoryError 异常
    soft, hard = resource.getrlimit(resource.RLIMIT_AS)
    resource.setrlimit(resource.RLIMIT_AS, (maxsize, hard))

if __name__ == '__main__':
    set_max_runtime(15)
    while True:
        pass

15.启动一个WEB浏览器

通过脚本启动浏览器并打开指定的URL网页

>>> import webbrowser
>>> webbrowser.open('http://www.python.org') #启动一个浏览器,并且与平台无关
True
>>>

>>> # Open the page in a new browser window
>>> webbrowser.open_new('http://www.python.org') #打开一个新的浏览器窗口
True
>>>

>>> # Open the page in a new browser tab
>>> webbrowser.open_new_tab('http://www.python.org') #打开一个新标签页
>>>

>>> c = webbrowser.get('firefox') #通过指定浏览器平台 打开网页
>>> c.open('http://www.python.org')
True
>>> c.open_new_tab('http://docs.python.org')
True
>>>

以上是关于Python(十三)脚本编程与系统管理的主要内容,如果未能解决你的问题,请参考以下文章

Python(十三)脚本编程与系统管理

DPDK编程指南(翻译)( 二十三 中)

文献综述十三:超市商品管理系统的设计与实现

嵌入式Linux从入门到精通之第十三节:系统编程之互斥与同步

草根学Python(十三)线程和进程

草根学Python(十三)线程和进程