Move a pattern from specific line to end of that r

2019-09-21 05:29发布

问题:

I am wondering how can I move a pattern placed after certain line to another location of a rule.

Consider the following example,

rule tmap {

  hideON 
   student_analysis Sam -expr "marks(Sam)" -gt 0
  hideOFF

  -Designate [ School = "ABC]
  -Designate [ Name = "Sam" ]
  -Designate [ Country= "ABC]
  -Designate [ State = "Sam"]
  -Designate [ ROLL Number= "Sam"]
}

where hideOFF needs to be moved from student_analysis to just before the end curly braces. Output file should have something like

rule tmap {

      hideON 
       student_analysis Sam -expr "marks(Sam)" -gt 0


      -Designate [ School = "ABC]
      -Designate [ Name = "Sam" ]
      -Designate [ Country= "ABC]
      -Designate [ State = "Sam"]
      -Designate [ ROLL Number= "Sam"]
     hideOFF
    }

Just to add these -Designate entries might be in a single row so number of row should not be a criterion. I might have my input file like

 rule tmap {

      hideON 
       student_analysis Sam -expr "marks(Sam)" -gt 0
      hideOff

      -Designate [ School = "ABC] -Designate [ Name = "Sam" ] -Designate [ Country= "ABC] -Designate [ State = "Sam"] -Designate [ ROLL Number= "Sam"]

    }

In case if you need more clarification, please let me know.

Final solution Based on Peters help

#!/usr/bin/tclsh
set fileData [lindex $argv 0 ]
set fp [open $fileData r]
set data [read $fp]
set lines [split [string trim $data] \n]
set hideoff {}
foreach line $lines {
  if {[regexp {^\s*hideoff\s*} $line match ] == 1 } {
        set hideoff $line
   } elseif {[regexp {^\s*\}\s*} $line match ] == 1 } {
        puts $hideoff
        puts $line
    } else {
        puts $line
    }
}

Thanks everyone for so nice suggestions.

回答1:

(Tcl solution) You can do that by reprinting lines. You look at them first, skip the line you want to move and then print it when you see the line you want it inserted before.

set lines [split [string trim $data] \n]

set hideoff {}
foreach line $lines {
    if {[string trim $line] eq "hideOFF"} {
        set hideoff $line
    } elseif {[string trim $line] eq "\}"} {
        puts $hideoff
        puts $line
    } else {
        puts $line
    }
}

Another Tcl solution:

regsub -all {(\s*hideOFF)([^\}]+)} $data \\2\\1\n

Both solutions work for multiple blocks.

Documentation: eq (operator), foreach, if, puts, regsub, set, split, string, Syntax of Tcl regular expressions



回答2:

You can use ex text-editor which is available in all Unix/Linux variants to achieve this, in simple/single line as

printf '%s\n' '/hideOFF' 'd' '/}' '-' 'put' 'wq' | ex file

The ex editor constructs are similar to the vi's syntax. The logic I adopted for this is as below (Remember as if you are opening the file in vi/vim)

  1. Searching for the pattern hideOFF as '/hideOFF', deleting it using d command key.
  2. Now searching for the close-brace /}, - and put writes the line from the register above the brace
  3. wq writes back the contents to the file.

You can see it working below.

$ cat file
rule tmap {

  hideON
   student_analysis Sam -expr "marks(Sam)" -gt 0
  hideOFF

  -Designate [ School = "ABC]
  -Designate [ Name = "Sam" ]
  -Designate [ Country= "ABC]
  -Designate [ State = "Sam"]
  -Designate [ ROLL Number= "Sam"]
}

Running the command directly on command-line:-

$ printf '%s\n' '/hideOFF' 'd' '/}' '-' 'put' 'wq' | ex file
$ cat file
rule tmap {

  hideON
   student_analysis Sam -expr "marks(Sam)" -gt 0

  -Designate [ School = "ABC]
  -Designate [ Name = "Sam" ]
  -Designate [ Country= "ABC]
  -Designate [ State = "Sam"]
  -Designate [ ROLL Number= "Sam"]
  hideOFF
}


回答3:

You need to re-write the file, and once you are doing that it's very simple with any language.

Go line by line and copy them to a new file. When you come to hideOFF line do not write it to the new file, just save into a variable. Then, when you come to the closing brace of that block write out the variable (with the line) and the brace. This will have moved the line with hideOFF right before the line with the brace.

You'll need a flag to know when you enter and leave a block.

Please try it out and let us know how it is going.



回答4:

Tested following regex in VIM and it does exactly what you require:

%s/hideOFF\(\_.*\)}/\1\rhideOFF\r}/i

**Explanation:**
%s - Search across the file 
\( \) - Used to capture a group of text to be used in replace section 
\_.* - Capture everything, including new lines 
\1 - Insert captured group here  
\r - new line

I referred to following two sources:

http://vim.wikia.com/wiki/Search_across_multiple_lines & Vim Regex Capture Groups

Original text:

After Regex:



标签: perl shell tcl