【问题标题】:Bash remove an exact line and number of characters from a large fileBash 从大文件中删除确切的行和字符数
【发布时间】:2019-12-03 14:20:22
【问题描述】:

我想以相对节省内存的方式匹配和替换大型数据库转储中的多行正则表达式。我能找到的最接近的解决方案是:

pcregrep -M --line-offsets --buffer-size=100000000 '^COPY "my_app"\."spatial_ref_sys"\s\(.+?\) FROM stdin;(.|\n)*?\\.$' my_app-dump.sql

这将使用最多 280MB 的内存,并且(由于 --line-offsets 选项)给我这个输出:1123175:0,105。这表示行号 (1123175)、距行首的字符偏移量 (0) 以及匹配字符串的长度 (105)。此匹配跨越多行。

我的问题是:打开文件、删除有问题的 sn-p 并保存它的“最佳”方法是什么?我更喜欢可以单独在 bash 中完成的事情,但这不是必需的。

上下文

我正在从安装了 EC2 的 Postgres 数据库迁移到 AWS RDS Postgres 数据库。我遇到的一个问题是,当我使用pg_dump 时,它发出的某些命令需要超级用户权限,而 RDS 没有提供给我。我已经通过sed 删除了多个有问题的 SQL 语句。这些语句对我们的应用程序都不是关键的,但保留它们会导致还原失败。

当前工作解决方案

perl -i -0pe 's/^COPY "my_app"\."spatial_ref_sys"\s\(.+?\) FROM stdin;(.|\n)*?\\.$//gmi' my_app-dump.sql

这使用 perl 直接在 DB 转储上执行正则表达式查找和替换。但是,这会将整个文件加载到大小为 6GB 的内存中。从本地安装的 Postgres DB 测试它可以正常工作,因为我有可用的内存,但我必须在 AWS EC2 实例上运行它,所以我必须启动一个非常强大的实例来执行此迁移。我觉得这是不必要的。我已经知道pcregrep可以匹配280MB或内存,我只需要删除我不知道该怎么做的找到的sn-p。

演示和示例输入

This demo 上的 regex101 正是我想要实现的目标。它匹配正确的行并用空字符串替换它们。

这里还有一个来自 DB 转储的 sn-p:

COPY "my_app"."schema_migrations" ("version") FROM stdin;
20170213125755
20180213083924
20180219070405
20180219102435
20180220074712
20180221082708
20180221104015
20180523211327
20190213080433
20190218070455
20190226084814
20190625083212
20190704092807
20190705070442
20190711073923
20190718095547
\.


--
-- Data for Name: spatial_ref_sys; Type: TABLE DATA; Schema: my_app; Owner: -
--

COPY "my_app"."spatial_ref_sys" ("srid", "auth_name", "auth_srid", "srtext", "proj4text") FROM stdin;
\.

以上只有这应该匹配正则表达式:

COPY "my_app"."spatial_ref_sys" ("srid", "auth_name", "auth_srid", "srtext", "proj4text") FROM stdin;
\.

所有这些也应该匹配,即使它们不在 DB 转储中:

COPY "my_app"."spatial_ref_sys" ("srid", "auth_name", "auth_srid", "srtext", "proj4text") FROM stdin;
asdf
bla
\.

COPY "my_app"."spatial_ref_sys" (foo, bar) FROM stdin;
\.

COPY "my_app"."spatial_ref_sys" ("foasd 234$", "qwdmc") FROM stdin;
asdjnaksjdnkajnd&(*)-
alkc n;{}
\.

【问题讨论】:

  • 请提供一些示例输入。回答的志愿者不应该每个人都自己编造(也有可能弄错)。

标签: regex bash perl


【解决方案1】:

为避免读取内存中的整个文件,您可以在扫描输入文件时尝试写入输出文件。例如:

use feature qw(say);
use strict;
use warnings;

{
    my $input_fn = 'my_app-dump.sql';
    my $output_fn = 'my_app_filtered.sql';
    Filter->new(
        input_fn => $input_fn,
        output_fn => $output_fn
    );
}

package Filter;
use feature qw(say);
use strict;
use warnings;

sub new {
    my ( $class, %args ) = @_;
    my $self = bless \%args, $class;

    $self->open_files();
    $self->filter_io_loop();
    $self->close_files();
}

sub filter_io_loop {
    my ( $self ) = @_;

    my $fh = $self->{fh};
    my $ofh = $self->{ofh};
    while( my $line = <$fh> ) {
        if ( $line =~ /^COPY "my_app"\."spatial_ref_sys"\s\([^)]*\) FROM stdin;/ ) {
            $self->skip_lines( $fh, $line );
        }
        else {
            print $ofh $line;
        }
    }
}

sub skip_lines {
    my ( $self,  $fh, $line ) = @_;

    while (1) {
        return if $line =~ /\\\.$/;
        $line = <$fh>;
        if (!defined $line) {
            warn "Warning: EOF reached while searching for end of COPY\n";
            return;
        }
    }
}


sub close_files {
    my ( $self ) = @_;

    close $self->{ofh};
    close $self->{fh};
}

sub open_files {
    my ( $self ) = @_;

    my $input_fn = $self->{input_fn};
    my $output_fn = $self->{output_fn};
    open ( my $fh, '<', $input_fn ) or die "Could not open file '$input_fn': $!";
    open ( my $ofh, '>', $output_fn ) or die "Could not open file '$output_fn': $!";
    $self->{fh} = $fh;
    $self->{ofh} = $ofh;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    • 2018-11-20
    • 1970-01-01
    • 2018-09-08
    • 2012-04-26
    • 1970-01-01
    相关资源
    最近更新 更多