移动行以跟随文件中的另一行

时间:2009-10-20 22:20:55

标签: linux awk

我有一个文件在文件中有这样一行:

check=('78905905f5a4ed82160c327f3fd34cba')

我希望能够将此行移动到如下所示的行:

files=('somefile.txt')

虽然数组有时会跨越多行,例如:

files=('somefile.txt'
       'file2.png'
       'another.txt'
       'andanother...')

text
in between

check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

数组/行总是以a)结尾,中间没有文本将包含一个闭括号。

我得到一些建议,awk可以做到这一点:

awk '/files/{
    f=0
    print $0
    for(i=1;i<=d;i++){ print a[i]  }
    g=0
    delete a # remove array after found
    next
}
/check/{ f=1; g=1 }
f{ a[++d]=$0 }
!g' file

这只会跨越一条线。我被告知要扩大搜索范围:

awk '/source/ && /\)$/{
    f=0
    print $0
    for(i=1;i<=d;i++){ print a[i]  }
    g=0
    delete a # remove array after found
    next
}
/md5sum/ && /\)$/{ f=1; g=1 }
f{ a[++d]=$0 }
!g'

刚刚学习awk所以我很感激这方面的帮助。或者,如果有其他工具可以做到这一点,我想听听它。有人告诉我'ed'这些类型的能力。

5 个答案:

答案 0 :(得分:2)

首先回答你的上一个问题,是的,awk是典型的Unix工具,其他候选人是非常强大的PerlPython或..我最喜欢的... Rubyawk的一个优点是它始终存在;它是基础系统的一部分。解决此类问题的另一种方法是使用控制ed(1)ex(1)的编辑器脚本。

好的,修改过的问题的新程序。该程序将根据需要向上或向下移动“检查”行,以便它们跟随“文件”行。

BEGIN {
  checkAt = 0
  filesAt = 0
  scanning = 0
}

/check=\(/ {
  checkAt = NR
  scanning = 1
}

/files=\(/ {
  filesAt = NR
  scanning = 1
}

/)$/ {
  if (scanning) {
    if (checkAt > filesAt) {
      checkEnd = NR
    } else {
      filesEnd = NR
    }
    scanning = 0
  }
}

{
  lines[NR] = $0
}

END {
  for (i = 1; i <= NR; ++i) {
    if (checkAt <= i && i <= checkEnd) {
      continue
    }
    print lines[i]
    if (i == filesEnd) {
      for (j = checkAt; j <= checkEnd; ++j) {
        print lines[j]
      }
    }
  }
}

答案 1 :(得分:0)

以下是使用sed:

的方法
sed -e /^check=(/,/)/{H;d} -e /)/{G;s/\n//} < filename

这假设在“files = ...”之后没有正确的括号如果有,那么你需要更高的精度:

sed -e /^check=(/,/)/{H;d} -e /^files=(/,/)/{/)/{G;s/\n//}} < filename

修改:
在bash工作?好的,试试这个:

sed -e /^check=(/,/)/H -e /^check=(/,/)/d -e '/)/G;s/\n//' < filename

这似乎有效,但我不清楚为什么这个变体而不是其他一些明显的变体。这种特殊人物的舞蹈一直是正则表达式的问题。

答案 2 :(得分:0)

我看着用Awk这样做,但看起来你不会真的从中得到任何巧妙的东西,它只是相同的逻辑,但是有一些Awk的痛苦,所以我做到了在Perl:)

#!/usr/bin/perl

open(IN, $ARGV[0]) || die("Could not open file: " . $ARGV[0]);

my $buffer="";

foreach $line (<IN>) {
        if ($line =~ /^check=/) {
                $flag = 1;
                $buffer .= $line;
        } elsif ($flag == 1 && $line =~/\)/) {
                $flag = 0;
                $buffer .= $line;
        } elsif ($flag == 1) {
                $buffer .= $line;
        } elsif ($flag == 0 && $line =~ /^files=/) {
                $flag = 2;
                print $line;
        } elsif ($flag == 2 && $line =~ /\)/) {
                $flag = 0;
                print $line;
                if (length($buffer) > 0) {
                        print $buffer;
                        $buffer = "";
                }
        } else {
                print $line;
        }

}

输出:)

Chill:~ rus$ cat test check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

text in between

files=('somefile.txt'
       'file2.png'
       'another.txt'
       'andanother...')

asdasdasd

check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

text in between

files=('somefile.txt'
       'file2.png'
       'another.txt'
       'andanother...')

asdsd

check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

text in between

files=('somefile.txt'
       'file2.png'
       'another.txt'
       'andanother...')

Chill:~ rus$ ./t.pl test

text in between

files=('somefile.txt'
       'file2.png'
       'another.txt'
       'andanother...') check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

asdasdasd


text in between

files=('somefile.txt'
       'file2.png'
       'another.txt'
       'andanother...') check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

asdsd


text in between

files=('somefile.txt'
       'file2.png'
       'another.txt'
       'andanother...') check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')
ta da ?! :d

答案 3 :(得分:0)

@todd,在为你提供awk解决方案之后我似乎已经让你陷入困境了。 ? :)。 这是另一种方法,这次不使用标志方法。有一些松散的结束(提示:检查模式p,q和输出),我留给你整理。

gawk 'BEGIN{
    RS="check=[(]"
    q="files=(.*\047)"  # pattern to replace files= part
    p=".*(files=(.*\047)).*" # to get the whole files= part to variable
}
NR>1{
    b=gensub(p, "\\1","g",$0) # get the files=part to var b
    printf "%s\n\n",b    
    printf "check=("
    gsub(q,"",$0)
    print $0
}' file

注意:gensub特定于gawk所以如果你有gawk,那就没关系

输出

$ more file
check=('5277a9164001a4276837b59dade26af2'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

text in between one

files=('somefile1.txt'
       'file1.png'    
       'another1.txt' 
       'andanother1...')

asdasdasd blah blah

check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

text in between  two

files=('somefile2.txt'
       'file2.png'    
       'another2.txt' 
       'andanother2...')

asdsd blaasdf aslasdfaslj aslfjsldfsa 123e12

check=('78905905fblah blah5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'         
       '3f8b60b6fbb993c18442b62ea661aa6b')        

text in between

files=('somefile3.txt'
       'file3.png'    
       'another3.txt' 
       'andanother3...')

$ ./shell.sh
files=('somefile1.txt'             
       'file1.png'                 
       'another1.txt'              
       'andanother1...'            

check=('5277a9164001a4276837b59dade26af2'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

text in between one

)

asdasdasd blah blah


files=('somefile2.txt'
       'file2.png'
       'another2.txt'
       'andanother2...'

check=('78905905f5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

text in between  two

)

asdsd blaasdf aslasdfaslj aslfjsldfsa 123e12


files=('somefile3.txt'
       'file3.png'
       'another3.txt'
       'andanother3...'

check=('78905905fblah blah5a4ed82160c327f3fd34cba'
       '5277a9164001a4276837b59dade26af2'
       '3f8b60b6fbb993c18442b62ea661aa6b')

text in between

)

答案 4 :(得分:0)

这可能对您有用:

 sed ':a;$!N;/^files=.*\ncheck=/{/.*)$/!ba;s/\([^)]*)\)\(.*\)\(\ncheck=.*\)/\1\3\2/p;d};/^files=.*/ba;P;D' file