18.函数编程的练习

 

 

下面是一些函数编程的练习:

 

 

 mtping.py内容:

#!/usr/bin/env python
#coding:utf8

import os 

def ping(ip):
    result = os.system("ping -c2 %s &>/dev/null" %ip)
    if result:
        print "%s:down " % ip 
    else:
        print "%s:up" % ip
    
    
if __name__ == "__main__":
    for i in range(1,255):
        ipaddr = "172.40.2.%s" % i
        ping(ipaddr)

 

 mtping2.py内容:

import os 
import threading

def ping(ip):
    result = os.system("ping -c2 %s &>/dev/null" %ip)
    if result:
        print "%s:down " % ip 
    else:
        print "%s:up" % ip
    
    
if __name__ == "__main__":
    for i in range(1,255):
        ipaddr = "172.40.2.%s" % i
        t = threading.Thread(target=ping,args=[ipaddr])
        ping(ipaddr)
    

 

 生成随机密码

方法1:用raw_input方式不推荐

#!/usr/bin/env python
#coding:utf8

import random
import string
all_chs = string.letters + string.digits

def gen_pass(): 
    pwd = ''
    
    num = int(raw_input("number: "))
    
    
    for i in range(num):
        ch = random.choice(all_chs)
        pwd += ch
        
    print pwd
    
if __name__ == "__main__":
    gen_pass()

 

 方法2:

#!/usr/bin/env python
#coding:utf8

import random
import string
all_chs = string.letters + string.digits
def gen_pass(num=8):
    pwd = ''

    for i in range(num):
        ch = random.choice(all_chs)
        pwd += ch
        
    return pwd
    
if __name__ == "__main__":
    print gen_pass(5)
    print gen_pass()
    print gen_pass(10)

 

 

 编写 makeTextFile.py 脚本,主要要求如下:
1、编写一个程序,要求用户输入文件名

2、如果文件已存在,要求用户重新输入

3、提示用户输入数据,每行数据先写到列表中

4、将列表数据写入到用户输入的文件名中

 

方案
首先通过循环不断要求用户输入文件名,如果文件已经存在,要求用户输入新文件名。
然后提示用户输入文件内容,将用户的输入信息保存到一个列表中。最后将列表中的每个字符串尾部都加上行结束标志,并写入文件。
为了程序的跨平台性,可以调用 os.linesep 确定行结束标志。在 Linux 系统中,行结束标志为’\n’;在 Windows 系统中,行结束标志为’\r\n’

 

#!/usr/bin/env python
#coding:utf8

import os

def get_fname():
    while True:
        fname = raw_input("filename: ")
        if not os.path.exists(fname):
            break
        print "%s already exists.Try again" % fname
    return fname


def get_contents():

    contents = []
    while True:
        data = raw_input("(Enter to quit)>")
        if not data:
            break
        contents.append(data + '\n')
    return contents

def wfile(fname,contents):
    fobj = open(fname,'w')
    fobj.writelines(contents)
    fobj.close()


if __name__ == "__main__":
    filename = get_fname()
    lines = get_contents()
    wfile(filename,lines)

 

 在shell中,可以用这种方式实现远程改密码:

chpwd.sh内容:远程改密码

#!/bin/bash
if [ -z "$3" ];then
    echo "Usage: $0 ipfile oldpass newpass"
    exit 1
fi 

ipfile=$1 
oldpass=$2
newpass=$3

if [! -f $ipfile ];then
    echo "$ipfile does not exists"
    exit 2
    
fi 
for ip in $(cat $ipfile)
do
    expect <<EOF
        spawn ssh root@$ip "echo $newpass | passwd --stdin root"
        expect "(yes/no)?"{
            send "yes\r"
            expect "password:"
                send "$oldpass\r"
        } "password:" { send "$oldpass\r"}
    expect eof
EOF 
done

执行结果:

chmod +x chpwd.sh
./chpwd.sh
./chpwd.sh ipaddr.txt redhat redhat 

 

而在Python中,可以通过paramiko模块实现

chpwd.py内容:远程改密码

方法1:

#!/usr/bin/env python

import sys
import paramiko

def remote_comm(host,pwd,comm):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(host,username='root',password=pwd)
    stdin,stdout,stderr = ssh.exec_command(comm)
    print stdout.read(),
    print stderr.read(),



if __name__ == "__main__":
    if len(sys.argv) !=4:
        print "Usage: %s ipfile oldpass newpass" % sys.argv[0]

    else:
        ipfile = sys.argv[1]
        oldpass = sys.argv[2]
        newpass = sys.argv[3]

        ch_pwd = "echo %s | passwd --stdin root" % newpass
        fobj = open(ipfile)
        for line in fobj:
            ip = line.strip()
            remote_comm(ip,oldpass,ch_pwd)

执行结果:

[root@host-192-168-3-6 test]# chmod +x chpwd.py 
[root@host-192-168-3-6 test]# cat ipaddr.txt 
192.168.3.6
root@host-192-168-3-6 test]# ./chpwd.py ipaddr.txt abc123 abc123
Changing password for user root.
passwd: all authentication tokens updated successfully.

 

方法2:mtchpwd.py内容:远程改密码

#!/usr/bin/env python

import sys
import paramiko
import threading

def remote_comm(host,pwd,comm):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(host,username='root',password=pwd)
    stdin,stdout,stderr = ssh.exec_command(comm)
    print stdout.read(),
    print stderr.read(),



if __name__ == "__main__":
    if len(sys.argv) !=4:
        print "Usage: %s ipfile oldpass newpass" % sys.argv[0]

    else:
        ipfile = sys.argv[1]
        oldpass = sys.argv[2]
        newpass = sys.argv[3]

        ch_pwd = "echo %s | passwd --stdin root" % newpass
        fobj = open(ipfile)
        for line in fobj:
            ip = line.strip()
            t = threading.Thread(target=remote_comm,args=(ip,oldpass,ch_pwd))
            t.start()

 

 

编写 idcheck.py 脚本,主要要求如下:
1、程序接受用户输入
2、用户输入的数据要求大于两个字符
3、判断用户输入的标识符是否合法

方案
合法标识符的要求是:
1、第一个字符必须是字母或下划线
2、其余字符可以是字母、下划线或数字
3、大小写敏感

 

方法1:

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

import string
alphas = string.letters + '_'
nums = string.digits

print 'Welcome to my id check prog'
inp = raw_input('Enter a id: ')

if inp[0] not in alphas: #判断首字符是否合法
print 'bad identifier'
else:
for otherchar in inp[1:]: #判断其余字符是否合法

if otherchar not in (alphas + nums):
print 'bad other char'
break


else: #注意此处的 else 是 for 结构的一部分
print '%s is good' % inp

 

 执行结果:

Welcome to my id check prog
Enter a id: 123
bad identifier

 

方法2:

#!/usr/bin/env python
#coding:utf8

import string

first_chs = string.letters + '_'
other_chs = first_chs + string.digits

def check_id(myid):
    if myid[0] not in first_chs:
        print "1st char invalid."
        return
    
    for ind,ch in enumerate(myid[1:]):
        if ch not in other_chs:
            print "char in position:%s invalid" % (ind +2)
            break
    else:    
        print "%s is valid" % myid
    
    
if __name__ == "__main__":
    myid = raw_input("id to check: ")
    if myid:
        check_id(myid)
    else:
        print "You must input an identifier."

 

执行结果:

