apt like 列输出 - python 库

发布于 2024-08-05 00:47:04 字数 1543 浏览 7 评论 0原文

Debian 的 apt 工具以统一宽度的列输出结果。例如,尝试运行“aptitude search svn”..并且所有名称都出现在相同宽度的第一列中。

现在,如果您调整终端大小,列宽也会相应调整。

有没有一个 Python 库可以实现这一点?请注意,库必须了解终端宽度并采用表格作为输入 - 例如,可以是 [('rapidsvn', 'A GUI client for subversion'), ...] .. 并且您还可以指定第一列(或任何列)的最大宽度。另请注意,如果超过终端宽度,则如何修剪下面第二列中的字符串,从而不会引入不需要的第二行。

$ aptitude search svn
[...]
p   python-svn-dbg                    - A(nother) Python interface to Subversion (d
v   python2.5-svn                     -                                            
v   python2.6-svn                     -                                            
p   rapidsvn                          - A GUI client for subversion                
p   statsvn                           - SVN repository statistics                  
p   svn-arch-mirror                   - one-way mirroring from Subversion to Arch r
p   svn-autoreleasedeb                - Automatically release/upload debian package
p   svn-buildpackage                  - helper programs to maintain Debian packages
p   svn-load                          - An enhanced import facility for Subversion 
p   svn-workbench                     - A Workbench for Subversion                 
p   svnmailer                         - extensible Subversion commit notification t
p   websvn                            - interface for subversion repositories writt
$

编辑:(回应下面亚历克斯的回答)...输出将类似于“能力搜索”,1)只有最后一列(这是唯一具有最长字符串的列)行)将被修剪,2)通常只有 2-4 列,但最后一列(“描述”)预计至少占据终端宽度的一半。 3) 所有行包含相同数量的列,4) 所有条目仅是字符串

Debian's apt tool outputs results in uniform width columns. For instance, try running "aptitude search svn" .. and all names appear in the first column of the same width.

Now if you resize the terminal, the column width is adjusted accordingly.

Is there a Python library that enables one to do this? Note that the library has to be aware of the terminal width and take a table as input - which could be, for instance, [('rapidsvn', 'A GUI client for subversion'), ...] .. and you may also specify a max-width for the first column (or any column). Also note how the string in the second column below is trimmed if exceeds the terminal width .. thus not introducing the undesired second line.

