如何通过后台运行的程序读取参数?

时间:2015-08-10 02:41:47

标签: python python-3.x argparse

示例:一个每10秒打印一次列表值的简单程序

import argparse
import time
import sys

myList = []

def parseArguments():
    parser = argparse.ArgumentParser(description="example")
    parser.add_argument('-a', '--addElement', help='adds an element to the list')

    args = parser.parse_args()

    if args.addElement:
        myList.append(args.addElement)

def main():
    parseArguments()

    while(True):
        print(myList)
        time.sleep(10)

问题是程序只读取在开始时传递的参数,我希望它在运行时随时读取传递的参数。

我想在后台运行程序,就像服务一样,并且每隔一段时间就将参数传递给程序。

3 个答案:

答案 0 :(得分:2)

我明白你所要求的看起来像服务(或守护进程)能够接受异步命令。

外部接口:

prog foo

=>确定重复打印['foo']

后:

prog bar

=>第二个实例退出并且第一个实例重复打印['foo', 'bar']

内部设计

这远非简单!您需要设置IPC机制以允许第二个实例与第一个实例进行通信,并在第一个实例中使用非阻塞IO(或多线程)。在Unix下,你可以使用os.mkfifo,但是你想要一个便携式解决方案,你必须在localhost上使用IP套接字

高级伪代码中的结构

get argument via argparse
bind to a fix port on localhost, in UDP protocol
if success:
    # ok it is the first prog
    initialize list from argument
    loop:
        get command from UDP socket, with timeout = 10s
        if cmd is add param:
            add parameter to list
        elif cmd is exit:   # not asked in question but should exist
            exit
        print list
else:
    # another prog has taken the socket, pass it the arg
    send the arg to the UDP port with proper protocol

关于这个简单设计的注意事项:有一个竞争条件是在第一次尝试绑定和发送之间退出的套接字上已经有一个prog等待。要解决这个问题,你应该使用TCP协议,select监听套接字超时,并正常关闭以确保在另一端收到消息。如果出现错误,您将迭代(最长时间),因为第一台服务器可能已经退出。

这是一个实现示例:

import socket
import select
import argparse
import time
import sys

TIMEOUT=10
IFACE='127.0.0.1'
PORT=4000
DEBUG=False

myList = []
old = ""

def parseArguments():
    parser = argparse.ArgumentParser(description="example")
    parser.add_argument('-a', '--addElement',
                        help='adds an element to the list')
    parser.add_argument('-q', '--quit', action='store_true',
                        help='closes main service')
    parser.add_argument('-d', '--debug', action='store_true',
                        help='display debug information')

    args = parser.parse_args()

    if args.quit:
        senddata("QUIT\n")
        sys.exit(0)

    if args.debug:
        DEBUG=True

    if args.addElement:
        myList.append(args.addElement)

def read(s):
    global old
    data = old
    while True:
        block = s.recv(1024)
        if len(block) == 0: return data
        if b'\n' in block:
            block,o = block.split(b'\n', 1)
            old = o.decode()
            data += block.decode()
            return data
        data += block.decode()

def gracefulclose(s, msg):
    s.send(msg.encode())
    s.shutdown(socket.SHUT_WR)
    try:
        read(s)
    finally:
        s.close()

def server(s):
    if DEBUG:
        print("SERVER")
    s.listen(5)
    while True:
        sl = select.select([s], [], [], TIMEOUT)
        if len(sl[0]) > 0:
            s2, peer = s.accept()
            try:
                data = read(s2)
                print(data)
                gracefulclose(s2, "OK")
            finally:
                s2.close()
            if data.startswith("QUIT"):
                return
            elif data.startswith("DATA:"):
                myList.append(data[5:])
        print(myList)

def senddata(data):
    s = socket.socket(socket.AF_INET)
    try:
        s.connect((IFACE, PORT))
        s.send(data.encode())
        data = read(s)
        if (data.startswith("OK")):
            return True
    except:
        pass
    finally:
        s.close()
    return False

def client():
    return senddata("DATA:" + myList[0] + "\n")

def main():
    end = False
    MAX = 5
    while not end and MAX > 0:
        s = socket.socket(socket.AF_INET)
        try:
            s.bind((IFACE, PORT))
        except Exception:
            s.close()
            s = None
        if s:
            try:
                server(s)
            finally:
                s.close()
                return
        else:
            if DEBUG:
                print("CLIENT", " ", 6 - MAX)
            end = client()
        MAX -= 1
        time.sleep(1)

if __name__ == "__main__":
    parseArguments()
    main()

答案 1 :(得分:1)

import argparse
import time
import sys

myList = []

def parseArguments():
    parser = argparse.ArgumentParser(description="example")
    parser.add_argument('-a', '--addElement', help='adds an element to the list')

    args = parser.parse_args()

    if args.addElement:
        myList.append(args.addElement)

def main():
    parseArguments()

    import select
    while(True):
        while select.select([sys.stdin], [], [], 0)[0]:
            myList.append(sys.stdin.readline().strip())
        print(myList)
        time.sleep(10)

如果在执行期间传递更多参数,则必须从stdin中读取它们。使用select模块,您可以检查stdin中是否有新行,然后将它们添加到myList。

答案 2 :(得分:1)

基本上你要问的是如何进行进程间通信(IPC)。

我为什么这么说?那么,回答你自己:你想如何将这些论点传递给你的后台服务?用手?我不这么认为(因为这样你就有了一个简单的交互式程序,应该等待用户输入)。您可能想要一些其他脚本/程序通过某种命令 on-demand 发送这些参数。

通常有几种方式可以传达两个或更多节目,最常见的是:

共享文件 - 您只需检查磁盘上文件的内容即可。此解决方案的优点是您可以使用您喜欢的文本编辑器编辑此文件,而无需编写客户端应用程序。

Pipes - 一个程序读取其输入,这是另一个程序的输出。你应该简单地阅读sys.stdin。

# receiver
def read_input():
    for l in sys.stdin:
        yield l

套接字 - 通过网络接口发送的数据流(但可以在同一台计算机上本地发送)。 Python文档对套接字编程非常友好introduction

共享内存 - 您的程序读/写相同的内存块。在Python中,您可以使用mmap模块来实现此目的。

无论您选择哪种方式来传达您的流程,都应该在它们之间建立某种接口。它可以是非常简单的基于文本的界面,如下所示:

# command syntax
<command> SPACE <parameter> NEWLINE
SPACE := 0x20     # space character
NEWLINE := 0x0A   # '\n' character

# a command adding element to receiver's list
ADD SPACE <element> NEWLINE

# a command removing element from receiver's list:
REMOVE SPACE <element> NEWLINE

# examples:
ADD first element\n
REMOVE first element\n

因此,例如,如果您通过套接字发送消息(我建议),您的接收方(服务器)应该读取缓冲区直到换行符,然后检查第一个单词是否为“ADD”,然后添加剩余字符(减去换行符到你的清单。当然,您应该为某种“攻击”做好准备 - 就像您应该指定您的邮件不能超过例如4096字节。这样,您可以在达到其限制后丢弃当前缓冲区,这意味着在等待换行符时不会无限期地分配内存。这是一条非常重要的规则:不要相信用户输入。

祝你好运! :)