id to check: 124
1st char invalid.

 

编写 formatoutput.py 脚本,主要要求如下:

1、提示用户输入(多行)数据
2、假定屏幕的宽度为 50,用户输入的多行数据如下显示(文本内容居中) :

 

方案
为了实现文本内容居中,可以在字符串的两端打印相关的“+”号和空格即可。
字符串每一侧打印的空格数可以使用屏幕宽度减去字符串长度再除以 2 得到。 这里要注
意的是如果字符串的长度为奇数,那么将会少打印一个空格。可以在做除法时,把余数保留
下来,字符串左侧打印的空格数为除法的商值,右侧打印的空格数为商值加余数

 

方法1:

#!/usr/bin/env python
#coding:utf8


width = 48
data = [] #定义列表用于存储用户数据

while True:
    entry = raw_input('enter data(. to quit)> ')
    if entry == '.':
        break
    data.append(entry)


print '+' + '*' * width + '+'
for item in data:
    length, extra = divmod((width - len(item)) , 2)
    print '+' + ' ' * length + item + ' ' * (length + extra) + '+'

print '+' + '*' * width + '+'

执行结果:

enter data(. to quit)> hello
enter data(. to quit)> great work
enter data(. to quit)> .
+************************************************+
+                     hello                      +
+                   great work                   +
+************************************************+

 

 方法2:

#!/usr/bin/env python
#coding:utf8

def get_contents():

    contents = []
    while True:
        data = raw_input("(Enter to quit)>")
        if not data:
            break
        contents.append(data)
    return contents

if __name__ == '__main__':
    width = 48
    lines = get_contents()
    print "+%s+" %('*' * width)
    for line in lines:
        sp_wid,extra = divmod((width-len(line)),2)
        print "+%s%s%s+" % (' ' * sp_wid,line,' ' * (sp_wid+extra))
    print "+%s+" % ('*' * width)        

 

(Enter to quit)>hello
(Enter to quit)>great work
(Enter to quit)>
+************************************************+
+                     hello                      +
+                   great work                   +
+************************************************+

 

创建用户
编写 adduser.py 脚本,主要要求如下:
1、编写一个程序,实现创建用户的功能
2、提示用户输入用户名
3、随机生成 8 位密码
4、创建用户并设置密码
5、发邮件通知用户相关信息

创建用户完成后,需要发送邮件通知。因为每封邮件的内容格式一样,只是用户名和
密码有变化,所以使用字符串模板可以很好的实现该功能。

 

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

import string
import random
import os

allChs = string.letters + string.digits

#以下三行为字符串模板的字符串
patt = """your account is created.
username: $user
password: $pwd"""


def genPwd(num = 8): #生成随机密码函数
    pwd = ''
    for i in range(num):
        pwd += random.choice(allChs)
    return pwd

if __name__ == '__main__':
    username = raw_input('username: ')
    password = genPwd()
    os.system('useradd %s' % username)
    os.system('echo %s | passwd --stdin %s' % (password, username))
    t = string.Template(patt)
    os.system("echo '%s' | mail -s 'create user' root" % t.substitute(user = username,pwd = password))

 

执行结果:

[root@host-192-168-3-6 tarena]#python adduser.py 
username: alice
Changing password for user alice.
passwd: all authentication tokens updated successfully.
[root@host-192-168-3-6 tarena]# mail
Heirloom Mail version 12.5 7/5/10.  Type ? for help.
"/var/spool/mail/root": 2 messages 1 new
    1 root                  Fri Jun 16 10:31  21/691   "user info"
>N  2 root                  Sat Jul 29 10:03  20/710   "create user"
& 
Message  2:
From root@host-192-168-3-6.localdomain  Sat Jul 29 10:03:55 2017
Return-Path: <root@host-192-168-3-6.localdomain>
X-Original-To: root
Delivered-To: root@host-192-168-3-6.localdomain
Date: Sat, 29 Jul 2017 10:03:52 +0800
To: root@host-192-168-3-6.localdomain
Subject: create user
User-Agent: Heirloom mailx 12.5 7/5/10
Content-Type: text/plain; charset=us-ascii
From: root@host-192-168-3-6.localdomain (root)
Status: R

your account is created.
username: alice
password: IHgkunGZ

& 

 

 

方法2:

 

#!/usr/bin/env python
#coding:utf8

import sys
import randpass2
import os
import string

contents = """username: ${username}
password: ${password}
"""
t = string.Template(contents)

def adduser(user,passwd,email):
    data = t.substitute(username=user,password=passwd)
    os.system("useradd %s" % user)
    os.system("echo %s| passwd --stdin %s" % (passwd,user))
    os.system("echo -e '%s' | mail -s 'user info' %s" % (data,email))

if __name__ == '__main__':
    username = sys.argv[1]
    pwd = randpass2.gen_pass()
    adduser(username,pwd,'root@localhost')

 

执行结果:

[root@host-192-168-3-6 test]# chmod +x adduser.py 
[root@host-192-168-3-6 test]# ./adduser.py jerry
Changing password for user jerry.
passwd: all authentication tokens updated successfully.

 

注意:randpass2模块是自定义的模块,其中的内容为:

#coding:utf8

import random
import string
all_chs = string.letters + string.digits
def gen_pass(num=8):
    pwd = ''

    for i in range(num):
        ch = random.choice(all_chs)
        pwd += ch
        
    return pwd
    
if __name__ == "__main__":
    print gen_pass(5)
    print gen_pass()
    print gen_pass(10)

 

 

用列表构建栈结构

栈是一个后进先出的结构,编写 stack.py 脚本,主要要求如下:
1、编写一个程序,用列表实现栈结构
2、需要支持压栈、出栈、查询功能

方案
列表是可变的数据类型,用它模拟栈结构非常合适。在列表的内建方法中,append()

用于向列表追加元素,pop()用于从列表中弹出元素。pop()可以接受下标作为参数,用于
弹出指定位置的元素,默认弹出的是最后一个元素。

 

 

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

stack = []

def pushit(): #定义压栈函数
    item = raw_input('input item: ').strip()
    stack.append(item)

def popit(): #定义出栈函数
    if len(stack) == 0:
        print '\033[32;1mEmpty stack.\033[0m'

    else:
        print '\033[32;1mpoped [' + stack.pop() + ']\033[0m'

def viewstack(): #定义查看函数
    print '\033[32;1m', stack, '\033[0m'

CMDs = {'p':pushit, 'o':popit, 'v':viewstack} #将函数存储在字典中

def showmenu():
    prompt = """(P)ush
p(O)p
(V)iew
(Q)uit
Please input your choice: """
    while True:
        try:
            choice = raw_input(prompt).strip().lower()[0]
        except (KeyboardInterrupt, EOFError):
            choice = 'q'

        if choice not in 'povq':
            print 'Invalid input. Try again.'
            continue

        if choice == 'q':
            break

        else:
            CMDs[choice]() #执行字典中对应的函数

if __name__ == '__main__':
    showmenu()

执行结果:

(P)ush
p(O)p
(V)iew
(Q)uit
Please input your choice: p
input item: hello
(P)ush
p(O)p
(V)iew
(Q)uit
Please input your choice: p
input item: world
(P)ush
p(O)p
(V)iew
(Q)uit
Please input your choice: v
 ['hello', 'world'] 
(P)ush
p(O)p
(V)iew
(Q)uit
Please input your choice: o
poped [world]
(P)ush
p(O)p
(V)iew
(Q)uit
Please input your choice: q

 

 

