标签归档:python

shell 中格式化显示 json 字符串

有时需要在终端环境中查看 json 数据,比如使用 curl 调试接口时。直接看到的 json 数据是类似这样的:

$ echo '{"foo": "lorem", "bar": "ipsum"}'
{"foo": "lorem", "bar": "ipsum"}

如果想要以更直观的方式格式化显示 json 数据,可以利用 python3 提供的标准库 json 来实现:

$ echo '{"foo": "lorem", "bar": "ipsum"}' | python3 -m json.tool
{
    "foo": "lorem",
    "bar": "ipsum"
}

为了更方便地使用这一工具,可以为它设置一个别名:

将下面内容写入 ~/.bashrc 或其他您的 shell 配置文件中:

alias pjson='python3 -m json.tool'

执行 source ~/.bashrc

之后在该 shell 下就可以这样用了:

$ echo '{"foo": "lorem", "bar": "ipsum"}' | pjson
{
    "foo": "lorem",
    "bar": "ipsum"
}

参考文献

Python 传值 or 传引用

先说结论:python不允许程序员选择采用传值还是传引用。

  • 如果函数收到的是一个 可变对象 (比如 字典 或者 列表 )的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。
  • 如果函数收到的是一个 不可变对象 (比如 数字字符 或者 元组 )的引用,就不能直接修改原始对象--相当于通过“传值‘来传递对象。

在函数传值,或函数内部函数引用外部变量时,基本符合这个逻辑,比如:

def test_dect_out():
    lvs = {}
    num1 = 3

    def lan():
        lvs['sss'] = 4
        num1 = 5
        return lvs, num1

    print(lvs, num1)
    print(lan())
    print(lvs, num1)

{} 3
({'sss': 4}, 5)
{'sss': 4} 3

参考文献

Python 二进制结构化数据处理和封装

当 python 需要调用 C 程序,或是进行文件、网络操作时,需要对二进制结构化字节流进行处理,此时需要使用到 struct 这个模块提供的方法。

详细方法可以查看 官方教程,这里以 perlpack 作为对比,使用 python 实现类似 perl 数据打包的效果。

在 perl 的 pack 方法中,提供了一种 Z* 的写法,可以总是保证最后有一位空填充,在 python 中则可以这样实现:

# 类比 perl 的 pack "VVVVZ*", $max, 0, 0, 0, $user;
fmt = "<4lx" + str(len(user) + 1) + "s"  # 类比 perl Z*,总是保证最后有一位空填充
bindata = struct.pack(fmt, maxn, 0, 0, 0, bytes(user, encoding="utf8"))
# 小端序,4个 long (32位整) 后面跟 填充字节 ,然后再拼 字符长度 +1 个 s(字节串)

最后打印出来的效果是这样:

b'tasklist\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00dev\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

