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

发布于 2024-08-08 20:40:52 字数 1066 浏览 4 评论 0原文

我得到一个文件,其中有这样的行:

check=('78905905f5a4ed82160c327f3fd34cba')

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

files=('somefile.txt')

数组,但有时可以跨越多行,例如:

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

text
in between

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

数组/line 始终以 ) 结尾,并且其间的任何文本都不会包含右括号。

我得到了一些建议,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”了这些类型的功能。

I got a file that has a line in the file like this:

check=('78905905f5a4ed82160c327f3fd34cba')

I'd like to be able to move this line to follow a line that looks like this:

files=('somefile.txt')

The array though at times that can span multiple lines, for example:

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

text
in between

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

The array/line always ends in a ) and no text in between will contain a closed parenthesis.

I got some advice that awk can do this:

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

This will only span one line though. I was told to expand the 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'

Just learning awk so I'd appreciate help with this. Or if there is another tool that can do this, I'd like to hear about it. Someone told me that 'ed' these types of capabilities.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

樱&纷飞 2024-08-15 20:40:52

首先回答你的最后一个问题,是的,awk 是典型的 Unix 工具,其他候选工具是非常强大的 Perl, < code>Python,或者..我最喜欢的.. 红宝石awk 的优点之一是它始终存在;它是基础系统的一部分。解决此类问题的另一种方法是使用控制 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]
      }
    }
  }
}

To answer your last question first, yes, awk is the typical Unix tool for this, other candidates are the incredibly powerful Perl, Python, or .. my favorite .. Ruby. One advantage of awk is that it's always there; it's part of the base system. Another way to solve this kind of problem is with an editor script that controls ed(1) or ex(1).

Ok, new program for the revised question. This program will move the "check" lines either up or down as necessary so that they follow the "files" lines.

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]
      }
    }
  }
}
戈亓 2024-08-15 20:40:52

我打算用 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

I looked in to doing this with Awk, but it looked like you wouldn't really get anything clever out of it, it would just be the same logic, but with some Awk pain to go with it, so I did it in 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;
        }

}

And the output :)

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

酒绊 2024-08-15 20:40:52

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

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

Here's how to do it with sed:

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

This assumes that there are no right parentheses after the "files=..." If there are then you'll need more precision:

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

EDIT:
Working in bash? All right, try this:

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

This seems to work, but it's not clear to me why this variant and not a few other obvious ones. This dance-of-the-special-characters is always a problem with regexs.

巴黎盛开的樱花 2024-08-15 20:40:52

@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

)

@todd, I seem to have left you in the lurch after providing you the awk solution haven't i. ? :).
here's another method, this time not using method of flags. there are some loose ends (hint: check the patterns p,q and output again) that i leave it to you to tidy up.

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

NB: gensub is specific to gawk so if you have gawk, then that's alright

output

$ 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

)
浅沫记忆 2024-08-15 20:40:52

这可能对你有用:

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

This might work for you:

 sed ':a;$!N;/^files=.*\ncheck=/{/.*)$/!ba;s/\([^)]*)\)\(.*\)\(\ncheck=.*\)/\1\3\2/p;d};/^files=.*/ba;P;D' file
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文