方法2:

#!/usr/bin/env python
#coding:utf8

stack = []

def pushit():
    item = raw_input("item: ")
    stack.append(item)
    
def popit():
    if stack:
        print "poped item:",stack.pop()
    else:
        "Empty stack"

def viewit():
    print stack
    
def show_menu():
    CMDs = {'0':pushit,'1':popit,'2':viewit}
    prompt ='''(0) push it
(1) pop it 
(2) view it
(3) quit
Please input your choice(0/1/2/3):'''
    
    while True:
        choice = raw_input(prompt).strip()[0]
        if choice not in '0123':
            print "Invalid input,Try again."
            continue
        
        if choice == '3':
            break
        CMDs[choice]()
        
if __name__ == "__main__":
    show_menu()

 

 

rmsps.py内容:

#!/usr/bin/env python
#coding:utf8

#whitespace = '\t\n\v\r\f'
from string import whitespace


def lrmsps(astr):
    if not astr:
        return astr
    
    for i in range(len(astr)):
        if astr[i] not in whitespace:
            break
    else:
        return ''
        
    return astr[i:]

def rrmsps(astr):
    if not astr:
        return astr
    
    for i in range(-1,-(len(astr)+1),-1):
        if astr[i] not in whitespace:
            break
    else:
        return ''
    
    return astr[:(i+1)]


def rmsps(astr):
    return rrmsps(lrmsps(astr))


if __name__ == '__main__':
    hi = " hello \t"
    print "|%s|" % lrmsps(hi)
    print "|%s|" % rrmsps(hi)
    print "|%s|" % rmsps(hi)

 

执行结果:

|hello     |
| hello|
|hello|

 

rmsps2.py内容:

#!/usr/bin/env python
#coding:utf8

from string import whitespace

def lrmsps(astr):
    str_list = list(astr)
    for i in range(len(str_list)):
        if str_list[0] not in whitespace:
            break
        str_list.pop(0)
        
    return ''.join(str_list)


def rrmsps(astr):
    str_list = list(astr)
    for i in range(len(str_list)):
        if str_list[-1] not in whitespace:
            break
        str_list.pop()
        
    return ''.join(str_list)

def rmsps(astr):
    return lrmsps(rrmsps(astr))

if __name__ == '__main__':
    hi = ' hello \t'
    print "|%s|" % lrmsps(hi)
    print "|%s|" % rrmsps(hi)
    print "|%s|" % rmsps(hi)

 

执行结果:

 

|hello     |
| hello|
|hello|

 

convertip.py内容:

#!/usr/bin/env python
#coding:utf8

def int2ip(num):
    ip_list = []
    for i in range(4):
        num,mod = divmod(num, 256)
        ip_list.insert(0,str(mod))
        
    return '.'.join(ip_list)
        
def ip2int(ip):
    ip_list = ip.split('.')
    num = 0
    for i in range(len(ip_list)):
        num += int(ip_list[i]) * 256 ** (3-i)
    return num
    
if __name__ == "__main__":
    print int2ip(3232235786)
    print ip2int('192.168.1.10')

 

执行结果:

192.168.1.10
3232235786

 

 

用户登陆信息系统

编写 ulogin.py 脚本,主要要求如下:
1、使用字典模拟一个用户登陆信息系统
2、支持新用户注册,新用户名和密码注册到字典中
3、支持老用户登陆,用户名和密码正确提示登陆成功
4、主程序通过循环询问进行何种操作,根据用户的选择,执行注册或是登陆操作

方案
因为没有永久存储,不能把用户名和密码永久保存下来,所以在编写程序时,应该使用循环结构保证程序不要退出。
在字典里,将 key 设置为用户名,value 设置为密码

方法1:

 

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


import getpass

db = {} #定义用于用户信息的字典

def newUser(): #定义新用户注册函数
    username = raw_input('username: ')
    if username in db:
        print "\033[32;1m%s alread exists!\033[0m" % username

    else:
        password = getpass.getpass()
        db[username] = password


def oldUser(): #定义老用户登陆函数
    username = raw_input('username: ')
    password = getpass.getpass() #不在屏幕上显示密码

    if username in db:
        if db[username] == password:
            print '\033[32;1mlogin successful!\033[0m'
        else:
            print '\033[32;1mlogin incorrect.\033[0m'
    else:
        print '\033[32;1mlogin incorrect.\033[0m'


CMDs = {'n': newUser, 'o': oldUser} #将函数存放在字典中

def showMenu():
    prompt = """(N)ew user
(O)ld user
(Q)uit
please input your choice: """
    while True:
        try:
            choice = raw_input(prompt).strip()[0].lower()

        except (KeyboardInterrupt, EOFError):
            choice = 'q'

        if choice not in 'noq':
            print '\033[31;1mInvalid option.Try again(n / o / q).\033[0m'
            continue

        if choice == 'q':
            break

        CMDs[choice]()
        
if __name__ == '__main__':
    showMenu()

 

执行结果:

 

[root@host-192-168-3-6 tarena]# python ulogin.py 
(N)ew user
(O)ld user
(Q)uit
please input your choice: n
username: tom
Password: 
(N)ew user
(O)ld user
(Q)uit
please input your choice: n
username: tom
tom alread exists!
(N)ew user
(O)ld user
(Q)uit
please input your choice: o
username: tom
Password: 
login successful!
(N)ew user
(O)ld user
(Q)uit
please input your choice: q

 

方法2:

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

import getpass
db = {}


def new_user():
        user = raw_input("username: ")
        pwd = raw_input("password: ")
#   if user not in db:
#       db[user] ==pwd
        if pwd == db.setdefault(user,pwd):
                print "Register successful"
        else:
                print "%s already exists." % user

def olduser():
        user = raw_input("username: ")
        pwd = getpass.getpass("password: ")
        #if user not in db or db[user] != pwd:
        if db.get(user) != pwd:
                print "Login incorrect."
        else:
                print "Login successful."

def show_menu():
        CMDs = {'0':new_user,'1':olduser}

        prompt = """(0)new user
(1)old user
(2)quit
Please input your choice(0/1/2):"""

        while True:
                choice = raw_input(prompt).strip()[0]
                if choice not in '012':
                        print "Invalid choice.Try again.."
                        continue

                if choice == '2':
                        break
                CMDs[choice]()

if __name__ == "__main__":
    show_menu()

 

执行结果:

[root@host-192-168-3-6 tarena]# python ulogin2.py 
(0)new user
(1)old user
(2)quit
Please input your choice(0/1/2):0
username: tom
password: 123456
Register successful
(0)new user
(1)old user
(2)quit
Please input your choice(0/1/2):1
username: tom
password: 
Login successful.
(0)new user
(1)old user
(2)quit
Please input your choice(0/1/2):q
Invalid choice.Try again..
(0)new user
(1)old user
(2)quit
Please input your choice(0/1/2):2

 

 

模拟 case 句法

编写 case.py 脚本,主要要求如下:

提示用户输入执行何种操作(create/delete/modify)
1、如果输入为 create,则打印 create user
2、如果输入为 delete,则打印 delete user
3、如果输入为 modify,则打印 modify user
4、否则打印 input error

 

 

方案:

python 中没有 case/switch 语句,但是可以使用其他的结构进行模拟。利用列表、
元组、字典等数据结构都可以实现 case 语句的功能。
本例使用字典的方法进行 case 功能模拟