如果直接使用 .format 或是字符拼接 + `.ljust(256, '\000') 之类的方法在后面强行补 也是可以实现类似的效果,但是打印出来还是字符对象,不是我希望的字节流对象,而且很繁琐,不专业:

'tasklist                                                                                                                                                                                                                                                        dev                                                                                                                                                                                                                                                             '

大概就是这样,像是中间塞了一堆空格。建议数据打包还是使用 struct.pack 来进行。

基本实现需求。

参考文献

Python 实战项目解决循环依赖问题

问题重现

在一次简单的代码合并后,发现 Python 项目跑不起来了,报错如下:

ssh://root@192.168.226.76:22/usr/bin/python3 -B -u /tmp/pycharm_project_882/mimic_daemon_server/helper_main.py
Traceback (most recent call last):
  File "/tmp/pycharm_project_882/mimic_daemon_server/helper_main.py", line 1, in 
<module>
    from mimic_daemon_server import create_app
  File "/tmp/pycharm_project_882/mimic_daemon_server/__init__.py", line 14, in 
<module>
    from mimic_daemon_server.nodes import nodes
  File "/tmp/pycharm_project_882/mimic_daemon_server/nodes/__init__.py", line 2, in 
<module>
    from . import route
  File "/tmp/pycharm_project_882/mimic_daemon_server/nodes/route.py", line 5, in 
<module>
    from mimic_daemon_server.guest_common import ReplicationConfig
  File "/tmp/pycharm_project_882/mimic_daemon_server/guest_common/__init__.py", line 3, in 
<module>
    from .abstract_config import AbstractConfig
  File "/tmp/pycharm_project_882/mimic_daemon_server/guest_common/abstract_config.py", line 16, in 
<module>
    import mimic_daemon_server.qemu_server.Drive
  File "/tmp/pycharm_project_882/mimic_daemon_server/qemu_server/__init__.py", line 25, in 
<module>
    from .QemuConfig import QemuConfig
  File "/tmp/pycharm_project_882/mimic_daemon_server/qemu_server/QemuConfig.py", line 12, in 
<module>
    from mimic_daemon_server.guest_common import AbstractConfig
ImportError: cannot import name 'AbstractConfig' from 'mimic_daemon_server.guest_common' (/tmp/pycharm_project_882/mimic_daemon_server/guest_common/__init__.py)

进程已结束,退出代码1

由于我在 Python 领域还是个初学者,没有遇到类似问题,但是根据分析,问题应该是出在了 循环依赖

a------->b-------->c
         ^         |
         |         |
         +----d<---+

报错分析

何为循环依赖呢?首先要搞清楚一点,python模块是天然的单例类,就是说第一次导入时会产生一个实例,后面再导入都会直接返回该实例。

模块的初始化时被导入时进行的,就是说每一次 import 都会执行 __init__.py 内的代码。

一旦 Python 的模块导入规划不合理,造成了多个互相依赖的模块均在第一次进入时开始实例化,就会产生 循环依赖 问题。

总结一下,Python 模块是可以循环依赖的,但是必须避免循环实例化

解决方法

解决循环依赖的方法也有很多:

(1)打破循环实例化态

如果想快速解决,在报错日志中找到循环实例化的两个模块,打破其循环实例化的状态即可。

例如观察文首的一段日志,会很明显的发现调用栈中前后引入了同一个模块,很容易定位到问题。

(2) 强制指定实例化顺序

可以在整个项目的根模块中的 __init__.py 中将所有子模块依赖一遍,调整可能出现循环依赖的顺序,就可以避免后期模块间依赖时产生不可预知的循环依赖问题。

也可以在模块中提供一个 init 方法,在需要的地方手动控制实例化顺序,避免导入时意外的循环依赖。

总结

Python 模块间可以随意引用,但是必须注意实例化的前后顺序,否则就会出现这种循环实例化问题,让人摸不着头脑。

参考文献

Python3下基于Scapy库完成网卡抓包解析

Scapy是一个可以让用户发送、侦听和解析并伪装网络报文的Python程序。这些功能可以用于制作侦测、扫描和攻击网络的工具。

Python 代码中可以通过 sniff 函数调用抓包分析,并对抓到的包进行回调操作。

Sniff 方法定义:

sniff(count=0,
      store=1,
      offline=None,
      prn=None,
      filter=None,
      L2socket=None,
      timeout=None,
      opened_socket=None,
      stop_filter=None,
      iface=None)
count:抓取报的数量,设置为0时则一直捕获
store:保存抓取的数据包或者丢弃,1保存,0丢弃
offline:从pcap文件中读取数据包,而不进行嗅探,默认为None
prn:为每个数据包定义一个回调函数,通常使用lambda表达式来写回调函数
filter:过滤规则,可以在里面定义winreshark里面的过滤语法,使用 Berkeley Packet Filter (BPF)语法,具体参考:[http://blog.csdn.net/qwertyupoiuytr/article/details/54670477](http://blog.csdn.net/qwertyupoiuytr/article/details/54670477)
L2socket:使用给定的L2socket
timeout:在给定的事件后停止嗅探,默认为None
opened_socket:对指定的对象使用.recv进行读取
stop_filter:定义一个函数,决定在抓到指定的数据之后停止
iface:指定抓包的网卡,不指定则代表所有网卡

filter语法

type(定义了类型)
可选值:host, net, port, portrange
例如:
host hostnameA
net 172.31            //相当于172.31.0.0/16,又例如:192.168.1相当于192.168.1.0/24
port 80
portrange 6000-6010

dir(direction,定义了传输方向)
可选值:src, dst, src or dst, src and dst
例如:
src net 172.31
src or dst port 21

proto(protocol定义了网络协议)
可选值:ether, fddi, tr, wlan, ip, ip6, arp, rarp, decnet, tcp, udp, icmp
(fddi, tr, wlan是ether的别名, 包结构很类似)
例如:
ether src hostnameA
arp net 172.31
udp portrange 7000-8000

连接词:and, or, not
例如:
tcp or udp
not icmp

示例代码

#!/usr/bin/python3
# -*- coding: UTF-8 -*-

from scapy.all import *

def pack_callback(packet):
    print ( packet.show() )
    if packet['Ether'].payload:
        print (packet['Ether'].src)
        print (packet['Ether'].dst)
        print (packet['Ether'].type)

    if packet['ARP'].payload:
        print (packet['ARP'].psrc)
        print (packet['ARP'].pdst)
        print (packet['ARP'].hwsrc)
        print (packet['ARP'].hwdst)
    time.sleep(2)

filterstr="arp"

sniff(filter=filterstr,prn=pack_callback, iface='eth0', count=0)

参考文献