最近写一些测试工具,实在懒得搞GUI,然后意识到python有一个自带模块叫cmd,用了用发现简直是救星。

1. 基本用法

cmd模块很容易学到,基本的用法比较简单,继承模块下的Cmd类,添加需要的功能入口就好了。

Cmd类有个prompt属性,修改它可以把默认提示符((cmd))替换成自定义的;

为自己的Cmd类添加名为“do_xxx()”的方法,则运行时,在提示符下可以接受xxx指令。但对应的参数解析貌似是要自己搞定的,否则在指令名之后输入的所有东西,只要不回车,它都是一个大参数;

有指令的实现方法,也有对应帮助信息的实现方法。只要添加“help_xxx()”方法,就可以为xxx方法在运行时提供一个帮助信息了。当然你也可以实现一个“do_help()”来打印帮助汇总什么的;

2. 简单实例

闲话少说,先上代码

 from cmd import Cmd
from sys import exit class MyCmd(Cmd):
def __init__(self):
super(MyCmd, self).__init__()
self.prompt = "->" def do_hello(self, args):
print("Hello.") def help_hello(self, args):
print("hello - print hello and do nothing more.") def do_exit(self, args):
exit(0) def main():
mycmd = MyCmd()
mycmd.cmdloop(intro="My Cmd Demo.") if __name__ == "__main__":
main()

3. 问题来了

最初的想法实际上是要用cmd构造一个工具,连接到某个服务端,没动作的时候就接收和显示那边发来的数据,某些时候还要按照交互输入的指令,往服务端那边发送一些数据。为了交互方便,才选择cmd来构造界面。

那么起码来说,工具要有个do_exit()方法用来退出,还要有个do_send()方法用来发数据给服务端。

只有这么简单就好了,实际需要考虑等着收数据的同时还要等着操作者从界面输入指令,指令输入完毕回车以后,就要把数据发出去。显然这是个异步场景。说到异步,想到了tornado和twisted,但试了试twisted发现用来搞这种客户端并不怎么方便;后来想起python自带的asyncore,看了下文档发现很简单就可以做到既一直接收又能随时发送,就改用asyncore了。

但asyncore的dispatcher要跑起来的话,得运行asyncore.loop(),但这个如果在主线程里运行起来,后面就没有Cmd.cmdloop()什么事了。

对此,我想到的是,把asyncore.loop()放到一个后台线程里去,而把cmdloop()放在主线程里。而要让cmd界面能够通过dispatcher发送数据,还是得先让它知道dispatcher实例的存在。

好在python自带了不少各种电池,调用Threading提供的功能,没费多少功夫就搞好了。

大概是这样的:

from cmd import Cmd
from sys import exit, argv
import threading
import socket
import asyncore class BackgroundRunner(asyncore.dispatcher, object):
def __init__(self, host, port):
super(BackgroundRunner, self).__init__()
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, port))
self.buffer = b'' def handle_connect(self):
pass def handle_close(self):
self.close() def handle_read(self):
recvdata = self.recv(4096)
# TO-DO with the data received def writable(self):
return len(self.buffer) > 0 def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:] class MyCmd(Cmd, object)
def __init__(self):
super(MyCmd, self).__init__()
self.prompt = "->"
self.bgrunner = BackgroundRunner(host, port) # 调用setDaemon()将线程转到后台,否则它执行ssyncore.loop()的时候会占住你的标准输入和输出直到强行退出
nthd = threading.Thread(target=asyncore.loop)
nthd.setDaemon(True)
nthd.start()

  # Cmd.emptyline()方法应该视需要进行重载,否则回车输入空指令的默认处理会是执行上一条指令
def emptyline(self):
pass def do_send(self, args):
# bla bla bla, 对参数args(其实就是个字符串)做些事,得到你要发送的数据data
sendresult = self.bgrunner.send(data)
# bla bla bla def do_exit(self, args):
self.bgrunner.close()
exit(0) def main():
# 其实你完全可以用argparse什么的来处理命令行参数,我这只是给自己额写个示例才直接搞argv的,别太认真
host, port = argv[1:3]
c = MyCmd(host, int(port))
c.cmdloop() if __name__ == "__main__":
main()

4. 各种小麻烦

默认cmd模块中的Cmd类会使用rawinput来处理提示符显示和输入信息获取的工作,但是特定情况下会有个问题:

当交互线程等待用户输入指令的时候,如果希望后台线程可以打印信息到前台显示的话……

打印随便用print或者sys.stdout.write什么的,当然是打印出来了,但只要开始输入新的指令,这些打印信息就都被清除掉了,只剩下提示符和新的输入。如果想实时看什么东西的话……

反复尝试和阅读cmd模块源码以后发现这么一件事:

Cmd类在实例化的时候,默认会有个use_rawinput属性是为1的,如果重载__init__()的时候把它设置为0,那么会改为通过readline来处理提示符和输入(当然你如果在windows上玩这一手的话,最好先把pyreadline装上,windows上我没弄过gnu readline,不知道有没搞成了的),然后打印信息被擦除的问题就得以解决了。

其实记录信息完全可以让logging模块去搞,但这次的任务只是个即时小工具而已……

