-1

我有一个 python 子进程,它 greps 文件中的字符并将结果输出到 csv 文件。我想在我的子进程中对列表中的每个项目执行相同的 grep,然后将结果输出到 csv 文件,将结果附加到先前的结果中。我有以下代码:

list = ['ABCD', 'EFGH', 'IJKL', 'MNOP', 'QRST']

    with open('output.csv','w') as out:
          out.writelines(
                        for x in list:
                            cat = subprocess.Popen(['cat', 'LogFile_20130410.msg.log'],
                                    stdout=subprocess.PIPE,)
                            grep = subprocess.Popen(['grep', x],
                                    stdin=cat.stdout,
                                    stdout=subprocess.PIPE,)
                            awk = subprocess.Popen(['awk', '{print $14,$10,$5,$7}'],
                                    stdin=grep.stdout,
                                    stdout=subprocess.PIPE,)
                            sort = subprocess.Popen(['sort', '-t ', '-k4', '-n', '-r'],
                                    stdin=awk.stdout,
                                    stdout=subprocess.PIPE,)
                            head = subprocess.Popen(['head', '-10'],
                                    stdin=sort.stdout,
                                    stdout=open('output.csv', 'w'),)
                        )

我认为我的子进程正在运行,但我的输出文件只包含列表中的最后一项。我想我正在覆盖我的数据。这是我的问题还是我的 for 语句错误?如果我的 for 语句是正确的,我如何写入 output.csv 文件而不覆盖?还有一种方法可以将我的输出写入单独的列吗?

我的输出如下所示:

QRST 39h0gcro 确认 83ms

这些字段中的每一个都应位于单独的列中。

4

1 回答 1

2

您的代码的实际问题是:

head = subprocess.Popen(['head', '-10'],
                        stdin=sort.stdout,
                        stdout=open('output.csv', 'w'),)

您每次都通过循环执行此操作。因此,每次通过循环时,您都会再次output.csv'w'模式打开,这会擦除那里的所有内容并用新的输出替换它。

最重要的是,您永远不会关闭文件。您可能会很幸运,垃圾收集器每次都会在下次打开文件之前自动关闭文件。但这并不保证会发生。如果没有,在开始下一个循环之前,您可能没有从一个循环将任何内容刷新到磁盘。

此外,您已经在with循环中始终打开同一个文件。而且您还试图out.writelines(…)围绕整个循环进行。这将引发 a SyntaxError,因为您不能将块语句用作函数参数……但是如果您将其替换为确实有效的内容,则仅意味着您有两个文件描述符争夺写入和刷新同一个文件.

您可以通过以下方式一次性解决所有这些问题:(a)with在循环外部仅打开一次文件,(b) 不尝试out.writelines()在循环周围调用,以及 (c)out作为stdout管道中的最后一个命令传递,而不是重新打开同一个文件。

或者,您不能在循环之外打开文件,每次通过循环时都以ar+模式而不是w模式打开它,并确保在循环结束之前将其关闭。但这更复杂,没有明显的好处。


无论如何,这是一个奇怪的设计。除了无用的事实之外cat,如果您真的只想将一堆 POSIX 命令连接在一起,只需使用sh而不是python

grep $x < LogFile_20130410.msg.log | 
    awk '{print $14,$10,$5,$7}' | 
    sort -t' ' -k4 -n -r |
    head -10  >> output.csv

这要简单得多。


或者,如果你想用 Python 来做,为什么不使用 Python 而不是 POSIX 命令呢?

with open('LogFile_20130410.msg.log') as f:
    grepped = [line for line in f if line.startswith(x)]
columns = [row.split() for row in grepped]
columns4 = [(row[13], row[9], row[4], row[6]) for row in columns]
columns4.sort(key=lambda row: int(row[3]), reverse=True)
top10 = columns4[:10]
with open('output.csv', 'a') as outf:
    csv.writer(outf).writerows(top10)

这并不相同,但大多数差异都无关紧要。例如,我已将代码简化为不使用正则表达式,因为根据您的示例数据,您显然不需要它。

但它仍然不适用于您的示例数据——出于同样的原因,您awk的基于 - 的代码不起作用,只是方式不同。

首先,awk视为$14“第 14 列,如果没有那么多则为空”,而 Python 则视为row[13]“第 14 列,如果没有那么多则引发异常”。鉴于您的输入数据只有 4 列,并且您要查找的列都不在前 5 列中,您awk的基于 - 的代码将打印出 10 行 4 个空列,而此 Python 代码将引发异常。如果你真的想要awk- 风格的行为:

def getcol(a, col):
    try:
        return a[col]
    except IndexError:
        return ''
columns4 = [(getcol(row, 13), getcol(row, 9), getcol(row, 4), getcol(row, 6)) for row in columns]

或者,由于该行有点笨拙,请添加另一个函数:

def getcols(a, *cols):
    return [getcol(a, col) for col in cols]
columns4 = [getcols(row, 13, 9, 4, 6) for row in columns]

然后,sort -n将空列处理为 0,但在 Python 中int('')是一个例外。因此,再次创建一个包装器:

def intify(x):
    try:
        return int(x)
    except ValueError:
        return 0

…并使用它:

columns4.sort(key=lambda row: intify(row[3]), reverse=True)

现在,Python 代码也将成功打印出 10 行空列。

于 2013-04-10T08:25:38.177 回答