用Python实现Unix的cat命令

11

我目前正在复制以下的Unix命令:

cat command.info fort.13 > command.fort.13

使用以下Python代码:

with open('command.fort.13', 'w') as outFile:
  with open('fort.13', 'r') as fort13, open('command.info', 'r') as com:
    for line in com.read().split('\n'):
      if line.strip() != '':
        print >>outFile, line
    for line in fort13.read().split('\n'):
      if line.strip() != '':
        print >>outFile, line

这种方法是可行的,但一定有更好的方法。有什么建议吗?

编辑(2016):

这个问题四年后又开始引起关注。我在一个更长的Jupyter Notebook中写下了一些想法这里

问题的关键在于我的问题涉及到readlines的(出乎意料)行为。我原本想回答的答案可以用更好的方式提问,并且可以用read().splitlines()更好地回答。


6个回答

16

最简单的方法可能是忘掉每一行,直接读取整个文件,然后将其写入输出:

with open('command.fort.13', 'wb') as outFile:
    with open('command.info', 'rb') as com, open('fort.13', 'rb') as fort13:
        outFile.write(com.read())
        outFile.write(fort13.read())

正如评论中指出的那样,如果其中任何一个输入文件很大,这可能会导致高内存使用率(因为它首先将整个文件复制到内存中)。如果这可能是一个问题,则以下方法同样有效(通过分块复制输入文件):

import shutil
with open('command.fort.13', 'wb') as outFile:
    with open('command.info', 'rb') as com, open('fort.13', 'rb') as fort13:
        shutil.copyfileobj(com, outFile)
        shutil.copyfileobj(fort13, outFile)

8
#!/usr/bin/env python
import fileinput

for line in fileinput.input():
    print line,

使用方法:

$ python cat.py command.info fort.13 > command.fort.13

或者允许任意大的行:

#!/usr/bin/env python
import sys
from shutil import copyfileobj as copy

for filename in sys.argv[1:] or ["-"]:
    if filename == "-":
        copy(sys.stdin, sys.stdout)
    else:
        with open(filename, 'rb') as file:
            copy(file, sys.stdout)

使用方式相同。
或者在 Python 3.3 中使用 os.sendfile() 函数:
#!/usr/bin/env python3.3
import os
import sys

output_fd = sys.stdout.buffer.fileno()
for filename in sys.argv[1:]:
    with open(filename, 'rb') as file:
        while os.sendfile(output_fd, file.fileno(), None, 1 << 30) != 0:
            pass

上述的sendfile()调用是针对Linux > 2.6.33编写的。原则上,sendfile()比其他方法使用的读/写组合更有效率。

8
def cat(outfilename, *infilenames):
    with open(outfilename, 'w') as outfile:
        for infilename in infilenames:
            with open(infilename) as infile:
                for line in infile:
                    if line.strip():
                        outfile.write(line)

cat('command.fort.13', 'fort.13', 'command.info')

1
是的,如果不是因为原帖作者显然想要删除空行,我会使用更大的数据块进行操作。 - kindall
这个程序实际上复制了cat命令的连接作用。太棒了!给你加一分。 - bballdave025

1

列表推导式对于这种事情非常棒:

with open('command.fort.13', 'w') as output:
  for f in ['fort.13', 'command.info']:
    output.write(''.join([line for line in open(f).readlines() if line.strip()]))

1

遍历文件会产生行。

for line in infile:
  outfile.write(line)

如果infile是一个文件的位置,这将会将文件的位置侧向打印出来。 - Josiah

1

您可以通过几种方式来简化这个问题:

with open('command.fort.13', 'w') as outFile:
  with open('fort.13', 'r') as fort13, open('command.info', 'r') as com:
    for line in com:
      if line.strip():
        print >>outFile, line
    for line in fort13:
      if line.strip():
        print >>outFile, line

更重要的是,shutil 模块具有 copyfileobj 函数:
with open('command.fort.13', 'w') as outFile:
  with open('fort.13', 'r') as fort13:
    shutil.copyfileobj(com, outFile)
  with open('command.info', 'r') as com:
    shutil.copyfileobj(fort13, outFile)

这不会跳过空行,但cat也不会这样做,所以我不确定你是否真的想要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接