【Python】使用cmd模块构造一个带有后台线程的交互命令行界面的更多相关文章

  1. python之cmd模块

    md模块可以用来做交互式shell cmd模块是python中包含的一个公共模块,用于交互式shell和其他命令解释器等的基类,我们可以基于cmd模块自定义我们的子类,实现我们自己的交互式shell ...

  2. 使用 Python 的 Socket 模块构建一个 UDP 扫描工具

    译文:oschina 英文:bt3gl 当涉及到对一些目标网络的侦察时,出发点无疑是首先发现宿主主机.这个任务还可能包含嗅探和解析网络中数据包的能力. 几周前,我曾经谈到了如何使用Wireshark来 ...

  3. HandlerThread 创建一个异步的后台线程

    使用HandlerThread几大优点: 1.制作一个后台异步线程,需要的时候就可以丢一个任务给它,使用比较灵活; 2.Android系统提供的,使用简单方便,内部自己封装了Looper+Handle ...

  4. 通过python的hashlib模块计算一个文件的MD5值

    Python的hashlib提供了很多摘要算法,如MD5,SHA1等常用算法. 什么是摘要算法呢?摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(如MD5值 ...

  5. 吴裕雄--天生自然python学习笔记:python 用pygame模块制作一个音效播放器

    用 Sound 对象制作一个音效播放器. 应用程序总览 程序在执行后默认会把 WAV 音频文件加载到清单中,单击“播放”按钮可开始 播放,同时显示 “正在播放 xxx 音效”的信息 . 播放过程中,可 ...

  6. Python的Cmd模块的简易运用学习

    昨天大佬阿炳给发了一份代码给我,看着感觉很好玩,这是自己写了个命令行吗,完了我就找篇更详细一点的博客学习了一下  cmd的主要方法和属性 方法: (1)cmdloop():类似与Tkinter的mai ...

  7. python利用scapy模块写一个TCP路由追踪和扫描存活IP的脚本

    前言: 没有前言 0x01 from scapy.all import * import sys from socket import * import os from threading impor ...

  8. python之collections模块(OrderDict,defaultdict)

    前言: import collections print([name for name in dir(collections) if not name.startswith("_" ...

  9. python的_thread模块来实现多线程(<python核心编程例子>)

    python中_thread模块是一个低级别的多线程模块,它的问题在于主线程运行完毕后,会立马把子线程给结束掉,不加处理地使用_thread模块是不合适的.这里把书中讲述的有关_thread使用的例子 ...

随机推荐

  1. AJAX提交到Handler.ashx一般处理程序返回json数据-转

    直接贴代码!我也测试通过! 一切看注释! 谢谢! <%@ WebHandler Language="C#" class="Handler" %> u ...

  2. XSL简单实例

    利用xsl可以做到将数据和格式完全分离,下面比较一个xml+css和xml+xsl的区别. XML + css resume.xml文件内容如下: <?xml version="1.0 ...

  3. Spring Boot 2.x(十七):快速入门Elastic Search

    What -- Elasticsearch是什么? Elasticsearch是一个基于Lucene的搜索服务器,Elasticsearch也是使用Java编写的,它的内部使用Lucene做索引与搜索 ...

  4. 【阿里云IoT+YF3300】1.时代大背景下的阿里云IoT物联网的现状和未来

    “未来十到二十年,大家基本已经形成了一个共识,那便是新格局的奠定将由 AI 和物联网技术来支撑.放眼国内,在这些互联网巨头之中,未来真正成为竞争对手厮杀的,阿里和华为是首当其冲,在这两个领域双方分别暗 ...

  5. HDU1080 【LCS变形】

    题意: 给你每种字符匹配的权值大小,给你两个串,长度小的串可以在小串里面添加空格和大串匹配,问你一个最大匹配权值. 思路: 有点类似于LCS吧,我们在求两个串的LCS的时候,不行的就扔掉了,在这里就是 ...

  6. 浅谈C# String对象

    本文介绍C#中的string是一个引用类型,C# String对象是存放在堆上,而不是堆栈上的,因此,当把一个字符串变量赋给另一个字符串时,会得到对内存中同一个字符串的两个引用. AD:WOT2015 ...

  7. Educational Codeforces Round 18D(完全二叉树中序遍历&lowbit)

    题目链接:http://codeforces.com/contest/792/problem/D 题意:第一行输入n, q,分别表示给出一颗n个节点的中序遍历满二叉树,后面有q个询问; 接下来有q组形 ...

  8. LCT 动态树 模板

    洛谷:P3690 [模板]Link Cut Tree (动态树) /*诸多细节,不注意就会调死去! 见注释.*/ #include<cstdio> #include<iostream ...

  9. -webkit-line-clamp 兼容性问题

    1.一般情况下,想要实现文本超过几行后显示省略号的css. color: #101010; font-size: 14px; text-align: justify; font-family: Sou ...

  10. ubuntu 14 安装XML::Simple 模块

    最近需要用到perl 来解析xml 文件,从网上搜索了一下,大部分都建议使用XML::Simple 模块来解析,这里记录一下安装过程 方法一: 直接使用CPAN 来安装模块 $ perl -MCPAN ...