我知道(such as here)上有类似的主题,但我的设计设计稍微复杂一些。
我正在设计一个将在SSH窗口中运行的CLI脚本。该脚本将在Ubuntu 14.10服务器上托管和执行。它旨在主动监视主机交换机上端口和客户端的当前状态。每30秒或由用户定义,它将通过SNMP获取数据,然后刷新信息并将其显示在屏幕上。当它等待下一次刷新时,会有一个计时器,指示它何时会再次查询设备以获取信息。
我希望允许用户随时按特定键来更改输出视图或编辑关键变量。 (该功能类似于Unix top
。)例如,按t
将要求它们在循环之间输入所需的秒数。 h
,m
或i
会切换显示/隐藏某些列。这些不暂停计时器也不会退出循环,因为在下次刷新时会应用更改。 r
会强制立即刷新并应用更改。 q
或Ctrl+C
将退出脚本。
主要活动如下:
Query loop <-----------------------------
| |
| Process Data
| ^
| |
v Query Data #SNMPBULKWALK
Timer <------------- ^
| | | |
| | Check time remaining |
| | ^ |
| |_______________| |
|___________________________________|
使用按键中断,它会像这样:
Query loop <----------------------
| | ???<---Change variables
| (Continue) ^
V | |
Timer <--------- !!INTERRUPT!!---------> Identify key
| | | ^
| | Check time remaining |
| | ^ |
| |___________| |
|_____________________________|
我有点难过。我开始相信我可能需要实现线程 - 我没有经验 - 因为while
循环本身并不能满足我们的需求。我还不确定如何将更改注入包含变量的对象(例如,计时器,显示格式的标志),因为我们的循环将不断使用它。
答案 0 :(得分:1)
没什么复杂的,也没有任何需要任何包裹。
唯一的问题是它需要在程序退出时将终端恢复正常。
即。如果程序崩溃,终端将无法恢复,用户也不会看到他正在输入的内容。
但如果用户知道他在做什么,他可以强制重启shell,一切都会恢复正常。
对于corse,你可以更简单的方式使用这段代码,并使用raw_input()来完成这些工作。
from thread import start_new_thread as thread
from time import sleep
# Get only one character from stdin without echoing it to stdout
import termios
import fcntl
import sys
import os
fd = sys.stdin.fileno()
oldterm, oldflags = None, None
def prepareterm ():
"""Turn off echoing"""
global oldterm, oldflags
if oldterm!=None and oldflags!=None: return
oldterm = termios.tcgetattr(fd)
newattr = oldterm[:] # Copy of attributes to change
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
def restoreterm ():
"""Restore terminal to its previous state"""
global oldterm, oldflags
if oldterm==None and oldflags==None: return
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
oldterm, oldflags = None, None
def getchar():
"""Get character from stdin"""
prepareterm()
while 1:
try:
c = sys.stdin.read(1)
break
except IOError:
try: sleep(0.001)
except: restoreterm(); raise KeyboardInterrupt
restoreterm()
return c
def control ():
"""Waits for keypress"""
global running, done
while running:
c = getchar().lower()
print "Keypress:", c
if c=="q": print "Quitting!"; running = 0; break
done += 1
def work ():
"""Does the server-client part"""
global done
while running:
# Do your stuff here!!!
# Just to illustrate:
print "I am protending to work!\nPress Q to kill me!"
sleep(1)
print "I am done!\nExiting . . ."
done += 1
# Just to feel better
import atexit
atexit.register(restoreterm)
# Start the program
running = 1
done = 0
thread(work, ())
thread(control, ())
# Block the program not to close when threads detach from the main one:
while running:
try: sleep(0.2)
except: running = 0
# Wait for both threads to finish:
while done!=2:
try: sleep(0.001)
except: pass # Ignore KeyboardInterrupt
restoreterm() # Just in case!
在实践中,程序永远不能在不将终端恢复正常的情况下退出,但是会发生这种情况。
现在,您可以通过仅使用一个线程并将工作循环放入主线程来简化操作,并使用raw_input从用户获取命令。 或者甚至更好,将服务器 - 客户端代码放在后台并等待主线程中的输入。
使用线程模块而不是原始线程也可能更安全。
如果您使用asyncore模块,您将使每个客户端都自己运行,并且您的主线程将被asyncore.loop()占用。 你可以覆盖它,即。重写它以检查输入和你想做的其他事情,同时保持asyncore检查同步。 此外,重负载需要覆盖其中的一些讨厌的函数,因为它的缓冲区固定为512字节,如果我没有弄错。否则,它可能是解决您问题的好方法。
最后,为了清楚起见,没有回显用户输入的代码被采用并改编自getpass模块。 只是有点我的。
答案 1 :(得分:0)