$ aptitude search svn
[...]
p   python-svn-dbg                    - A(nother) Python interface to Subversion (d
v   python2.5-svn                     -                                            
v   python2.6-svn                     -                                            
p   rapidsvn                          - A GUI client for subversion                
p   statsvn                           - SVN repository statistics                  
p   svn-arch-mirror                   - one-way mirroring from Subversion to Arch r
p   svn-autoreleasedeb                - Automatically release/upload debian package
p   svn-buildpackage                  - helper programs to maintain Debian packages
p   svn-load                          - An enhanced import facility for Subversion 
p   svn-workbench                     - A Workbench for Subversion                 
p   svnmailer                         - extensible Subversion commit notification t
p   websvn                            - interface for subversion repositories writt
$

EDIT: (in response to Alex's answer below) ... the output will be similar to 'aptitude search' in that 1) only the last column (which is the only column with the longest string in a row) is to be trimmed, 2) there are typically 2-4 columns only, but the last column ("description") is expected to take at least half the terminal width. 3) all rows contain equal number of columns, 4) all entries are strings only

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

万水千山粽是情ミ 2024-08-12 00:47:04

更新colprint 例程现在可在 applib Python 库 托管在 GitHub

以下是感兴趣的人的完整计划:

# This function was written by Alex Martelli
# http://stackoverflow.com/questions/1396820/
def colprint(table, totwidth=None):
    """Print the table in terminal taking care of wrapping/alignment

    - `table`:    A table of strings. Elements must not be `None`
    - `totwidth`: If None, console width is used
    """
    if not table: return
    if totwidth is None:
        totwidth = find_console_width()
        totwidth -= 1 # for not printing an extra empty line on windows
    numcols = max(len(row) for row in table)
    # ensure all rows have >= numcols columns, maybe empty
    padded = [row+numcols*('',) for row in table]
    # compute col widths, including separating space (except for last one)
    widths = [ 1 + max(len(x) for x in column) for column in zip(*padded)]
    widths[-1] -= 1
    # drop or truncate columns from the right in order to fit
    while sum(widths) > totwidth:
        mustlose = sum(widths) - totwidth
        if widths[-1] <= mustlose:
            del widths[-1]
        else:
            widths[-1] -= mustlose
            break
    # and finally, the output phase!
    for row in padded:
        print(''.join([u'%*s' % (-w, i[:w])
                       for w, i in zip(widths, row)]))

def find_console_width():
    if sys.platform.startswith('win'):
        return _find_windows_console_width()
    else:
        return _find_unix_console_width()
def _find_unix_console_width():
    """Return the width of the Unix terminal

    If `stdout` is not a real terminal, return the default value (80)
    """
    import termios, fcntl, struct, sys

    # fcntl.ioctl will fail if stdout is not a tty
    if not sys.stdout.isatty():
        return 80

    s = struct.pack("HHHH", 0, 0, 0, 0)
    fd_stdout = sys.stdout.fileno()
    size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
    height, width = struct.unpack("HHHH", size)[:2]
    return width
def _find_windows_console_width():
    """Return the width of the Windows console

    If the width cannot be determined, return the default value (80)
    """
    # http://code.activestate.com/recipes/440694/
    from ctypes import windll, create_string_buffer
    STDIN, STDOUT, STDERR = -10, -11, -12

    h = windll.kernel32.GetStdHandle(STDERR)
    csbi = create_string_buffer(22)
    res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)

    if res:
        import struct
        (bufx, bufy, curx, cury, wattr,
         left, top, right, bottom,
         maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
        sizex = right - left + 1
        sizey = bottom - top + 1
    else:
        sizex, sizey = 80, 25

    return sizex

Update: The colprint routine is now available in the applib Python library hosted in GitHub.

Here's the complete program for those of you interested:

# This function was written by Alex Martelli
# http://stackoverflow.com/questions/1396820/
def colprint(table, totwidth=None):
    """Print the table in terminal taking care of wrapping/alignment

    - `table`:    A table of strings. Elements must not be `None`
    - `totwidth`: If None, console width is used
    """
    if not table: return
    if totwidth is None:
        totwidth = find_console_width()
        totwidth -= 1 # for not printing an extra empty line on windows
    numcols = max(len(row) for row in table)
    # ensure all rows have >= numcols columns, maybe empty
    padded = [row+numcols*('',) for row in table]
    # compute col widths, including separating space (except for last one)
    widths = [ 1 + max(len(x) for x in column) for column in zip(*padded)]
    widths[-1] -= 1
    # drop or truncate columns from the right in order to fit
    while sum(widths) > totwidth:
        mustlose = sum(widths) - totwidth
        if widths[-1] <= mustlose:
            del widths[-1]
        else:
            widths[-1] -= mustlose
            break
    # and finally, the output phase!
    for row in padded:
        print(''.join([u'%*s' % (-w, i[:w])
                       for w, i in zip(widths, row)]))

def find_console_width():
    if sys.platform.startswith('win'):
        return _find_windows_console_width()
    else:
        return _find_unix_console_width()
def _find_unix_console_width():
    """Return the width of the Unix terminal

    If `stdout` is not a real terminal, return the default value (80)
    """
    import termios, fcntl, struct, sys

    # fcntl.ioctl will fail if stdout is not a tty
    if not sys.stdout.isatty():
        return 80

    s = struct.pack("HHHH", 0, 0, 0, 0)
    fd_stdout = sys.stdout.fileno()
    size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
    height, width = struct.unpack("HHHH", size)[:2]
    return width
def _find_windows_console_width():
    """Return the width of the Windows console

    If the width cannot be determined, return the default value (80)
    """
    # http://code.activestate.com/recipes/440694/
    from ctypes import windll, create_string_buffer
    STDIN, STDOUT, STDERR = -10, -11, -12

    h = windll.kernel32.GetStdHandle(STDERR)
    csbi = create_string_buffer(22)
    res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)

    if res:
        import struct
        (bufx, bufy, curx, cury, wattr,
         left, top, right, bottom,
         maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
        sizex = right - left + 1
        sizey = bottom - top + 1
    else:
        sizex, sizey = 80, 25

    return sizex
狠疯拽 2024-08-12 00:47:04

我认为没有通用的跨平台方法来“获取终端的宽度”——绝对不是“查看 COLUMNS 环境变量”(请参阅​​我对问题的评论) 。在 Linux 和 Mac OS X(我期望所有现代 Unix 版本)上,

curses.wrapper(lambda _: curses.tigetnum('cols'))

返回列数;但我不知道 wcurses 在 Windows 中是否支持此功能。

一旦你确实拥有了(如果你坚持的话,来自 os.environ['COLUMNS'] ,或者通过诅咒,或者来自预言机,或者默认为 80,或者你喜欢的任何其他方式)所需的输出宽度,剩下的就是
相当可行。这是一项繁琐的工作,有很多机会出现相差一类的错误,并且很容易受到许多您没有完全弄清楚的详细规范的影响,例如:哪一列被剪切以避免换行 - 它总是最后一张,还是……?为什么你在示例输出中显示 3 列,而根据你的问题,只传递了两列......?如果并非所有行都具有相同的列数,会发生什么情况?表中的所有条目都必须是字符串吗?还有许许多多类似的其他谜团。

因此,对您未表达的所有规格进行有点武断的猜测,一种方法可能是这样的......:

import sys

def colprint(totwidth, table):
  numcols = max(len(row) for row in table)
  # ensure all rows have >= numcols columns, maybe empty
  padded = [row+numcols*('',) for row in table]
  # compute col widths, including separating space (except for last one)
  widths = [ 1 + max(len(x) for x in column) for column in zip(*padded)]
  widths[-1] -= 1
  # drop or truncate columns from the right in order to fit
  while sum(widths) > totwidth:
    mustlose = sum(widths) - totwidth
    if widths[-1] <= mustlose:
      del widths[-1]
    else:
      widths[-1] -= mustlose
      break
  # and finally, the output phase!
  for row in padded:
    for w, i in zip(widths, row):
      sys.stdout.write('%*s' % (-w, i[:w]))
    sys.stdout.write('\n')

I don't think there's a general, cross-platform way to "get the width of the terminal" -- most definitely NOT "look at the COLUMNS environment variable" (see my comment on the question). On Linux and Mac OS X (and I expect all modern Unix versions),

curses.wrapper(lambda _: curses.tigetnum('cols'))

returns the number of columns; but I don't know if wcurses supports this in Windows.

Once you do have (from os.environ['COLUMNS'] if you insist, or via curses, or from an oracle, or defaulted to 80, or any other way you like) the desired output width, the rest is
quite feasible. It's finnicky work, with many chances for off-by-one kinds of errors, and very vulnerable to a lot of detailed specs that you don't make entirely clear, such as: which column gets cut to avoid wrapping -- it it always the last one, or...? How come you're showing 3 columns in the sample output when according to your question only two are passed in...? what is supposed to happen if not all rows have the same number of columns? must all entries in table be strings? and many, many other mysteries of this ilk.

So, taking somewhat-arbitrary guesses for all the specs that you don't express, one approach might be something like...:

import sys

def colprint(totwidth, table):
  numcols = max(len(row) for row in table)
  # ensure all rows have >= numcols columns, maybe empty
  padded = [row+numcols*('',) for row in table]
  # compute col widths, including separating space (except for last one)
  widths = [ 1 + max(len(x) for x in column) for column in zip(*padded)]
  widths[-1] -= 1
  # drop or truncate columns from the right in order to fit
  while sum(widths) > totwidth:
    mustlose = sum(widths) - totwidth
    if widths[-1] <= mustlose:
      del widths[-1]
    else:
      widths[-1] -= mustlose
      break
  # and finally, the output phase!
  for row in padded:
    for w, i in zip(widths, row):
      sys.stdout.write('%*s' % (-w, i[:w]))
    sys.stdout.write('\n')
春庭雪 2024-08-12 00:47:04

好吧, aptitude 使用 cwidget 来格式化纯文本中的列展示。您可以调用 cwidget 为它编写一个 python 扩展,但我认为这不值得麻烦...您可以使用您首选的方法来获取字符中的实际水平尺寸并自行计算。

Well, aptitude uses cwidget to format the columns in the text-only display. You could call into cwidget writing a python extension for it, but I don't think it is worth the trouble... You can use your preferred method of getting the actual horizontal size in chars and calculate yourself.

み零 2024-08-12 00:47:04

首先,使用 ioctl 获取 TTY 的大小:

import termios, fcntl, struct, sys

def get_tty_size():
    s = struct.pack("HHHH", 0, 0, 0, 0)
    fd_stdout = sys.stdout.fileno()
    size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
    return struct.unpack("HHHH", size)[:2]

print get_tty_size()

然后使用类似这样的函数来创建列:

pad = lambda s, n=20: "%s%s" % (s,' '*(n-len(s)))

将它们放在一起,您就可以调整控制台的列大小了!

First, use ioctl to get the size of the TTY:

import termios, fcntl, struct, sys

def get_tty_size():
    s = struct.pack("HHHH", 0, 0, 0, 0)
    fd_stdout = sys.stdout.fileno()
    size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
    return struct.unpack("HHHH", size)[:2]

print get_tty_size()

Then use a function like this to make columns:

pad = lambda s, n=20: "%s%s" % (s,' '*(n-len(s)))

Put those together and you've got resizing columns for the console!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文