简体   繁体   English

在bash中跨多行替换值

[英]Replace value across multiple lines in bash

I'm struggling even with all the examples available on stackoverflow and google.即使使用 stackoverflow 和 google 上可用的所有示例,我也在苦苦挣扎。

Basically i have the following text基本上我有以下文字

    /start r.start ""
      GAIN 0x256 __POSITIVE 1 FOO
      OTHER
      OTHER
      /start MACRO
        200
        CODE "r.start" 0x256 0x2 10 0xA3
      /end MACRO
      OTHER
    /end

and i need to read through this file searching for a name egrstart which is passed as $1 and substitute eg 0x256 with a value that i pass as $2.我需要通读这个文件,搜索作为 $1 传递的名称 egrstart 并用我作为 $2 传递的值替换例如 0x256。 There a two instance to substitute, line 2 and line 7.有两个实例要替换,第 2 行和第 7 行。

Things i know:我知道的事情:

  1. /start is preceded by 4 spaces /start 前面有 4 个空格
  2. GAIN is preceded by 6 spaces GAIN 前面有 6 个空格
  3. \\r\\n or \\n might be present \\r\\n 或 \\n 可能存在
  4. CODE is preceded by 8 spaces CODE 前面有 8 个空格

Till now i've reached this point直到现在我已经达到了这一点

pattern="N;s\s\/start r.start \"\"\n      GAIN 0x\(.*\)"
replacement"\/start r.start \"\"\n      GAIN 0x82"
sed -e "$pattern/$replacement/p" test.txt

but i get nothing.但我一无所获。 I was also able to substitute the first line but for whatever reason it pasted me the first two lines twice on top of each other我也能够替换第一行,但无论出于何种原因,它都将前两行粘贴在彼此之上两次

the expected value assuming the following call假设以下调用的预期值

./run.sh r.start 0x284569

should be应该

    /start r.start ""
      GAIN 0x284569 __POSITIVE 1 FOO
      OTHER
      OTHER
      /start MACRO
        200
        CODE "r.start" 0x284569 0x2 10 0xA3
      /end MACRO
      OTHER
    /end

Here's my best shot for the second part这是我第二部分的最佳镜头

var="r.start"
val="0x48209F82"

pattern="\(CODE \"$var\" \).*\(.*\)"
replacement="\1$val\2"
sed "s/$pattern/$replacement/g" test.txt

The problem is that it is deleting everything after the value substitution.问题是它在值替换后删除了所有内容。 I can't put in \\2 the following chars我不能输入 \\2 以下字符

EDIT:编辑:

By doing the following通过执行以下操作

var="r.start"
val="0x48209F82"

pattern="\(CODE \"$var\" \).*\(\s.*\s.*\s.*\)"
replacement="\1$val\2"
sed "s/$pattern/$replacement/g" test.txt

I get what i want but it feels a little bit dirty, how do i reduce the last part in case of a variable number of char?我得到了我想要的东西,但感觉有点脏,如果字符数可变,我该如何减少最后一部分? Can i just somehow match everything till end of line?我可以以某种方式匹配所有内容直到行尾吗?

How about something like the following?像下面这样的怎么样? It uses a sed range to select everything between the /start and /end , and within the range uses a block to substitute the value for lines which match GAIN and CODE .它使用sed范围来选择/start/end之间的所有内容,并在该范围内使用一个块来替换与GAINCODE匹配的行的值。

$ cat foo.txt
    /start r.start ""
      GAIN 0x256 __POSITIVE 1 FOO
      OTHER
      OTHER
      /start MACRO
        200
        CODE "r.start" 0x256 0x2 10 0xA3
      /end MACRO
      OTHER
    /end
$ sed '\#/start r\.start#,\#/end#{/\(GAIN\|CODE "r\.start"\)/s/0x[a-fA-F0-9]\+/0x284569/}' foo.txt
    /start r.start ""
      GAIN 0x284569 __POSITIVE 1 FOO
      OTHER
      OTHER
      /start MACRO
        200
        CODE "r.start" 0x284569 0x2 10 0xA3
      /end MACRO
      OTHER
    /end

The sed script looks like this when formatted nicely: sed脚本在格式良好时看起来像这样:

