我有一个长期运行的Python脚本,它从Twitter收集推文,我想知道它每隔一段时间做一次。
目前,我使用signal
库来捕获中断,此时我调用了print函数。像这样:
import signal
def print_info(count):
print "#Tweets:", count
#Print out the process ID so I can interrupt it for info
print 'PID:', os.getpid()
#Start listening for interrupts
signal.signal(signal.SIGUSR1, functools.partial(print_info, tweet_count))
每当我想要我的信息时,我会打开一个新的终端并发出我的中断:
$kill -USR1 <pid>
有更好的方法吗?我知道我可以按计划的时间间隔使用我的脚本,但我更感兴趣的是根据需要知道,并且可能也会发布其他命令。
答案 0 :(得分:2)
向进程发送信号会中断进程。下面你会发现一种使用专用线程来模拟python控制台的方法。控制台暴露为unix插槽。
import traceback
import importlib
from code import InteractiveConsole
import sys
import socket
import os
import threading
from logging import getLogger
# template used to generate file name
SOCK_FILE_TEMPLATE = '%(dir)s/%(prefix)s-%(pid)d.socket'
log = getLogger(__name__)
class SocketConsole(object):
'''
Ported form :eventlet.backdoor.SocketConsole:.
'''
def __init__(self, locals, conn, banner=None): # pylint: diable=W0622
self.locals = locals
self.desc = _fileobject(conn)
self.banner = banner
self.saved = None
def switch(self):
self.saved = sys.stdin, sys.stderr, sys.stdout
sys.stdin = sys.stdout = sys.stderr = self.desc
def switch_out(self):
sys.stdin, sys.stderr, sys.stdout = self.saved
def finalize(self):
self.desc = None
def _run(self):
try:
console = InteractiveConsole(self.locals)
# __builtins__ may either be the __builtin__ module or
# __builtin__.__dict__ in the latter case typing
# locals() at the backdoor prompt spews out lots of
# useless stuff
import __builtin__
console.locals["__builtins__"] = __builtin__
console.interact(banner=self.banner)
except SystemExit: # raised by quit()
sys.exc_clear()
finally:
self.switch_out()
self.finalize()
class _fileobject(socket._fileobject):
def write(self, data):
self._sock.sendall(data)
def isatty(self):
return True
def flush(self):
pass
def readline(self, *a):
return socket._fileobject.readline(self, *a).replace("\r\n", "\n")
def make_threaded_backdoor(prefix=None):
'''
:return: started daemon thread running :main_loop:
'''
socket_file_name = _get_filename(prefix)
db_thread = threading.Thread(target=main_loop, args=(socket_file_name,))
db_thread.setDaemon(True)
db_thread.start()
return db_thread
def _get_filename(prefix):
return SOCK_FILE_TEMPLATE % {
'dir': '/var/run',
'prefix': prefix,
'pid': os.getpid(),
}
def main_loop(socket_filename):
try:
log.debug('Binding backdoor socket to %s', socket_filename)
check_socket(socket_filename)
sockobj = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sockobj.bind(socket_filename)
sockobj.listen(5)
except Exception, e:
log.exception('Failed to init backdoor socket %s', e)
return
while True:
conn = None
try:
conn, _ = sockobj.accept()
console = SocketConsole(locals=None, conn=conn, banner=None)
console.switch()
console._run()
except IOError:
log.debug('IOError closing connection')
finally:
if conn:
conn.close()
def check_socket(socket_filename):
try:
os.unlink(socket_filename)
except OSError:
if os.path.exists(socket_filename):
raise
示例程序:
make_threaded_backdoor(prefix='test')
while True:
pass
示例会话:
mmatczuk@cactus:~$ rlwrap nc -U /var/run/test-3196.socket
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import os
>>> os.getpid()
3196
>>> quit()
mmatczuk@cactus:~$
这是一个非常强大的工具,可用于:
甚至更多。
答案 1 :(得分:0)
我亲自将信息写入一个文件,以便我之后使用它,尽管这样做的缺点可能是稍微慢一点,因为它必须每次或每隔几次写一个文件来检索一条推文。
无论如何,如果您将其写入文件"output.txt"
,则可以打开bash
并输入tail output.txt
以查找文件中打印的最新10行,或者您可以输入tail -f output.txt
,它使用您写入文件的行不断更新终端提示。如果你想停止,只需Ctrl-C
。
答案 2 :(得分:0)
这是一个长期运行的程序示例,它还维护状态套接字。当客户端连接到套接字时,脚本会将一些状态信息写入套接字。
#!/usr/bin/python
import os
import sys
import argparse
import random
import threading
import socket
import time
import select
val1 = 0
val2 = 0
lastupdate = 0
quit = False
# This function runs in a separate thread. When a client connects,
# we write out some basic status information, close the client socket,
# and wait for the next connection.
def connection_handler(sock):
global val1, val2, lastupdate, quit
while not quit:
# We use select() with a timeout here so that we are able to catch the
# quit flag in a timely manner.
rlist, wlist, xlist = select.select([sock],[],[], 0.5)
if not rlist:
continue
client, clientaddr = sock.accept()
client.send('%s %s %s\n' % (lastupdate, val1, val2))
client.close()
# This function starts the listener thread.
def start_listener():
sock = socket.socket(socket.AF_UNIX)
try:
os.unlink('/var/tmp/myprog.socket')
except OSError:
pass
sock.bind('/var/tmp/myprog.socket')
sock.listen(5)
t = threading.Thread(
target=connection_handler,
args=(sock,))
t.start()
def main():
global val1, val2, lastupdate
start_listener()
# Here is the part of our script that actually does "work".
while True:
print 'updating...'
lastupdate = time.time()
val1 = val1 + random.randint(1,10)
val2 = val2 + random.randint(100,200)
print 'sleeping...'
time.sleep(5)
if __name__ == '__main__':
try:
main()
except (Exception,KeyboardInterrupt,SystemExit):
quit=True
raise
您可以编写一个简单的Python客户端来连接套接字,或者您可以使用类似socat
的内容:
$ socat - unix:/var/tmp/myprog.sock
1403061693.06 6 152
答案 3 :(得分:0)
之前我曾写过类似的应用程序。
这是我做的:
当只需要几个命令时,我就像你一样使用信号,只是为了不使它太复杂。根据命令,我指的是您希望应用程序执行的操作,例如帖子中的print_info
。
但是当应用程序更新时,需要更多不同的命令,我开始使用特殊线程侦听套接字端口或读取本地文件以接受命令。假设应用程序需要支持prinf_info1
print_info2
print_info3
,因此您可以使用客户端连接到目标端口并编写 print_info1 以使应用程序执行命令< em> print_info1 (或者如果您使用的是读取本地文件机制,只需将 print_info1 写入本地文件)。
当使用侦听套接字端口机制时,缺点是编写一个客户端来执行命令会花费更多的工作,优点是你可以在任何地方发出命令。
当使用读取本地文件机制时,缺点是你必须让线程在循环中检查文件并且它将使用一点资源,优点是给出命令非常简单(只需将一个字符串写入一个文件)并且您不需要编写客户端和套接字侦听服务器。
答案 4 :(得分:0)
rpyc
是完成此任务的完美工具。
简而言之,您定义了一个rpyc.Service
类,它公开了您要公开的命令,并启动rpyc.Server
线程。
然后,您的客户端将连接到您的进程,并调用映射到您的服务公开的命令的方法。
它就像那样简单干净。无需担心套接字,信号,对象序列化。
它还有其他很酷的功能,例如协议是对称的。