shell脚本:搜索并替换多行,但保留一些字符串

时间:2014-03-07 13:25:55

标签: regex perl shell sed

我试图替换看起来像这样的文本文件的一部分

[stuff before]
<!--WEIRD_SPECIAL_COMMENT_BEGIN-->
[stuff here, most to be removed but everything within $TEXT-WANT-TO-KEEP$ should be kept]
<!--WEIRD_SPECIAL_COMMENT_END-->
[stuff after]  

但在WEIRD_SPECIAL_COMMENT_BEGIN和WEIRD_SPECIAL_COMMENT_END之间有一些我想保留的文字。文本始终用$符号括起来。

所以我真正想要的是将文字保存在$符号中,删除WEIRD_SPECIAL_COMMENT_BEGIN和WEIRD_SPECIAL_COMMENT_END之间的所有内容,并将文本粘贴到$ ... $中。 NEW_TEXT_1之前和NEW_TEXT_2。我想到的最终结果是这样的:

[stuff before]
<NEW_TEXT_1>
[TEXT_I_WANT_TO_KEEP]
<NEW_TEXT_2>
[stuff after] 

我刚接触shell,但无法使用sed命令弄清楚如何执行此操作。任何帮助非常感谢。

更新:

我的文件非常混乱,但一个简单的工作示例可能是乳胶文档

[stuff before]
\begin{enumerate}
\item bla bla 
\item bla bla
\item $x = y$
\end{enumerate}
[stuff after] 

结果我想到的可能是这样的

[stuff before]
\begin{equation}
x = y
\end{equation}
[stuff after] 

4 个答案:

答案 0 :(得分:0)

以下是我拍摄的照片:

cat stuff.txt | \
    sed 's/<\!--.*BEGIN-->/<NEW_TEXT_1>/' | \
    sed 's/<\!--.*END-->/<NEW_TEXT_2>/' | \
    sed 's/^.*\($.*$\).*$/\1/'

编辑:尝试#2,使用bash脚本!

#!/bin/bash

IN=0
while read s; do 

    if [ "$s" = "\\begin{equation}" ]; then
        IN=1
        continue
    fi

    if [ "$IN" = "1" ]; then 

        if [ "$s" = "\\end{equation}" ]; then
            IN=0 
            continue
        else
            echo $s | sed 's/.*\(\$.*\$\).*/\1/'
        fi

    else
        echo $s
    fi

done < stuff.txt;

edit2:尝试3;)这个bash脚本开始变得非常难看,但我想看看我是否可以使它工作,所以这里是:

#!/bin/bash

#backslashes and braces don't match well in bash string comparisons.
#so, lets replace them with better identifiers. 
cat stuff2.txt | sed 's/\\\(begin\|end\){enumerate}/XXXX\1/' > tmpfile

IN=0
while read s; do 


    if [ "$s" = "XXXXbegin" ]; then
        IN=1
        continue
    fi

    if [ "$IN" = "1" ]; then 

        if [ "$s" = "XXXXend" ]; then
            IN=0 
            continue
        else
                    #sed -n means don't print anything
                    # but the /p at the end means print matches

            echo $s | sed -n 's/.*\$\(.*\)\$.*/\1/p'
        fi

    else
        echo $s
    fi

done < tmpfile

rm -f tmpfile

答案 1 :(得分:0)

我无法想象能够使用简单的grepsed执行此操作。这些是基于内衬的工具,因此。我可以想象一个循环遍历文件的简单脚本:

while read line
do
    # Magic happens here
done < $myfile

现在,我们必须弄清楚魔法应该是什么......

更简单的是awk脚本可以做同样的事情:

awk '{ #Magic stuff happens here }' $myfile

awk脚本在隐含循环上运行。

让我们使用一个变量来跟踪你是否在那些奇怪的东西中

awk '{
    line = $0
    if ( $0 = "<!--WEIRD_SPECIAL_COMMENT_BEGIN-->" ) {
        weird_stuff = 1
    }
    if ( $0 = "<!--WEIRD_SPECIAL_COMMEND_END-->" ) {
        weird_stuff = 0
    }
    if ( weird_stuff = 1 ) {
        munge line...
    }
    print line
}' $myfile

现在,我们必须弄清楚如何消除这条线。 awk可以处理正则表达式,但它在处理这样的东西方面并不像Perl那么丰富。

你队中的$可以包含多件事吗?如果是这样,我们可以使用index命令查找$

awk '{
     line = $0
     if ( $0 = "<!--WEIRD_SPECIAL_COMMENT_BEGIN-->" ) {
         weird_stuff = 1
     }
     if ( $0 = "<!--WEIRD_SPECIAL_COMMEND_END-->" ) {
         weird_stuff = 0
     }
     if ( weird_stuff != 1 ) { # This is not in the weird stuff. Print the line
         print $0
         next
    }
    # Weird Stuff at this point forward
    first_char = index( $0, "$")
    if ( first_char = 0 ) {
        next    # Nothing to print
    }
    subline = substr( $0, first_char + 1 )  # Remove up to the $
    second_char = index( subline, "$" )
    if ( second_char = 0 ) {
        next    # No second "$" found. Nothing to print
    }
    print substr( subline, 1, $second_char - 1)
}' $myfile

我的awk真的非常生疏。当Perl在版本3中发现Perl时,我已经停止使用awk。所以,我不能保证这个程序是否有效。我甚至没有测试它。

但是,它确实可以让您了解如何处理您的问题。我本可以做一些类似的事情:

while read line
do
    # Magic stuff happens here...
done < $myfile

如果我这样做,我本可以使用sed,它可以一次性从输入字符串的其余部分中取出$...$

答案 2 :(得分:0)

等待。 Perl还好吗?我刚刚意识到它在你的标签中。你的头衔说“Shell Script”。对大多数人来说,这意味着awk很好,但Perl不是。

#! /usr/bin/env perl
#
use warnings;
use strict;
use feature qw(say);
use File::stat;

my $weird_flag = 0;

while ( my $line = <DATA> ) {
    chomp $line;
    if ( $line =~ /\\begin{enumerate}/ ) {
        $weird_flag = 1;
        say "$line";
        next;
    }
    if ( $line =~ /\\end{enumerate}/ ) {
        $weird_flag = 1;
        say "$line";
        next;
    }
    if ( not $weird_flag ) {
        say "$line";
    }
    else {
        if ( $line =~ s/.*?\$(.*)\$.*/$1/ ) {
            say "$line";
        }
    }
}
__DATA__
[stuff before]
\begin{enumerate}
\item bla bla 
\item bla bla
\item $x = y$
\end{enumerate}
[stuff after] 

答案 3 :(得分:0)

这可能适合你(GNU sed):

sed -e '/<!--WEIRD_SPECIAL_COMMENT_BEGIN-->/,/<!--WEIRD_SPECIAL_COMMENT_END-->/{//d;s/[^$]*\$\([^$]*\).*/[\1]/;i\<NEW_TEXT_1>' -e 'a\<NEW_TEXT_2>' -e '}' file