【问题标题】:Iterate through linux sort output in python遍历python中的linux排序输出
【发布时间】:2020-10-05 04:27:47
【问题描述】:

我无法找到使用 linux sort 命令作为我的 python 脚本输入的解决方案。

例如我想遍历sort -mk1 <(cat file1.txt) <(cat file2.txt))的结果

通常我会使用Popen 并使用nextstdout.readline() 遍历它,类似于:

import os
import subprocess

class Reader():
    def __init__(self):
        self.proc = subprocess.Popen(['sort -mk1', '<(', 'cat file1.txt', ')', '<(', 'cat file2.txt', ')'], stdout=subprocess.PIPE)

    def __iter__(self):
        return self

    def __next__(self):
        while True:
            line = self.proc.stdout.readline()
            if not line:
                raise StopIteration
            return line


p = Reader()
for line in p:
    # only print certain lines based on some filter 

使用上面的,我会得到一个错误:No such file or directory: 'sort -mk1'

经过一番研究,我想我不能使用 Popen,必须使用os.execl 才能使用bin/bash

所以现在我尝试如下:

import os
import subprocess

class Reader():
    def __init__(self):
        self.proc = os.execl('/bin/bash', '/bin/bash', '-c', 'set -o pipefail; sort -mk1 <(cat file1.txt) <(cat file2.txt)')

    def __iter__(self):
        return self

    def __next__(self):
        while True:
            line = self.proc.stdout.readline()
            if not line:
                raise StopIteration
            return line


p = Reader()
for line in p:
    # only print certain lines based on some filter 

问题在于它实际上会立即打印所有行。我想一种解决方案是将其结果通过管道传输到一个文件中,然后在 python 中我遍历该文件。但我真的不想将它保存到文件然后过滤它,似乎没有必要。是的,我可以使用其他 linux 命令,例如 awk,但我想使用 python 进行进一步处理。

所以问题是:

  1. 有没有办法让Popen 的解决方案起作用?
  2. 如何使用第二种解决方案遍历sort 的输出?

【问题讨论】:

  • Process Subtituion (&lt;( command )) 是 bash 提供的东西(运行命令,创建一个 FIFO 并将其替换为 FIFO 的名称)。如果您将这些作为参数提供给sort,它将无法执行您想要的操作(很可能sort 会将&lt;() 视为文件名)。为什么你不能简单地做sort -mk filename1.txt filename2.txt
  • 对于您的第二种情况,使用 os.exec* 将替换整个过程,因此它不会继续您的 python 脚本中的下一条语句,因此处理输出没有意义。还没有尝试过,但为什么不能像第二个示例一样使用 Popen 来生成运行 bash 的进程?
  • 我想我不确定如何使用 Popen 来生成运行中的 bash

标签: python linux subprocess


【解决方案1】:

如果你想使用 shell 功能,你必须使用shell=True。如果你想使用 Bash 功能,你必须确保你运行的 shell 是 Bash。

        self.proc = subprocess.Popen(
            'sort -mk1 <(cat file1.txt) <(cat file2.txt)',
            stdout=subprocess.PIPE,
            shell=True,
            executable='/bin/bash')

注意shell=True 和朋友的第一个参数是如何使用单个字符串的(反之亦然;如果您没有shell=True,则必须自己将命令行解析为令牌)。

当然,cats are useless,但如果您将它们替换为 shell 可以轻松优雅地执行并且您无法轻松地用本机 Python 代码替换的东西,这可能是要走的路。

简而言之,&lt;(command) 是一个 Bash 进程替代; shell 将在子进程中运行command,并将参数替换为进程生成其输出的打开文件句柄的设备名称。所以sort 会看到类似

sort -mk /dev/fd/63 /dev/fd/64

其中/dev/fd/63 是第一个命令输出可用的管道,/dev/fd/64 是另一个命令标准输出的读取端。

【讨论】:

【解决方案2】:

你的脚本有很多问题。

首先,您的Popen 无法使用,原因如下:

  1. 第一个参数应该是要运行的命令,并且您传递了sort -mk 并且没有这样的文件。您应该简单地传递sort,并将-mk 作为参数传递。
  2. Process substituion &lt;( command ) 是由 shell 处理的东西,它正在执行类似 运行命令、创建 FIFO 并将其替换为 FIFO 名称的操作。将这些直接传递给sort 是行不通的。 sort 可能只会将 &lt;( 视为文件名。

您使用os.exec* 的第二种方式也行不通,因为os.exec* 将替换您当前的进程。因此它永远不会继续你的 Python 脚本中的下一条语句。

在您的情况下,似乎没有理由使用进程替换。为什么你不能简单地做一些类似subprocess.Popen(['sort', '-mk', 'filename1', 'filename2']) 的事情?

【讨论】:

  • 可以说我必须使用进程替换,因为它不仅仅是读取文件。我只是在这里这样做是为了简化问题。实际上,它获取一个 zip 文件,解压缩它并执行其他过程。
【解决方案3】:

我不明白你为什么要做sort -mk1 $(cat file),sort 可以对文件进行操作。看看check_output。这会让你的生活变得简单

output=subprocess.check_output('ls')
for line in output:
    print(line)

当然,您必须处理异常,手册页有详细信息

【讨论】:

  • 我不认为我可以在排序上使用 check_output,至少我试过了,它也不起作用
  • ofcource 你可以我只是尝试了这个` out =check_output(['sort', '/etc/resolv.conf']).splitlines()` 然后打印它,就像一个魅力
  • 在你的例子中它会,但我需要使用进程替换。是的,在这个问题上它没有意义,但我只是这样做是为了简化问题。但我必须使用进程替换,而 check_output 无法使用
  • 当然会,在与Popen 相同的情况下。你必须使用shell=True executable='/bin/bash'。有关详细信息,请参阅我的答案。 check_output 的问题在于它会在子进程退出后一次性返回所有行,而不是让您在生成时一次读取一行。
猜你喜欢
  • 2012-10-28
  • 2011-03-01
  • 2013-05-18
  • 1970-01-01
  • 2023-01-14
  • 2014-02-06
  • 1970-01-01
  • 1970-01-01
  • 2015-09-27
相关资源
最近更新 更多