方法1:

#!/usr/bin/env python
db = {'create' : 'create', 'delete' : 'delete', 'modify' : 'modify'}
prompt = """create
delete
modify
please input your choice: """
choice = raw_input(prompt)

if db.get(choice):
    print db[choice], 'user'
else:
    print 'input error'

 

 

执行结果:

create
delete
modify
please input your choice: create
create user

方法2:

#!/usr/bin/env python
#coding:utf8

import sys 

ops = ['create','modify','delete']

op = sys.argv[1]

if op in ops:
    print "%s user" % op
    
else:
    print "Usage: create | modify | delete user"

 

执行结果:

[root@host-192-168-3-6 tarena]# chmod +x case.py 
[root@host-192-168-3-6 tarena]# ./case.py tom
Usage: create | modify | delete user

 

 

 实现系统中unix2dos命令的功能

方法1:

#!/usr/bin/env python
#coding:utf8

import os 
import sys

def unix2dos(fname):
    dfname = os.path.splitext(fname)[0] + '.win'
    src_fobj = open(fname)
    dst_fobj = open(dfname,'w')
    for line in src_fobj:
        dst_fobj.write(line.rstrip('\n\r') + '\r\n')
        
    src_fobj.close()
    dst_fobj.close()
    
if __name__ == '__main__':
    if len(sys.argv) != 2:
        print "Usage: %s filename " % sys.argv[0]
        sys.exit(1)
    
    filename = sys.argv[1]
    if not os.path.isfile(filename):
        print "No such file: %s" % filename 
        sys.exit(2)
    unix2dos(filename)

 

执行结果:

{root@host-192-168-3-6 test]# python u2d.py 
Usage: u2d.py filename 
[root@host-192-168-3-6 test]# vim abc.txt
abcdeef
hello
[root@host-192-168-3-6 test]# python u2d.py abc.txt 
[root@host-192-168-3-6 test]# ls
abc.txt
abc.win

 

方法2:

#!/usr/bin/env python
#coding:utf8

import sys

def unix2dos(fname,sep='\r\n'):
    dst_name = fname + '.txt'
    with open(fname) as src_fobj:
        with open(dst_name,'w') as dst_fobj:
            for line in src_fobj:
                dst_fobj.write("%s%s" % (line.rstrip('\r\n'),sep))
                

if __name__ == "__main__":
    unix2dos(sys.argv[1])

 

执行结果:

[root@host-192-168-3-6 ~]# python u2d.py myadd.py
root@host-192-168-3-6 ~]# ls
myadd.py     
myadd.py.txt 
u2d.py

>>> with open('myadd.py') as f1:
...    f1.readline()
... 
'#!/usr/bin/env python\n'

>>> with open('myadd.py.txt') as f1:
...     f1.readline()
... 
'#!/usr/bin/env python\r\n'
>>> 

 实现系统中dos2unix命令的功能

方法1:

#!/usr/bin/env python
#coding:utf8

import os 
import sys

def dos2unix(fname):
    dfname = os.path.splitext(fname)[0] + '.unix'
    src_fobj = open(fname)
    dst_fobj = open(dfname,'w')
    for line in src_fobj:
        dst_fobj.write(line.rstrip('\n\r') + '\n')
        
    src_fobj.close()
    dst_fobj.close()
    
if __name__ == '__main__':
    if len(sys.argv) != 2:
        print "Usage: %s filename " % sys.argv[0]
        sys.exit(1)
    
    filename = sys.argv[1]
    if not os.path.isfile(filename):
        print "No such file: %s" % filename 
        sys.exit(2)
    dos2unix(filename)

 

方法2:

#!/usr/bin/env python
#coding:utf8

import sys 
import u2d
from functools import partial


dos2unix = partial(u2d.unix2dos,sep = '\n')


if __name__ == '__main__':
    dos2unix(sys.argv[1])

 

执行结果:

[root@host-192-168-3-6 ~]# python d2u.py myadd.py.txt 
[root@host-192-168-3-6 ~]# ls
myadd.py    
myadd.py.txt
d2u.py
myadd.py.txt.txt 
u2d.py

>>> with open('myadd.py.txt.txt') as f3:
...     f3.readline()
... 
'#!/usr/bin/env python\n'
>>> 

 

 

 编写 mathgame.py 脚本,主要要求如下:

随机生成两个 100 以内的数字
1、随机选择加法或是减法
2、总是使用大的数字减去小的数字
3、如果用户答错三次,程序给出正确答案

方案
程序中要求不断的出题,为了减少代码的冗余以及实现代码的重用,可以把代码放入函数中。
随机取得数字可以使用 random.choice(),取出来的数字放到列表中,为了总是使用
大数减去小数,需要再给列表排序
 

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

from operator import add, sub
from random import randint, choice

ops = {'+': add, '-': sub} #将 add 和 sub 函数放到字典中
MAXTRIES = 2

def doprob():
    op = choice('+-')
    nums = [randint(1, 100) for i in range(2)] #使用列表解析,生成数字列表
    nums.sort(reverse = True) #将列表按降序方式排序
    ans = ops[op](*nums) #此处*nums 用于将列表解开,得到数字
    pr = '%d %s %d = ' % (nums[0], op, nums[1])

    oops = 0 #定义错误计数器
    while True:
        try:
            if int(raw_input(pr)) == ans:
                print 'correct'
                break

            if oops == MAXTRIES:
                print 'answer\n%s%d' % (pr, ans)
                break
            else:
                print 'incorrect... try again.'
                oops += 1
        except (KeyboardInterrupt, EOFError, ValueError):
            print 'invalid input... try again.'


def main():
    while True:
        doprob()
        try:
            opt = raw_input('Again?[y]?').lower()
            if opt and opt[0] == 'n':
                break
        except (KeyboardInterrupt, EOFError):
            break

if __name__ == '__main__':
    main()

 

 

方法1:

 

执行结果:

[root@host-192-168-3-6 tarena]# python mathgame.py 
36 - 29 = 7
correct
Again?[y]?n

 

方法2:

#!/usr/bin/env python
#coding:utf8


import random

def add(x,y):
    return x + y 

def sub(x,y):
    return  x - y 


def probe():
    CMDs = {'+':add,'-':sub}
    nums = [random.randint(1,50) for i in range(2)]
    nums.sort(reverse=True)
    op = random.choice('+-')
    answer = CMDs[op](*nums)
    prompt = "%s%s%s = " % (nums[0],op,nums[1])
    tries = 0
    
    while tries <3:
        result = int(raw_input(prompt))
        if answer == result:
            print "Very Good!!!"
            break
        
        print "Wrong answer..."
        tries += 1
        
    else:
        print "\033[31;1m%s%s\033[0m" % (prompt,answer)
         
    
    
if __name__ == "__main__":
    while True:
        probe()
        yn = raw_input("Continue(y/n)? ").strip()[0]
        if yn in 'nN':
            break

 

执行结果:

39-25 = 14
Very Good!!!
Continue(y/n)? n

 

方法3:

#!/usr/bin/env python
#coding:utf8


import random

def add(x,y):
    return x + y 

def sub(x,y):
    return  x - y 


