首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于IP地址和端口的PID定位

基于IP地址和端口的PID定位
EN

Code Review用户
提问于 2015-07-28 20:35:07
回答 3查看 2.6K关注 0票数 6

我编写了一个简单的Python应用程序,它给出了一个IP地址和端口,然后应用程序在IP:PORT上找到了监听PID。

我希望听到对现行守则的建设性批评。

代码语言:javascript
复制
import argparse
import sys
import socket
import subprocess
import os


def is_legal_ip(address):
    ip = address.split(":")
    pieces = ip[0].split('.')
    if len(pieces) != 4: 
        return False
    try: 
        return all(0<=int(p)<256 for p in pieces)
    except ValueError: 
        return False

def parse_arguments():
    if len(sys.argv) > 2 or len(sys.argv) == 1:
        print("Please provide a single argument.")
        exit(1)

    parser = argparse.ArgumentParser("Returns a PID listenning on IP:PORT")
    parser.add_argument('ip_port', metavar='IP:PORT', type=str, nargs=1, help='local address')
    args = parser.parse_args()

    if (is_legal_ip(args.ip_port[0]) == False):
        print("Invalid IP address.")
        exit(1)

    return(args.ip_port[0])

def address_to_pid(ip_port):
    devnull = open(os.devnull, 'w')
    output = subprocess.check_output("netstat -nptl".split(), stderr = devnull).splitlines()
    devnull.close()

    for line in output[2::]:
        raw_line = line.split()
        if raw_line[3] == ip_port:
            if raw_line[6] == "-": 
                return "No PID listening on address"
            else:
                return raw_line[6].split(r"/")[0]


def main():
    ip_port = parse_arguments()

    print address_to_pid(ip_port)



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

回答 3

Code Review用户

回答已采纳

发布于 2015-07-28 20:55:55

首先,有几个简单的提示:

  • 遵循PEP8样式指南
  • 不要使用x == False,而是使用not x,例如在if (is_legal_ip(args.ip_port[0]) == False)
  • 省略不必要的导入(本例中为import socket)
  • 使用运算符周围的空格,例如all(0 <= int(p) < 256 for p in pieces)
  • 冒号后面的断线,例如在if __name__ == "__main__": main()

将参数解析的工作留给argparse

无需亲自与sys.args合作。例如,您可以简单地删除这些行:

如果len(sys.argv) >2或len(sys.argv) == 1: print(“请提供一个参数”)。出口(1)

按照您使用nargs=1指定ip端口参数的方式,argparse将负责验证命令行参数的数量。

简化了参数解析

你可以省略nargs=1。但是在这种情况下,args.ip_port的值将不是一个列表,而是一个简单的字符串。

您也可以省略type=str,因为这是默认的。

您的使用将变得更简单:

代码语言:javascript
复制
parser.add_argument('ip_port', metavar='IP:PORT', help='local address')
args = parser.parse_args()

if not is_legal_ip(args.ip_port):
    parser.error("Invalid IP address.")

return args.ip_port

使用两个参数而不是一个

使用ip:port是很尴尬的,多个地方的分裂只是噪音。使用两个参数而不是一个参数将更简单、更简洁:

代码语言:javascript
复制
parser.add_argument('ip', help='local address')
parser.add_argument('port', type=int, help='local port')
args = parser.parse_args()

if not is_legal_ip(args.ip):
    parser.error("Invalid IP address.")

return args.ip, args.port

请注意,通过这种方式,argparse可以负责验证端口号。

然后,在您的main函数中,您可以获得如下所示的ip地址和端口对:

代码语言:javascript
复制
ip, port = parse_arguments()

受益于argparse

的错误报告功能

而不是这样:

如果不是is_legal_ip(args.ip_port):打印(“无效IP地址。”)出口(1)

我建议这样做:

代码语言:javascript
复制
if not is_legal_ip(args.ip_port):
    parser.error("Invalid IP address.")

argparse将与错误消息一起打印使用帮助,并退出。

这里的一个微妙之处是,在本例中,argparse将与代码2一起退出,这在UNIX命令参数无效的情况下是常见的。

使用文件句柄

在处理文件时,请使用with

代码语言:javascript
复制
with open(os.devnull, 'w') as devnull:
    output = subprocess.check_output("netstat -nl".split(), stderr=devnull).splitlines()

这使得devnull.close()没有必要,在退出块时,Python会调用它。

