将行移到文件中的另一行

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

check=('78905905f5a4ed82160c327f3fd34cba') 

我希望能够移动此行以遵循如下所示的行:

 files=('somefile.txt') 

该数组尽pipe有时可以跨越多行,例如:

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

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

我有一些build议,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 

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

 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,所以我会很感激这个帮助。 或者,如果有另一个工具可以做到这一点,我想听听。 有人告诉我,“编辑”这些types的function。

为了回答你的最后一个问题,是的, 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] } } } } 

我看着用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

以下是如何使用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 //'

这似乎工作,但我不明白为什么这个变种,而不是其他一些明显的。 这种特殊字符的跳舞总是一个正则表达式的问题。

@托德,我似乎已经离开你在提供你的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 ) 

这可能适合你:

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