如何在shell脚本中使用sed命令逐行修改文件的条目?

如何在shell脚本中使用sed命令逐行修改文件的条目?

这是我的file1

#$BQ
#{ 音量@home }
#数据库达巴
#关系 tcdeatid
#复制1
#{ 版本 0 }
#opendb
#清除
#.lruno := 72
#.infno := 1
#.tid.noel := 101
#.tid.info := 64
#.tid.setnr := 1225              <---(1225号码将更改)
#.typeidm := 1
#.源表:= 2
#writedb     
#清除
#.lruno := 72
#.infno := 205
#.tid.noel := 101
#.tid.info := 76
#.tid.setnr := 5625              <---(5625号码将被更改)
#.typeidm := 1
#.源表:= 2
#writedb
#$EOJ

在 中file2,我有正确的条目:

#.tid.info := 3345              <---(我想把这个数字放入文件1代替 64)
#.tid.setnr := 1254              <---(我想把这个数字放入文件1代替 1225)
#.tid.info := 5567              <---(我想把这个数字放入文件1代替 76)
#.tid.setnr := 9056              <---(我想把这个数字放入文件1代替5625)

我想file1根据来自 的数据进行更改file2

我努力了 sed "s/tid.setnr := 1225/tid.setnr := 1254/g" file1 > modified_file1

但我不想手动执行此操作,而是希望我的脚本读取新值tid.infotid.setnrfile2并修改file1. in 中的第一行应替换 containsfile2中的整个第一行,第二行应替换contains中的第二行,依此类推。file1tid.infofile2file1tid.setnr

答案1

使用 GNU sed 的 R 命令:

sed -e 's/^#.tid.setnr :=.*//;tA;b;:A;R file2' -e 'd' file1

答案2

执行此操作的一种方法sed是生成sed脚本:

tmpfile=/tmp/Nainita.$$
exec 3< file2
grep -n "tid\.setnr" file1 | while IFS=: read n junk
do
        IFS= read -r line <&3  ||  break
        printf "%sc%s\n%s\n" "$n" '\' "$line"
done > "$tmpfile"
exec 3<&-
sed -f "$tmpfile" file1 > modified_file1
rm -f "$tmpfile"
  • exec 3< file2打开file2以读取文件描述符 3。
  • 按行号查找包含 ,的行grep -n。这被输送到一个循环中。file1tid.setnrwhile
  • while IFS=: read n junk

    • while … read …表示一次读取一行,重复读取,只要有信息可读取(即到达数据末尾时停止)。
    • IFS=: read n junk: 意味着将第一行(即来自 的行号grep -n)之前的所有内容读入n,并将该行的其余部分(旧tid.setnr数据行)读入junk,我们忽略它,因为我们不关心它。
  • IFS= read -r line <&3从文件描述符 3 ( ) 中读取一行file2,执行我们知道如何执行的所有操作来告诉 shell 不要破坏它,并将其放入名为 的变量中line
  • … || break说,如果上述read失败(可能是由于文件结尾),则跳出循环。
  • printf了一个sed Change 命令,指向行号n,表示该行应该替换为line变量的内容。
  • done > "$tmpfile"标记循环结束while,并指定整个循环的标准输出为$tmpfile。这最终会看起来像这样:

    13c\
    #.tid.setnr := 1254
    22c\
    #.tid.setnr := 9056
    • 请注意,while一旦从任一输入获得 EOF,循环就会终止。因此,如果行数file1多于,则多余的行将保持不变(即,不会生成它们的命令)。同样,如果行数多于,则多余的行将被忽略。不幸的是,这种差异不会被报告,尽管添加该功能并不困难。tid.setnrfile2cfile2tid.setnrfile1
  • exec 3<&-关闭文件描述符 3。

  • sed -f "$tmpfile" file1 > modified_file1运行sed在 上file1,从 读取命令$tmpfile并将输出写入到modified_file1

这应该做你想做的。显然,如果需要,可以更改文件名。您应该“按原样”运行一次,然后查看该modified_file1文件(或者简单地更改命令sed以不重定向其输出,以便它写入屏幕)并验证输出是否是您想要的。然后您可以将sed命令更改为就地sed -i编辑file1(如果您想要这样做)。

如果该sed命令给出诸如“行号过多”之类的错误,请尝试将脚本文件 ( $tmpfile) 拆分为较小的文件。我建议从 100 个命令以下的大小开始;例如,90 个命令,即 180 行,因为每个命令是两行。您可以使用文本编辑器1手动执行此操作,但有一个专门为此工作编写的工具。直观上,它被称为split.命令