def probe():
    CMDs = {'+':add,'-':sub}
    nums = [random.randint(1,50) for i in range(2)]
    nums.sort(reverse=True)
    op = random.choice('+-')
    answer = CMDs[op](*nums)
    prompt = "%s%s%s = " % (nums[0],op,nums[1])
    tries = 0
    
    while tries <3:
        try:
            result = int(raw_input(prompt))
        except:
            continue
        
        if answer == result:
            print "Very Good!!!"
            break
        
        print "Wrong answer..."
        tries += 1
        
    else:
        print "\033[31;1m%s%s\033[0m" % (prompt,answer)
         
    
    
if __name__ == "__main__":
    while True:
        probe()
        
        try:    
            yn = raw_input("Continue(y/n)? ").strip()[0]
        except (KeyboardInterrupt,EOFError):
            print "\nBye Bye"
            yn = 'n'
        except IndexError:
            continue
        if yn in 'nN':
            break
        

 

执行结果:

29+13 = 42
Very Good!!!
Continue(y/n)? 123
42-29 = 13
Very Good!!!
Continue(y/n)? y
32-4 = 28
Very Good!!!
Continue(y/n)? n

 

 方法4:

#!/usr/bin/env python
#coding:utf8


import random
'''
def add(x,y):
    return x + y 

def sub(x,y):
    return  x - y 
'''

def probe():
    #CMDs = {'+':add,'-':sub}
    CMDs = {'+':lambda x,y:x+y,'-':lambda x,y:x-y}
    nums = [random.randint(1,50) for i in range(2)]
    nums.sort(reverse=True)
    op = random.choice('+-')
    answer = CMDs[op](*nums)
    prompt = "%s%s%s = " % (nums[0],op,nums[1])
    tries = 0
    
    while tries <3:
        try:
            result = int(raw_input(prompt))
        except:
            continue
        
        if answer == result:
            print "Very Good!!!"
            break
        
        print "Wrong answer..."
        tries += 1
        
    else:
        print "\033[31;1m%s%s\033[0m" % (prompt,answer)
         
    
    
if __name__ == "__main__":
    while True:
        probe()
        
        try:    
            yn = raw_input("Continue(y/n)? ").strip()[0]
        except (KeyboardInterrupt,EOFError):
            print "\nBye Bye"
            yn = 'n'
        except IndexError:
            continue
        if yn in 'nN':
            break

 

 执行结果:

38-12 = 26
Very Good!!!
Continue(y/n)? n

 

 

 

func1.py内容:

!/usr/bin/env python
#coding:utf8


def func1(x):
    return x % 2


def func2(x):
    return x **2

def func3(x,y):
    return x + y

if __name__ == "__main__":
    print filter(func1,range(1,11))
    print filter(lambda x:x %2,range(1,11))
    print map(func2,range(1,11))
    print map(lambda x: x**2,range(1,11))
    print reduce(func3,range(1,11))
    print reduce(lambda x,y: x+y,range(1,11))

 

 执行结果:

[1, 3, 5, 7, 9]
[1, 3, 5, 7, 9]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
55
55

 

递归列出目录:

[root@host-192-168-3-6 ~]# cd /home/
[root@host-192-168-3-6 home]# mkdir -p demo/{aaa/bbb,ccc}
[root@host-192-168-3-6 home]# ls
centos  demo  jerry  list  mytouch  zhuji
[root@host-192-168-3-6 home]# ls demo/
aaa  ccc
[root@host-192-168-3-6 home]# ls demo/aaa/
bbb
[root@host-192-168-3-6 home]# cp /etc/hosts demo/
[root@host-192-168-3-6 home]# touch demo/d.txt
[root@host-192-168-3-6 home]# touch demo/aaa/{a1,a2}.txt
[root@host-192-168-3-6 home]# ls demo/aaa/
a1.txt  a2.txt  bbb
[root@host-192-168-3-6 home]# touch demo/aaa/bbb/b.txt
[root@host-192-168-3-6 home]# touch demo/ccc/{c1,c2}.txt
[root@host-192-168-3-6 home]# ls -R demo/
demo/:
aaa  ccc  d.txt  hosts

demo/aaa:
a1.txt  a2.txt  bbb

demo/aaa/bbb:
b.txt

demo/ccc:
c1.txt  c2.txt
[root@host-192-168-3-6 home]# 

 

方法1:lsdir.py内容:

#!/usr/bin/env python
#coding:utf8


import os
import sys 


def lsdir(folder):
    contents = os.listdir(folder)
    print "%s:\n%s\n" % (folder,contents)
    
    for item in contents:
        full_path = os.path.join(folder,item)
        if os.path.isdir(full_path):
            lsdir(full_path)
            
if __name__ == "__main__":
    lsdir(sys.argv[1])      

 

执行结果:

[root@host-192-168-3-6 ~]# python lsdir.py /home/demo/
/home/demo/:
['aaa', 'ccc', 'hosts', 'd.txt']

/home/demo/aaa:
['bbb', 'a1.txt', 'a2.txt']

/home/demo/aaa/bbb:
['b.txt']

/home/demo/ccc:
['c2.txt', 'c1.txt']

 

方法2:

step1:

