【问题标题】:unix prepend line to file without buffer/copyunix 在没有缓冲区/复制的情况下将行添加到文件中
【发布时间】:2012-08-23 01:23:48
【问题描述】:

我想要这个,sed 解决方案看起来不错,但是当我尝试时,它似乎创建了一个巨大的临时文件。

unix command to prepend text to a file

我有很大的 sql 文件,大约 28 gigs,我在文件系统上没有太多空间,只想在文件前面添加一行。如何在不占用更多文件空间的情况下做到这一点?

【问题讨论】:

  • 能不能把这一行放在一个单独的文件里,然后用cat single-line-file 28-gigabyte-file | program-that-reads-the-data?这样可以避免在磁盘上显式创建文件。

标签: file unix sed


【解决方案1】:

用 gcc 编译:

#include <stdio.h>
#include <string.h>
#include <malloc.h>

// Prepend size must be less than this value
#define bufSize 1024000

int main( int argc, char **argv )
{
    FILE *fil;
    unsigned char *smallBuf, *mainBuf;
    size_t sReadSize, mReadSize;
    long readPos = 0, writePos = 0;
    int appendSize;

    if( argc != 3 )
    {
        printf( "Usage: %s, <prepend_line> <file>\n", argv[0] );
        return 1;
    }

    sReadSize = appendSize = strlen( argv[1] ) + 1;

    smallBuf = (unsigned char *) malloc( appendSize );
    mainBuf = (unsigned char *) malloc( bufSize );
    if( !smallBuf || !mainBuf )
    {
        printf( "No memory\n" );
        return 1;
    }

    memcpy( smallBuf, argv[1], appendSize );
    smallBuf[ appendSize - 1 ] = '\n';

    fil = fopen( argv[2], "rb+" );
    if( !fil )
    {
        printf( "Cannot open file\n" );
        return 1;
    }

    while( 1 )
    {
        fseek( fil, readPos, 0 );
        readPos += mReadSize = fread( mainBuf, 1, bufSize, fil );

        fseek( fil, writePos, 0 );
        writePos += fwrite( smallBuf, 1, sReadSize, fil );

        if( mReadSize < bufSize )
        {
            if( mReadSize > 0 )
                fwrite( mainBuf, 1, mReadSize, fil );
            break;
        }

        fseek( fil, readPos, 0 );
        readPos += sReadSize = fread( smallBuf, 1, appendSize, fil );

        fseek( fil, writePos, 0 );
        writePos += fwrite( mainBuf, 1, mReadSize, fil );

        if( sReadSize < appendSize )
        {
            if( sReadSize > 0 )
                fwrite( smallBuf, 1, sReadSize, fil );
            break;
        }
    }

    fclose( fil );
    return 0;
}

【讨论】:

  • 我有 2 个缓冲区,首先我读取 1 个块,写入前置,读取第二个,先写入,...请多加注意。
  • 是的,我仔细检查后发现了这一点。我将编辑原始评论,但我仍然会注意到这是一个危险的操作,特别是因为原始问题是关于多 GB 文件的。在断电、程序意外提前终止或任何其他因素的情况下,就地修改很容易发生灾难性故障,这将使文件的唯一副本部分修改(尽管可能至少以某种可预测的方式,忽略缓存/写回时间问题)。
  • Joelio 要求提供一个不需要额外内存的简单建议。当然,通过添加一个包含缓冲区内容和当前进度信息的临时文件,它可能是抗故障的。
【解决方案2】:

不幸的是,在我见过的每个操作系统和文件系统上,通常不能像附加一样在适当的位置完成预置。有人可能会争辩说,如果数据量是底层文件系统块大小的几倍,文件系统可以有效地做到这一点,但由于通常情况并非如此,我不知道有任何实际实现了这样的功能.因此,可能唯一的方法是通过临时文件或副本。但是,您可以使用压缩在一定程度上缓解空间紧缩,但这需要一些准备工作,并且最终可能不适合。大致如下:

1) gzip original_file.sql    # or bzip2 or whatever
2) create new_file.sql with data to be prepended
3) (cat new_file.sql; zcat original_file.sql.gz) | gzip > updated_file.sql.gz
4) zcat updated_file.sql.gz | less  # inspect the top of the file to make sure it looks right
5) rm original_file.sql.gz new_file.sql
6) gunzip updated_file.sql.gz # if necessary to have uncompressed text available - if not, just leave it compressed

【讨论】:

  • 尽管不是我想要的答案,但似乎是正确的并且是共识。
【解决方案3】:

您可以为此使用 perl:

perl -i -n -e 'print "xxx\n$_" if $.==1;print if $.!=1' your_file

【讨论】:

  • -i 在后台使用临时文件。
猜你喜欢
  • 2012-06-25
  • 1970-01-01
  • 2017-03-24
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-14
  • 1970-01-01
相关资源
最近更新 更多