【问题标题】:Paging output from pythonpython的分页输出
【发布时间】:2011-07-18 04:34:50
【问题描述】:

我正在尝试实现类似于git log 的东西,它只会在日志具有一定长度时分页输出。如果你不熟悉 git,我实际上是在尝试实现这一点:

python some_script.py | less

在 python2.6/pydoc.py 中的分页实现的一些帮助下,我能够想出这个:

import os
text = '...some text...'
pipe = os.popen('less', 'w')
pipe.write(text)
pipe.close()

效果很好,但不推荐使用 os.popen() 。我考虑过写入临时文件并使用其路径调用 less,但这似乎并不理想。这可能与子流程有关吗?还有其他想法吗?

编辑:

所以我已经让子流程工作了。我可以用Popen.communicate(text) 给它文本变量,但因为我真的想重定向打印语句,所以我决定这样做:

  import os, sys, subprocess, tempfile

  page = True
  if page:
      path = tempfile.mkstemp()[1]
      tmp_file = open(path, 'a')
      sys.stdout = tmp_file
  print '...some text...'
  if page:
      tmp_file.flush()
      tmp_file.close()
      p = subprocess.Popen(['less', path], stdin=subprocess.PIPE)
      p.communicate()
      sys.stdout = sys.__stdout__     

当然,我最终会将它包装成函数。有没有人觉得这有问题?

【问题讨论】:

  • 几点说明:(1)临时文件名是唯一的:打开方式应该是'w',而不是'a'(不可能追加到文件中)。 (2) 阅读前无需close()文件。 (3)不需要和pager进程通信(一个简单的subprocess.call()就够了)。 (4) 更明确的是不篡改像sys.stdout这样的全局;除非你真的需要这样做(比如如果你想重定向你使用的所有子模块的输出),最好显式调用一个特殊的打印函数。
  • 这是一个类似的主题,简洁明了 答案:stackoverflow.com/questions/37584717/…

标签: python


【解决方案1】:

这个怎么样:

import pydoc
text = '... some text ... '
pydoc.pager(text)

这(在我的 opensuse linux 机器上)将文本发送到寻呼机(在我的情况下为“less”),其工作方式与在 Python 解释器中调用“help(... python command...)”相同。

【讨论】:

  • 虽然我怀疑此功能将来不会可用,但很遗憾it is not documented,因此原则上不能完全保证它会保持可用。不过,考虑到它的便利性,我想这是一个通常应该值得的风险。此外,source code 看起来无论如何都可以轻松复制。
  • 尝试打印格式化的 json 数据时,它会破坏我的输出。
  • 6 年后,这种方法仍然适用于 Python 3.7.2 ;-)
【解决方案2】:

在你的代码中明确是个好主意,这样它表明你使用了一个特殊的打印函数printc()而不是标准的。使用subprocess.call() 也足够了(您不需要管道机械)。此外,您可以通过不存储临时文件的名称来保存变量:

from __future__ import print_function

import subprocess, tempfile

page = True  # For tests

# Definition of a printc() function that prints to the correct output
if page:
    tmp_file = open(tempfile.mkstemp()[1], 'w')  # No need to store the name in a specific variable
    def printc(*largs, **kwargs):
        if 'file' not in kwargs:  # The code can still use the usual file argument of print()
            kwargs['file'] = tmp_file  # Forces the output to go to the temp file
        print(*largs, **kwargs)
else:
    printc = print  # Regular print

# Main program:

printc('...some text...', 'some more text', sep='/')  # Python3 syntax

# Paging of the current contents of the temp file:
if page:
    tmp_file.flush()  # No need to close the file: you can keep printing to it
    subprocess.call(['less', tmp_file.name])  # Simpler than a full Popen()

通过这种方式,您可以获得 Python 3 的 print 函数的灵活性,并且代码明确表明您正在做一些花哨的打印工作。这比在代码的某些位置修改“全局”sys.stdout 变量更适合大型程序。

【讨论】:

  • 感谢您的建议!我已经添加了您的优化。我确实需要重定向子模块输出,就像您在评论中提到的那样,所以我似乎坚持使用 sys.stdout 方法。我已经把这一切都放到了一个类中,所以可以用pager = Pager()pager.begin() 调用它。
【解决方案3】:

【讨论】:

  • 是的,这是我尝试的第一件事。起初无法让它工作,但现在可以了。谢谢。
  • 当然。我给了你。如果您有任何建议,我希望能提供一些更通用的实施建议——请参阅我的编辑。
  • 希望我能为您提供更多帮助,但我现在在 Windows 上,这些都不适合我,即使提供的 MSYS 的完整路径提供的更少。
  • @eryksun Less 对我来说很好用,MYS bin 目录也在我的路径中;只是上面的 python 脚本都不适合我。它们对你有用吗?
【解决方案4】:

我不喜欢执行外部命令,所以我用纯 Python 写了pager。它仍然有一个问题 - 管道输入仅适用于 Windows。

【讨论】:

【解决方案5】:

需要注意的是,这对我来说是一种将进程输出重定向到 Linux 上的寻呼机的方法(我没有方便的 Windows 在那里进行测试),无论出于何种原因,将其全部写入文件或StringIO,然后一次性将其提供给寻呼机:

import os, sys

less = None
if os.isatty(sys.stdout.fileno()):
    less = subprocess.Popen(
        ['less', '-R', '--quit-if-one-screen'],
        stdin=subprocess.PIPE)
    os.dup2(less.stdin.fileno(), sys.stdout.fileno())

现在需要注意的是:

less 的行为就像 -E 已通过并在我到达输出底部时终止。我认为这是因为,作为脚本的子脚本,当脚本运行时它正在死去,但是将以下内容添加到脚本的末尾只会导致 less 挂起,否则它会退出并且我没有时间找出原因:

if less:
    less.stdin.close()  # no different with or without
    less.communicate()  # no different with less.wait()

(当我第一次想到这个 hack 时,这不是问题,因为我创建它是为了让 PyGTK 2.x 程序将其输出通过管道传输到 grep 以解决 PyGTK 不暴露来自 GTK+ C API 所需的函数使一些虚假的日志消息静音。)

【讨论】:

  • 同时执行 os.close(less.stdin.fileno()) 和 os.close(sys.stdout.fileno()) 似乎有效
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多