split --lines=180 "$tmpfile"

会将脚本分割成当前目录中名为xaa, xab, xac, ... 的文件。首先n−1 的长度为 180 行;最后一项将是构成总数所需的任何内容 (≤ 180)。例如,如果您有 500 个 实例tid.setnr,那么您的脚本将有 1000 行长,您将得到 6 个x文件 — xaaxabxacxadxae每个文件有 180 行,并且xaf将有 100 行。现在做

sed -f xaa file1 > modified_file1aa

如果仍然出现“行数过多”,请返回并使用较少的行数重试--lines。如果没有报错,看看modified_file1aa前90行是不是tid.setnr被改了。如果看起来不错,那么就做

sed -f xab modified_file1aa > modified_file1ab
sed -f xac modified_file1ab > modified_file1ac
sed -f xad modified_file1ac > modified_file1ad
sed -f xae modified_file1ad > modified_file1ae
sed -f xaf modified_file1ae > modified_file1af

modified_file1af现在是你的最后了modified_file1

如果你想玩实验的话,可以

  • 尝试更大的数字,--lines直到找出最大值是多少。
  • 尝试

    sed -f xaa -f xab -f xac -f xad -f xae -f xaf file1 > modified_file1_test
    

    但这可能行不通。

  • 如果您做了上述实验,我鼓励您告诉我们结果。

如果您只需要执行此操作一次,那么您就完成了。但是,如果您需要重复执行此操作,请将本答案顶部代码块的最后两行更改为

分割 --lines=180 "$tmpfile"            <---(当然,使用适合您的数字)
cp file1 修改后的文件1
对于 x* 中的 f
        sed -i -f“$f”modified_file1
完毕
rm -f "$tmpfile" x*

正如我之前提到的,该-i选项告诉sed您就地编辑指定的文件(即,将更改写回输入文件)。或者,更简单的是,

split --lines=180 "$tmpfile"
for f in x*
do
        sed -i -f "$f" file1
done
rm -f "$tmpfile" x*

如果您不需要保持原件file1完好无损。

请注意,其用法概要split

分裂 [选项]……[输入[字首]]

它的默认行为是创建名为PREFIXaaPREFIXabPREFIXac等的文件。换句话说,默认PREFIXx。如果您可能有其他名称以 开头的文件x,您应该定义prefix为唯一的文件(例如,prefix=Nainita.$$.prefix=/tmp/Nainita.$$.),然后将上面的内容更改为

split --lines=180 "$tmpfile" "$prefix"
for f in "$prefix"*
do
        sed -i -f "$f" file1
done
rm -f "$tmpfile" "$prefix"*

______
1如果你热衷于惩罚,那么你可以这样做sed——但为什么要玩火呢?

答案3

{   sed '/^#\.tid\.setnr/!d;=;g;G' |
    paste  -d'c\\\n' - - - ./addfile
}   <./mainfile | sed -f - ./mainfile

它只是在前面加上相关的行号./主文件到每一行。/添加文件由命令字符串c\和 a分隔<换行>在将结果sed作为脚本传递给另一个进行编辑之前./主文件。根据您的示例数据,生成的脚本如下所示:

13c\
#.tid.setnr := 1254              <--- (I want to put this number in file1 in place of 1225)
22c\
#.tid.setnr := 9056              <--- (I want to put this number in file1 in place of 5625)

...指示sed悬挂c线1322其输出以匹配每个命令的附加行。

答案4

我发现了perl。这个脚本是一个粗略的脚本,似乎可以解决这个问题:

#!/usr/bin/perl
#
use strict;

# subroutine to load a test file into an array
sub load{
   my $file = shift;
   open my $in, "<", $file or die "unable to open $file : $!";
   my @data = <$in>;
   chomp @data;
   foreach (@data) { s/\cM//g;}
   return @data;
}


my $file1 = shift || die "usage: $0 [file1] [file2]\n";
my @file1_data = &load($file1);
my $file2 = shift || die "usage: $0 [file1] [file2]\n";
my @file2_data = &load($file2);

my $i = 0;
foreach( @file1_data )
{
    if( $i < @file2_data ) 
    {
        my @s = split / /, $file2_data[$i];
        if( @s )
        {
            if( /^$s[0]/ )
            {
            $_ = $file2_data[$i++];
            }
        }
    }
    print "$_\n";

相关内容