\#/start r\.start#,\#/end# {
    /\(GAIN\|CODE "r\.start"\)/s/0x[a-fA-F0-9]\+/0x284569/
}

Note笔记

  • the use of \\#...# instead of /.../ for the range patterns, so we don't have to quote slashes使用\\#...#而不是/.../作为范围模式,所以我们不必引用斜杠
  • the use of /patten1/s/pattern2/string/ to replace pattern2 only for lines containing pattern1使用/patten1/s/pattern2/string/替换pattern2仅用于容纳线pattern1
  • the use of a {...} block so that the substitution only applies between /start and /end使用{...}块,以便替换仅适用于/start/end

You can parameterise the pattern and value like so:您可以像这样参数化模式和值:

#!/bin/bash

var='r\.start'
val='0x48209F82'

sed '\#/start '$var'#,\#/end#{/\(GAIN\|CODE "'$var'"\)/s/0x[0-9a-fA-F]\+/'$val'/}' foo.txt

This might work for you (GNU sed):这可能对你有用(GNU sed):

sed -E '/^    \/start r\.start /{:a;N;/^    \/end$/M!ba;s/^(      GAIN |        CODE "r\.start" )0x\S+/\10x284569/Mg}' file

Gather up lines between /start and /end and using pattern matching replace the desired values.收集/start/end之间的行并使用模式匹配替换所需的值。

The solution may be placed in a function:解决方案可以放在一个函数中:

f () { sed -E '/^    \/start '"$1"' /{:a;N;/^    \/end$/M!ba;s/^(      GAIN |        CODE "'"$1"'" )0x\S+/\1'"$2"'/Mg}' "$3"; }

And called:并称:

f r\\.start 0x284569 file

Here is a perl way:这是一个perl方式:

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

# retrieve the 2 parameters
my $start = shift @ARGV or die "missing 1rst arg";
my $repl =  shift @ARGV or die "missing 2nd arg";

# input file
open my $fh_in, '<', 'file.txt' or die "$!";
# output file
open my $fh_out, '>', 'output' or die "$!";

# loop through input file
while(<$fh_in>) {
    # if we are between /start {1srt parameter} and /end
    if (/^ {4}\/start\h+$start/ ... /^ {4}\/end\h*$/) {
        # substitute 0x.... by {2nd parameter}
        s/^(?: {6}GAIN | {8}CODE "$start" )\K0x\w+/$repl/;
    }
    print $fh_out $_;
}

In action:在行动:

cat file.txt 
    /start other.start ""
      GAIN 0x256 __POSITIVE 1 FOO
      OTHER
      OTHER
      /start MACRO
        200
        CODE "r.start" 0x256 0x2 10 0xA3
      /end MACRO
      OTHER
    /end
    /start r.start ""
      GAIN 0x256 __POSITIVE 1 FOO
      OTHER
      OTHER
      /start MACRO
        200
        CODE "r.start" 0x256 0x2 10 0xA3
      /end MACRO
      OTHER
    /end

./test.pl r.start 0x123456

cat output 
    /start other.start ""
      GAIN 0x256 __POSITIVE 1 FOO
      OTHER
      OTHER
      /start MACRO
        200
        CODE "r.start" 0x256 0x2 10 0xA3
      /end MACRO
      OTHER
    /end
    /start r.start ""
      GAIN 0x123456 __POSITIVE 1 FOO
      OTHER
      OTHER
      /start MACRO
        200
        CODE "r.start" 0x123456 0x2 10 0xA3
      /end MACRO
      OTHER
    /end

You want sed for this, and don't need a script:您需要为此使用 sed,并且不需要脚本:

Assuming the text is a file called the_text:假设文本是一个名为 the_text 的文件:

sed "s/0x256/REPLACETEXT/g" the_text

If you ant to do that with out echoing out the text then add a -i如果你不想回显文本就这样做,然后添加 -i

sed -i "s/0x256/REPLACETEXT/g" the_text

It is possible to daisy chain these commands or embed them into single script, this one echoes to the standard out:可以菊花链这些命令或将它们嵌入到单个脚本中,这与标准输出相呼应:

#!/bin/bash

sed "s/0x256/${2}/g" $1
sed "s/0x256/${2}/g" $1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM