0

我有一个非常非常长的文件。单行将不适合内存。我需要分别处理每一行,所以我想将每一行写入一个 FIFO 节点,在行之间发送 EOF。这个模型适用于我正在做的事情,并且是理想的。

这些行不能全部存储在 RAM 中,因此read内置不是一个选项。无论我使用什么命令,都需要在读取数 GB 源文件时直接(无缓冲)写入 FIFO。

我怎样才能做到这一点,最好使用纯 bash 和基本的 Linux 实用程序,而无需安装任何特殊程序?我已经尝试过类似的东西sed -u 'p',但不幸的是,我似乎无法获得任何常见的程序来在行之间发送 EOF。

bash 版本:

GNU bash, version 4.2.45(1)-release (x86_64-pc-linux-gnu)

更新

我想避免从文件中读取“行号 X”的实用程序/技巧。该文件有数千行很长的行;重复评估相同的换行符会花费很长时间。为了在合理的时间范围内完成此操作,任何正在读取行的程序都需要按顺序读取每一行,保存其先前的位置,就像read+ 管道一样。

4

3 回答 3

3

让我们考虑一下“不适合内存的散列行”这个问题,因为这是一个简单的问题,只需几行 Python 就可以解决。

import sys
import hashlib

def hash_lines(hashname, file):
    hash = hashlib.new(hashname)
    while True:
        data = file.read(1024 * 1024)
        if not data:
            break
        while True:
            i = data.find('\n')
            if i >= 0:
                hash.update(data[:i]) # See note
                yield hash.hexdigest()
                data = data[i+1:]
                hash = hashlib.new(hashname)
            else:
                hash.update(data)
                break
    yield hash.hexdigest()

for h in hash_lines('md5', sys.stdin):
    print h

这有点古怪,因为大多数语言对不适合内存的对象没有良好的抽象(Haskell 是一个例外;这可能是 Haskell 的四行代码)。

注意:i+1如果要在散列中包含换行符,请使用。

哈斯克尔版本

Haskell 版本就像一个管道(从右到左阅读)。Haskell 支持惰性 IO,这意味着它只在必要时读取输入,因此整行不需要一次在内存中。

更现代的版本将使用管道而不是惰性 IO。

module Main (main) where
import Crypto.Hash.MD5
import Data.ByteString.Lazy.Char8
import Data.ByteString.Base16
import Prelude hiding (interact, lines, unlines)

main = interact $ unlines . map (fromStrict . encode . hashlazy) . lines
于 2013-09-13T06:52:37.593 回答
1

问题是我应该使用普通文件,而不是 FIFO。我看错了。 read工作方式与head: 它不记得它在文件中的位置相同。流会记住它。我不知道我在想什么。 head -n 1将从流中读取一行,从流已经位于的任何位置开始。

#!/bin/bash

# So we don't leave a giant temporary file hanging around:
tmp=
trap '[ -n "$tmp" ] && rm -f "$tmp" &> /dev/null' EXIT
tmp="$(mktemp)" || exit $?

while head -n 1 > "$tmp" && [ -s "$tmp" ]; do
    # Run $tmp file through several programs, like md5sum
done < "$1"

这非常有效。 head -n 1从基本文件流中按顺序读取每一行。从空 FIFO 读取时,我也不必担心后台任务或阻塞。

于 2013-09-13T09:08:02.163 回答
0

散列长行应该是完全可行的,但在 bash 中可能不那么容易。

如果我建议使用 Python,可以这样做:

# open FIFO somehow. If it is stdin (as a pipe), fine. Of not, simply open it.
# I suggest f is the opened FIFO.

def read_blockwise(f, blocksize):
    while True:
        data = f.read(blocksize)
        if not data: break
        yield data

hasher = some_object_which_does_the_hashing()
for block in read_blockwise(f, 65536):
    spl = block.split('\n', 1)
    hasher.update(spl[0])
    if len(spl) > 1:
        hasher.wrap_over() # or whatever you need to do if a new line comes along
        new_hasher.update(spl[1])

这仅仅是面向 Python 的伪代码,它显示了如何做你想做的事情的方向。

请注意,没有记忆是不可行的,但我认为这并不重要。这些块非常小(并且可以做得更小),并且在它们出现时进行处理。

遇到换行符会终止旧行的处理并开始处理新行。

于 2013-09-13T06:52:21.470 回答