> import os
>>> import tab
>>> os.wa
os.wait(     os.wait3(    os.wait4(    os.waitpid(  os.walk(     
>>> os.walk('/home/demo')
<generator object walk at 0x7ff99ff2eeb0>
>>> list(os.walk('/home/demo'))
[('/home/demo', ['aaa', 'ccc'], ['hosts', 'd.txt']), ('/home/demo/aaa', ['bbb'], ['a1.txt', 'a2.txt']), ('/home/demo/aaa/bbb', [], ['b.txt']), ('/home/demo/ccc', [], ['c2.txt', 'c1.txt'])]
>>> a = os.walk('/home/demo')
>>> a.next()
('/home/demo', ['aaa', 'ccc'], ['hosts', 'd.txt'])
>>> a.next()
('/home/demo/aaa', ['bbb'], ['a1.txt', 'a2.txt'])
>>> a.next()
('/home/demo/aaa/bbb', [], ['b.txt'])
>>> a.next()
('/home/demo/ccc', [], ['c2.txt', 'c1.txt'])
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> 

 

step2:lsdir2.py内容

#!/usr/bin/env python
#coding:utf8

import os
import sys

def lsdir(folder):
    for path,dirs,files in os.walk(folder):
        print "%s:\n%s\n" % (path,(dirs + files))
        
if __name__ == "__main__":
    lsdir(sys.argv[1])

 

[root@host-192-168-3-6 ~]# python lsdir2.py /home/demo/
/home/demo/:
['aaa', 'ccc', 'hosts', 'd.txt']

/home/demo/aaa:
['bbb', 'a1.txt', 'a2.txt']

/home/demo/aaa/bbb:
['b.txt']

/home/demo/ccc:
['c2.txt', 'c1.txt']

 

 

模拟 cp 操作

 方案
“拷贝”一个文件,可以首先在目标位置创建一个空文件,然后再将源文件读出,写入到新文件中。
打开文件时并不消耗太多内存,但是将文件内容读到变量中,会根据读入的数据大小消耗相应的内存。

为了防止一次性读入大文件消耗太多的内存,可以采用循环的方式,多次少量读取数据

 

 注意:方法1和方法2可在文件和输入输出章节查看

 

方法3:函数方式


#!/usr/bin/env python
#coding:utf8
def cp():
    sfname = '/bin/ls'
    dfname = '/root/ls'

    src_fobj = open(sfname)
    dst_fobj = open(dfname,'w')

    while True:
        data = src_fobj.read(4096)
        if not data:
            break
        dst_fobj.write(data)

    src_fobj.close()
    dst_fobj.close()
cp()

 

方法4:

cp.py带参数:
#!/usr/bin/env python
#coding:utf8
def cp(sfname,dfname):

    src_fobj = open(sfname)
    dst_fobj = open(dfname,'w')

    while True:
        data = src_fobj.read(4096)
        if not data:
            break
        dst_fobj.write(data)

    src_fobj.close()
    dst_fobj.close()

cp('/bin/ls','/home/list')
cp('/bin/touch','/home/mytouch')

执行过程及结果:

cd /home/
[root@host-192-168-3-6 home]# ls
centos
[root@host-192-168-3-6 home]# cd centos/
[root@host-192-168-3-6 test]# python zgy.py
[root@host-192-168-3-6 test]# cd -
/home
[root@host-192-168-3-6 home]# ls
centos  list  mytouch

方法5:

#!/usr/bin/env python
#coding:utf8
import sys

def cp(sfname,dfname):

    src_fobj = open(sfname)
    dst_fobj = open(dfname,'w')

    while True:
        data = src_fobj.read(4096)
        if not data:
            break
        dst_fobj.write(data)

    src_fobj.close()
    dst_fobj.close()

cp(sys.argv[1],sys.argv[2])

执行过程及结果:

[root@host-192-168-3-6 test]# python cp.py /etc/hosts
hosts        hosts.allow  hosts.deny   
[root@host-192-168-3-6 test]# python zgy.py /etc/hosts /home/zhuji
[root@host-192-168-3-6 test]# cd /home/
[root@host-192-168-3-6 home]# cat zhuji 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
[root@host-192-168-3-6 home]# 

 

 

 

练习:利用cPickle模块

编写一个家庭理财系统。记录你的收支情况。首先,假设你有1万存款,以它作为基数,存钱、花钱都要明确的记录。

时间    存钱    花钱   余额     备注

 

分析:

1、需要两个记录文件,一个文件当成钱包文件(balance.txt),里面存放着你的最新余额,另一个是记帐本文件(record.txt)

2、为了每次测试方便,使用一个初始化文件(init_money.py),向钱包文件中写入10000

3、主程序文件(finance.py),要有四个基础函数,一个用于存钱(saveMoney),一个用于花钱(spendMoney),一个用于记帐(logger),一个用于查询(queryInfo)

4、主程序运行时,打印一个菜单,询问用户要做什么。获取用户行为后,调用相应的函数。

方法1:

step1:

root@host-192-168-3-6 ~]# cd tarena/
[root@host-192-168-3-6 tarena]# mkdir test
[root@host-192-168-3-6 tarena]# cd test
[root@host-192-168-3-6 test]# ls
initm.py

 

initm.py内容:

#!/usr/bin/env python

import cPickle as p

fd = file('balance.txt', 'w')
p.dump(10000, fd)
fd.close()

fobj = file('record.txt', 'w')
fobj.close()

执行initm.py

[root@host-192-168-3-6 test]# python initm.py 
[root@host-192-168-3-6 test]# ls
balance.txt  initm.py  record.txt
[root@host-192-168-3-6 test]# vim balance.txt
I10000

 

step2:新建finance.py

#!/usr/bin/env python

import sys
import cPickle as p

def spendMoney():
    spend_date = raw_input('time: ')
    spend_money = int(raw_input('how much: '))
    spend_comment = raw_input('detail: ')

    fd = file('balance.txt')
    balance = p.load(fd)
    fd.close()

    balance -= spend_money

    fd = file('balance.txt', 'w')
    p.dump(balance, fd)
    fd.close()

    logger(spend_date, spend_comment, balance, spend_money)

def saveMoney():
    save_date = raw_input('time: ')
    save_money = int(raw_input('how much: '))
    save_comment = raw_input('detail: ')

    fd = file('balance.txt')
    balance = p.load(fd)
    fd.close()

    balance += save_money

    fd = file('balance.txt', 'w')
    p.dump(balance, fd)
    fd.close()

    logger(save_date, save_comment, balance, amount_save = save_money)

def queryInfo():
    fobj = file('record.txt')
    print "%-15s%-8s%-8s%-8s%-30s" % ('time', 'kaixiao', 'save', 'balance', 'detail')
    for eachLine in fobj:
        print eachLine,

    fobj.close()

def logger(log_time, comment, new_balance, amount_spend = '', amount_save = ''):
    fd = file('record.txt', 'a')
    fd.write('%-15s%-8s%-8s%-8s%-30s\n' % (log_time, amount_spend, amount_save, new_balance, comment))
    fd.close()

CMDs = {'s': spendMoney, 'v':saveMoney, 'q':queryInfo}

def main():
    prompt = """\033[32;1m(S)pend money
sa(V)e money
(Q)uery infomation
(E)xit
Enter your choice: \033[0m"""

    while True:
        while True:
            choice = raw_input(prompt).strip()[0].lower()
            if choice not in 'svqe':
                print 'invalid input. try again(s v q e).'
                continue
            else:
                break

        if choice == 'e':
            sys.exit()
        else:
            CMDs[choice]()

if __name__ == '__main__':
    main()

 

执行finance.py

 

root@host-192-168-3-6 test]# python finance.py 
(S)pend money
sa(V)e money
(Q)uery infomation
(E)xit
Enter your choice: v
time: 2017-07-31       
how much: 2000
detail: abc
(S)pend money
sa(V)e money
(Q)uery infomation
(E)xit
Enter your choice: q
time           kaixiao save    balance detail                        
2017-07-31             2000    12000   abc                           
(S)pend money
sa(V)e money
(Q)uery infomation
(E)xit
Enter your choice: s
time: 2017-07-31
how much: 1000
detail: shopping
(S)pend money
sa(V)e money
(Q)uery infomation
(E)xit
Enter your choice: q
time           kaixiao save    balance detail                        
2017-07-31             2000    12000   abc                           
2017-07-31     1000            11000   shopping                      
(S)pend money
sa(V)e money
(Q)uery infomation
(E)xit
Enter your choice: e
[root@host-192-168-3-6 test]# 

 

方法2:

step1:

 

initm.py内容:

#!/usr/bin/env python

import cPickle as p

with open('record.txt', 'w') as record:
    pass

with open('balance.txt', 'w') as f:
    p.dump(10000, f)

执行initm.py

[root@host-192-168-3-6 test]# python initm.py 
[root@host-192-168-3-6 test]# ls
balance.txt  initm.py  record.txt
[root@host-192-168-3-6 test]# vim balance.txt
I10000

 

step2:新建finance.py

 

#!/usr/bin/env python
#coding:utf-8
import sys
import time

import cPickle as p

walletfile = '/root/tarena/test/balance.txt'   #绝对路径下的文件
recordfile = '/root/tarena/test/record.txt'

def spendMoney():
    amount = int(raw_input('数额:'))
    comment = raw_input('用途:')

    with file(walletfile) as f:
        newbalance = p.load(f)  - amount

    with file(walletfile,'w') as f:
        p.dump(newbalance,f)

    logger('',amount,newbalance,comment)

def saveMoney():
    amount = int(raw_input('数额:'))
    comment = raw_input('来源:')

    with file(walletfile) as f:
        newbalance = p.load(f) + amount

    with file(walletfile,'w') as f:
        p.dump(newbalance,f)

    logger(amount,'',newbalance,comment)

def queryInfo():
    print "%-14s%-9s%-9s%-9s%-19s" % ('时间','存入','支出','余额','备注')

    with file(recordfile) as f:
        for eachLine in f:
            print eachLine,

def logger(saM,spM,nb,cm):
    result = "%-12s%-7s%-7s%-7s%-17s\n" % (time.strftime('%Y-%m-%d'),saM,spM,nb,cm)

    with file(recordfile,'a') as f:
        f.write(result)


CMDs = {'s': spendMoney, 'v':saveMoney, 'q':queryInfo}

def showmenu():
    prompt = """(s)支出
(v)收入
(q)查询
(e)退出
请输入您的选择(s/v/q/e): """

    while True:
        choice = raw_input(prompt).strip()[0].lower()
        if choice not in 'svqe':
            print '\033[31;1m无效的选择,请重试.\033[0m'
            continue

        if choice == 'e':
            break
        else:
            CMDs[choice]()

if __name__ == '__main__':
    showmenu()

 

执行结果:

[root@host-192-168-3-6 test]# python finance.py 
(s)支出
(v)收入
(q)查询
(e)退出
请输入您的选择(s/v/q/e): q
时间        存入   支出   余额   备注             
(s)支出
(v)收入
(q)查询
(e)退出
请输入您的选择(s/v/q/e): v
数额:10000
来源:abc
(s)支出
(v)收入
(q)查询
(e)退出
请输入您的选择(s/v/q/e): q
时间        存入   支出   余额   备注             
2017-07-31  10000         20000  abc              
(s)支出
(v)收入
(q)查询
(e)退出
请输入您的选择(s/v/q/e): s
数额:1000
用途:shopping
(s)支出
(v)收入
(q)查询
(e)退出
请输入您的选择(s/v/q/e): q
时间        存入   支出   余额   备注             
2017-07-31  10000         20000  abc              
2017-07-31         1000   19000  shopping         
(s)支出
(v)收入
(q)查询
(e)退出
请输入您的选择(s/v/q/e): e
[root@host-192-168-3-6 test]# 

 

方法3:

#!/usr/bin/env python
#coding:utf8

import os
import cPickle as p
import time 

def spend_money(wallet,record,date,amount,comment):
    
    with open(wallet) as fobj:
        balance = p.load(fobj) - amount
        
    with open(wallet,'w') as fobj:
        p.dump(balance,fobj)
        
    with open(record,'a') as fobj:
        fobj.write(
             "%-12s%-8s%-8s%-10s%-20s\n" % (date,amount,'N/A',balance,comment)
            )
    
    
def save_money(wallet,record,date,amount,comment):

    with open(wallet) as fobj:
        balance = p.load(fobj) + amount
        
    with open(wallet,'w') as fobj:
        p.dump(balance,fobj)
        
    with open(record,'a') as fobj:
        fobj.write(
            "%-12s%-8s%-8s%-10s%-20s\n" % (date,'N/A',amount,balance,comment)
            )
    
def query_money(wallet,record):
    print  "%-12s%-8s%-8s%-10s%-20s\n" % \
    ('date','spend','save','balance','comment')
    with open(record) as fobj:
        for line in fobj:
            print line,
            
    with open(wallet) as fobj:
        print "New Balance: \n%s" % p.load(fobj)

def show_menu(wallet,record):
    CMDs = {'0':spend_money,'1':save_money,'2':query_money}
    prompt = """(0)spend money 
(1)save money 
(2)query money 
(3)quit
Please input your choice(0/1/2/3):"""

    while True:
        try:
            choice = raw_input(prompt).strip()[0]
        except IndexError:
            continue
        except (KeyboardInterrupt,EOFError):
            choice = '3'
            
        if choice not in '0123':
            print "Invalid choice ,Try again"
            continue
        
        if choice == '3':
            print "Bye-bye"
            break
        args = (wallet,record)
        if choice in '01':
            date = time.strftime("%Y%m%d")
            try: 
                amount = int(raw_input("amount: "))
                comment = raw_input("comment: ")
            except ValueError:
                print "Invalid number.Try again."
                continue
            except (KeyboardInterrupt,EOFError):
                print "\nBye-bye"
                break
            args = (wallet,record,date,amount,comment)
        CMDs[choice](*args)
            
            
if __name__ == "__main__":
    wallet = 'wallet.data'
    record = "record.txt"
    if not os.path.exists(wallet):
        with open(wallet,'w') as fobj:
            p.dump(10000, fobj)
    if not os.path.exists(record):
        os.mknod(record)
            
    show_menu(wallet,record)
     

 

执行结果:

 

[root@host-192-168-3-6 test]# ls
finance.py
[root@host-192-168-3-6 test]# python finance.py 
(0)spend money 
(1)save money 
(2)query money 
(3)quit
Please input your choice(0/1/2/3):1
amount: 10000
comment: abc
(0)spend money 
(1)save money 
(2)query money 
(3)quit
Please input your choice(0/1/2/3):2
date        spend   save    balance   comment             

20170801    N/A     10000   20000     abc                 
New Balance: 
20000
(0)spend money 
(1)save money 
(2)query money 
(3)quit
Please input your choice(0/1/2/3):0
amount: 1000
comment: shopping
(0)spend money 
(1)save money 
(2)query money 
(3)quit
Please input your choice(0/1/2/3):q
Invalid choice ,Try again
(0)spend money 
(1)save money 
(2)query money 
(3)quit
Please input your choice(0/1/2/3):2
date        spend   save    balance   comment             

20170801    N/A     10000   20000     abc                 
20170801    1000    N/A     19000     shopping            
New Balance: 
19000
(0)spend money 
(1)save money 
(2)query money 
(3)quit
Please input your choice(0/1/2/3):3
Bye-bye
[root@host-192-168-3-6 test]# ls
finance.py  record.txt  wallet.data
[root@host-192-168-3-6 test]# 

 

 

备份程序

编写 backup.py 脚本,主要要求如下:
1、需要支持完全和增量备份
2、周一执行完全备份
3、其他时间执行增量备份
4、备份文件需要打包为 tar 文件并使用 gzip 格式压缩

 

方案
备份要求采用 tar 包的方式打包并用 gzip 压缩,python tarfile 模块提供了该功能。

Tarfile 模块即可以支持普通的打包功能,还可以调用 gzip bzip2 实现压缩、解压缩的功能。
为了实现增量备份,需要知道哪些文件是已经改动过了,改动过的文件才需要备份。

判断一个文件是否改动过,可以计算文件的 md5 值,如果 md5 值有变化则表明文件已改,否则为未修改。python hashlib 模块提供了计算 md5 值的方法

 

在计算 md5 值时,有些文件可能非常大,不应该直接将文件全部读入内存,而应该使用循环的方式分部分更新。md5 值每天增量备份时都需要使用,必须将其永久的保存下来,
可以使用 cPickle 永久存储器实现该功能。把文件的 md5 值存入字典,再将字典存入到cPickle 永久存储器中。
每天都可能会产生一个增量备份文件,为了保证文件名不冲突又便于识别,可以采用基础名加日期的命名方式。
以下步骤假设要备份的目录为/home/demo/src,备份的目标目录为/home/demo/dst

 

方法1:

步骤一:编写脚本

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

import time
import hashlib
import tarfile
import os
import cPickle


basedir = '/home/demo'
srcdir = 'src'
dstdir = 'dst'
md5file = os.path.join(basedir, dstdir, 'md5.data') #md5 文件位置
fullname = 'full_src%s.tar.gz' % (time.strftime('%Y%m%d')) #完全备份文件名
incname = 'incr_src%s.tar.gz' % (time.strftime('%Y%m%d')) #增量备份文件名

def fullBackup(): #完全备份函数
    os.chdir(basedir)
    tar = tarfile.open(os.path.join(basedir, dstdir, fullname), 'w:gz')
    tar.add(srcdir)
    tar.close()

    md5dict = {} #计算文件 md5 值
    for eachFile in os.listdir(os.path.join(basedir, srcdir)):
        fullpath = os.path.join(basedir, srcdir, eachFile)
        md5dict[eachFile] = md5sum(fullpath)

    with open(md5file, 'w') as f: #存储文件 md5 值到 cPicle 存储器
        cPickle.dump(md5dict, f)

def incrBackup(): #增量备份函数
    with open(md5file) as f:
        storedmd5 = cPickle.load(f)

    newmd5 = {}
    for eachFile in os.listdir(os.path.join(basedir, srcdir)):
        fullpath = os.path.join(basedir, srcdir, eachFile)
        newmd5[eachFile] = md5sum(fullpath)

    tar = tarfile.open(os.path.join(basedir, dstdir, incname), 'w:gz')
    os.chdir(basedir)
    for eachKey in newmd5:
        if (eachKey not in storedmd5) or (newmd5[eachKey] != storedmd5[eachKey]):
            tar.add(os.path.join(srcdir, eachKey))
        tar.close()

        with open(md5file, 'w') as f:
            cPickle.dump(newmd5, f)


def md5sum(fname): #md5 值计算函数
    m = hashlib.md5()
    with open(fname) as f:
        while True:
            data = f.read(4096)
            if len(data) == 0:
                break
            m.update(data)
    return m.hexdigest()

def main():
    if time.strftime('%a') == 'Mon': #如果是周一执行完全备份,否则执行增量备份
        fullBackup()
    else:
        incrBackup()

if __name__ == '__main__':
    main()

step2:

[root@host-192-168-3-6 test]# mkdir /home/demo/
[root@host-192-168-3-6 test]# cd /home/demo/
[root@host-192-168-3-6 demo]# mkdir dst
[root@host-192-168-3-6 demo]# mkdir src
[root@host-192-168-3-6 demo]# cd -
/root/tarena/test
[root@host-192-168-3-6 test]# 
[root@host-192-168-3-6 test]# python backup.py 
[root@host-192-168-3-6 test]# ls
backup.py  finance.py  record.txt  wallet.data
[root@host-192-168-3-6 test]# cd /home/demo/
[root@host-192-168-3-6 demo]# ls
aaa  ccc  dst  d.txt  hosts  passwd  redhat-release  shadow  src
[root@host-192-168-3-6 demo]# cd src/
[root@host-192-168-3-6 src]# ls
[root@host-192-168-3-6 src]# cd ../dst/
[root@host-192-168-3-6 dst]# ls
full_src20170801.tar.gz  md5.data
[root@host-192-168-3-6 dst]# 

step3:测试脚本执行


[root@py01 bin]# crontab –e
00 01 * * * /root/bin/backup.py

 

方法2:

step1:

>>> afile = "/home/demo/aaa/a.txt"
>>> afile.split('/home')
['', '/demo/aaa/a.txt']
>>> afile.split("home")[1]
'/demo/aaa/a.txt'
>>> afile.split("home")[1].lstrip('/')
'demo/aaa/a.txt'
>>> 

 

step2:编写脚本

#!/usr/bin/env python
#coding:utf8


import time 
import os 
import tarfile
import cPickle as p 
import hashlib

def check_md5(fname):
    #计算文件md5值,为了防止文件太大,占用内存,每次计算4096字节
    m = hashlib.md5()
    with open(fname) as fobj:
        while True:
            data = fobj.read(4096)
            if not data:
                break
            m.update(data)
    return m.hexdigest()
    


def full_backup(src_dir,dst_dir,md5file):
    """首先,切割源目录,将要备份的目录及其所在的目录分开
    目标名称:demo_full_20160411.tar.gz,再生成绝对路径
    """
    md5dict = {}
    base_dir,back_dir = os.path.split(src_dir.rstrip('/'))
    back_name = "%s_full_%s.tar.gz" % (back_dir,time.strftime("%Y%m%d"))
    full_path = os.path.join(dst_dir,back_name)
    
    os.chdir(base_dir)
    tar = tarfile.open(full_path,"w:gz")
    tar.add(back_dir)
    tar.close()
    
    for path,dirs,files in os.walk(src_dir):
        for each_file in files:
            full_name = os.path.join(path,each_file)
            md5dict[full_name] = check_md5(full_name)
            
            
    with open(md5file,'w') as fobj:
        p.dump(md5dict,fobj)
    
def incr_backup(src_dir,dst_dir,md5file):
    base_dir,back_dir = os.path.split(src_dir.rstrip('/'))
    back_name = "%s_incr_%s.tar.gz" % (back_dir,time.strftime("%Y%m%d"))
    full_path = os.path.join(dst_dir,back_name)
    new_md5 = {}
    
    with open(md5file) as fobj:
        old_md5 = p.load(fobj)
    
    for path,dirs,files in os.walk(src_dir):
        for each_file in files:
            full_name = os.path.join(path,each_file)
            new_md5[full_name] = check_md5(full_name)
            
    with open(md5file,'w') as fobj:
        p.dump(new_md5,fobj)
        
        
    os.chdir(base_dir)
    tar = tarfile.open(full_path,'w:gz')
    for key in new_md5:
        
        #if key not in old_md5 or new_md5[key] != old_md5[key]:
        if old_md5.get(key) != new_md5[key]:
            #key值是文件名,如果文件名不在老字典中,是新文件;如果value不一样,是改动的文件
                                    
            tar.add(key.split(base_dir)[1].lstrip('/'))
    tar.close()    
    
if __name__ == "__main__":
    src_dir = '/home/demo'
    dst_dir = '/home/dst'
    md5file = '/home/dst/md5.data'
    if time.strftime('%a') == "Mon":
        full_backup(src_dir,dst_dir,md5file)
    else:
        incr_backup(src_dir,dst_dir,md5file)

 

step3:执行结果

 

[root@host-192-168-3-6 test]# cd -
/home
[root@host-192-168-3-6 home]# mkdir dst
[root@host-192-168-3-6 home]# mkdir demo
[root@host-192-168-3-6 home]# cd -
/root/tarena/test
[root@host-192-168-3-6 test]# python backup.py 
[root@host-192-168-3-6 test]# cd -
/home
[root@host-192-168-3-6 home]# ls
demo  dst
[root@host-192-168-3-6 home]# cd dst/
[root@host-192-168-3-6 dst]# ls
demo_full_20170801.tar.gz  md5.data

 

step4:修改脚本


if time.strftime('%a') == "Mon":
改成

if time.strftime('%a') == "Tue":

 

step5:执行结果

root@host-192-168-3-6 test]# python backup.py 
[root@host-192-168-3-6 test]# cd -
/home/dst
[root@host-192-168-3-6 dst]# ls
demo_full_20170801.tar.gz  demo_incr_20170801.tar.gz  md5.data

 

posted @ 2019-06-18 22:59  钟桂耀  阅读(288)  评论(0编辑  收藏  举报