票数 9
EN

Code Review用户

发布于 2015-07-28 20:59:55

风格

您有一些间距问题,例如,这一行:

代码语言:javascript
复制
return all(0<=int(p)<256 for p in pieces)

应扩大到以下方面:

代码语言:javascript
复制
return all(0 <= int(p) < 256 for p in pieces)

您也有一些不必要的括号。这一行:

代码语言:javascript
复制
return(args.ip_port[0])

应改为:

代码语言:javascript
复制
return args.ip_port[0]

这一行:

代码语言:javascript
复制
if (is_legal_ip(args.ip_port[0]) == False):

也应改为:

代码语言:javascript
复制
if is_legal_ip(args.ip_port[0]) == False:

您还应该在函数之间有两个空行,而不是一个。

您还应该阅读PEP8,这是Python的官方风格指南。

发行

您还应该使用上下文管理器来打开和关闭文件,而不是file.close。例如,这段代码:

代码语言:javascript
复制
devnull = open(os.devnull, 'w')
output = subprocess.check_output("netstat -nptl".split(), stderr = devnull).splitlines()
devnull.close()

应改为:

代码语言:javascript
复制
with open(os.devnull, 'w') as devnull:
    output = subprocess.check_output("netstat -nl".split(), stderr=devnull).splitlines()

杂项挑剔

这段代码:

代码语言:javascript
复制
for line in output[2::]:
    raw_line = line.split()
    if raw_line[3] == ip_port:
        if raw_line[6] == "-": 
            return "No PID listening on address"
        else:
            return raw_line[6].split(r"/")[0]

可缩短为:

代码语言:javascript
复制
for line in output[2::]:
    raw_line = line.split()
    if raw_line[3] == ip_port and if raw_line[6] == "-": 
        return "No PID listening on address"
    return raw_line[6].split(r"/")[0]

这一条件:

代码语言:javascript
复制
if len(sys.argv) > 2 or len(sys.argv) == 1:

也可缩短为:

代码语言:javascript
复制
if len(sys.argv) >= 1:

此外

票数 3
EN

Code Review用户

发布于 2015-07-31 14:23:38

您可以尝试类似于上述建议的pep8,并利用Python中的一些内置内容。

注意:这假设您希望查找特定的IPv4 NetAddr,而不仅仅是侦听内容。例如,下面的代码将不使用IPv6地址(没有mods),也不会捕获监听*(例如SSH)的内容。这也是python3友好的(尽管您可能需要调整psutil的一些东西才能使它与python3一起工作,但是python3语句已经准备好了。)

代码语言:javascript
复制
'''
my program does cool stuff
'''
import argparse
import psutil
from ipaddress import IPv4Network, AddressValueError



def is_legal_ip(address):
    '''
    determine if IP is valid
    '''
    address = address[0] 
    try:
        if IPv4Network(unicode(address)):
            pass
    except AddressValueError as valueerr:
        print('invalid IP provided: {} : {}'.format(address, valueerr))
        return False
    return True

def parse_arguments():
    '''
    parse the command line arguments
    '''
    parser = argparse.ArgumentParser('Returns a PID listenning on IP:PORT')
    parser.add_argument('ip_address',
                        type=str,
                        nargs=1,
                        help='local address')
    parser.add_argument('ip_port',
                        type=int,
                        nargs=1,
                        help='local port')
    args = parser.parse_args()
    if not is_legal_ip(args.ip_address):
        parser.error('Invalid IP address : {}'.format(args.ip_address))

    return(args.ip_address, args.ip_port)

def address_to_pid(ip_address='', ip_port=0):
    '''
    determine pid based on IP and port combo
    leverage psutil library and net_connections method
    return the pid of the listening binary
    '''
    possible_pids = set()
    [x.pid for x in psutil.net_connections() if x.laddr == (
        str(ip_address), ip_port) and x.pid not in possible_pids\
                and possible_pids.add(x.pid)]
    if len(possible_pids) < 1:
        return 'Nothing'
    return possible_pids.pop()


def main():
    '''
    a small program to get the PID listening on an IP/port combo
    '''
    ip_address, ip_port = parse_arguments()
    actual_pid = address_to_pid(ip_address[0], ip_port[0])
    print('{} is listening on {} port {}'.format(actual_pid,
                                                 ip_address,
                                                 ip_port))


if __name__ == '__main__':
    main()
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/98377

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档