【问题标题】:Split a variable table in python在python中拆分变量表
【发布时间】:2013-12-19 10:29:39
【问题描述】:

在调用 lsof 之后,我正在寻找分割每一行以获取表格每个单元格的字符串的通用方法,但问题来了,因为每次调用该命令时,每列的大小都会发生变化。

COMMAND     PID       USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
init          1       root  cwd       DIR                8,1      4096          2 /
kthreadd      2       root  txt   unknown                                         /proc/2/exe
kjournald    42       root  txt   unknown                                         /proc/42/exe
udevd        77       root  cwd       DIR                8,1      4096          2 /
udevd        77       root  txt       REG                8,1    133176     139359 /sbin/udevd
flush-8:1 26221       root  cwd       DIR                8,1      4096          2 /
flush-8:1 26221       root  rtd       DIR                8,1      4096          2 /
flush-8:1 26221       root  txt   unknown                                         /proc/26221/exe
sudo      26228       root    5u     unix 0xfff999002579d3c0       0t0     515611 socket
python    30077       root    2u      CHR                1,3       0t0        700 /dev/null

【问题讨论】:

  • 啊...这就是your previous question 试图解决的真正问题?
  • @JonClements 完全正确 :)
  • 命令名称中可能有空格,因此仅使用.split 是不安全的。也许您可以使用标题来发现字段宽度。
  • @gnibbler 你是对的。我更新了我的答案来处理这个问题

标签: python linux split command output


【解决方案1】:

不用解析lsof命令输出,而是安装psutil模块——它还有跨平台的优势。

import psutil

def get_all_files():
    files = set()
    for proc in psutil.process_iter():
        try:
            files.update(proc.get_open_files())
        except Exception: # probably don't have permission to get the files
            pass
    return files

print get_all_files()
# set([openfile(path='/opt/google/chrome/locales/en-GB.pak', fd=28), openfile(path='/home/jon/.config/google-chrome/Default/Session Storage/000789.log', fd=95), openfile(path='/proc/2414/mounts', fd=8) ... ]

然后您可以对其进行调整以包含父进程和其他信息,例如:

导入psutil

for proc in psutil.process_iter():
    try:
        fids = proc.get_open_files()
    except Exception:
        continue
    for fid in fids:
        #print dir(proc)
        print proc.name, proc.pid, proc.username, fid.path

#gnome-settings-daemon 2147 jon /proc/2147/mounts
#pulseaudio 2155 jon /home/jon/.config/pulse/2f6a9045c2bc8db6bf32b2d7517969bf-device-volumes.tdb
#pulseaudio 2155 jon /home/jon/.config/pulse/2f6a9045c2bc8db6bf32b2d7517969bf-stream-volumes.tdb

【讨论】:

  • 当我看到 psutil 返回进程打开的常规文件时,我希望在系统中打开所有文件。
  • @JohnSnow 好的...但是在我的机器上运行 lsof 返回 26,005 行,其中,一个 load 都是权限被拒绝和其他消息...至少以上将其从程序有权访问的进程中过滤为常规文件(如果需要,您也可以检索网络资源)...
  • 我的想法只是像root一样运行,所以权限应该没有问题。
【解决方案2】:

您知道列标签右对齐,第一个和最后一个除外。因此,您可以从列标签的末尾提取列边界(相当于:从相邻列标签之间的空白的开头)。

import re
# assuming input_file to be a file-like object
header = input_file.next()

borders = [match.start() for match in re.finditer(r'\s+', header)]
second_to_third_border = borders[1]
borders = borders[1:-1] # delete the first and last because not right-aligned

for line in input_file:
    first_to_second_border = line[:second_to_third_border].rfind(' ')
    actual_borders = [0, first_to_second_border] + borders + [len(line)]
    dset = []
    for (s, e) in zip(actual_borders[:-1], actual_borders[1:]):
        dset.append(line[s:e].strip())
    print dset

关于第一列:
您可以搜索每行第一列和第二列之间的边界。从第二列和第三列之间的边界向后搜索空白。 您应该倒退,因为正如上面的 cmets 所述,该命令可能包含空格 - PID 肯定不是这样。

关于最后一栏:
该列从倒数第二个和最后一个之间的边界延伸到给定行的末尾。

示例:

from StringIO import StringIO

input_file = StringIO('''\
COMMAND     PID       USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
init          1       root  cwd       DIR                8,1      4096          2 /
kthreadd      2       root  txt   unknown                                         /proc/2/exe
kjournald    42       root  txt   unknown                                         /proc/42/exe
''')

打印

['init', '1', 'root', 'cwd', 'DIR', '8,1', '4096', '2', '/']
['kthreadd', '2', 'root', 'txt', 'unknown', '', '', '', '/proc/2/exe']
['kjournald', '42', 'root', 'txt', 'unknown', '', '', '', '/proc/42/exe']

【讨论】:

    【解决方案3】:

    这个呢:

    import fileinput
    
    for line in fileinput.input():
        print(line.split())
    

    你可以这样试试:

    lsof | python your_script.py
    

    解决“NAME 中的空格问题”

    为了解决 cmets 中提到的 NAME 列中可能存在空格的问题,我可以提出以下解决方案。这是基于我保持简单的愿望以及只有最后一列可以有空格的事实。

    算法很简单: 1.找到最后一列开始的位置——我用标题NAME的起始位置 2. 剪掉那个位置之后的那行> 你刚才剪的是NAME列的值 3. split() 该行的其余部分。

    代码如下:

    import fileinput
    
    header_limits = dict()
    records = list()
    input = fileinput.input()
    
    header_line = None
    for line in input:
        if not header_line:
            header_line = line
            col_names = header_line.split()
            for col_name in col_names:
                header_limits[col_name] = header_line.find(col_name)
            continue
        else:
            record = dict()
            record['NAME'] = line[header_limits['NAME']:].strip()
            line = line[:header_limits['NAME'] - 1]
            record.update(zip(col_names, line.split()))
            records.append(record)
    
    for record in records:
        print "%s\n" % repr(record)
    

    结果是一个字典列表。每个字典对应 lsof 输出的一行。

    这是一项有趣的任务,展示了 python 在日常任务中的强大功能。

    无论如何,如果可能的话,我更喜欢使用一些 python 库作为建议的 psutils

    【讨论】:

      猜你喜欢
      • 2017-08-02
      • 2015-08-29
      • 2015-09-22
      • 1970-01-01
      • 2017-02-25
      • 2013-02-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多