Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move line(s) to follow another line in a file

Tags:

linux

awk

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.

like image 736
Todd Partridge 'Gen2ly' Avatar asked Nov 06 '22 19:11

Todd Partridge 'Gen2ly'


1 Answers

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]
      }
    }
  }
}
like image 197
DigitalRoss Avatar answered Nov 12 '22 16:11

DigitalRoss