Bashscript to find files and replace!

Hey i am trying to write a littel bash script. This should copy the directory and all the files in it. Then it has to search every file and directory in that copied directory for a line (like @ForTestingOnly) and then store the line number. Then it must go on and count each {and}, once the number is equal, it must store the agy line number. => it should delete all lines between those two numbers. I am trying to make a script that looks for all of these annotations and then removes the method that is immediately after that annotation. thanks for the reference ...

So far I have:

echo "please enter dir"
read dir
newdir="$dir""_final"
cp -r $dir $newdir 
cd $newdir

grep -lr -E '@ForTestingOnly' * | xargs sed -i 's/@ForTestingOnly//g'

      

now with grep I can search and replace an @ForTestingOnly anot. but I like to remove this and the next method ...

+2


a source to share


2 answers


Try it. He is oblivious to comments and literals, however, as David Gelhar warned . It only finds and removes the first occurrence of the "@ForTestingOnly" block (assuming there will be only one anyway).

#!/bin/bash
find . -maxdepth 1 | while read -r file
do
    open=0 close=0
    # start=$(sed -n '/@ForTestingOnly/{=;q}' "$file")
    while read -r line
    do
        case $line in
            *{*) (( open++ )) ;;
            *}*) (( close++ ));;
             '') : ;;    # skip blank lines
              *) # these lines contain the line number that the sed "=" command printed
                 if (( open == close ))
                 then 
                     break
                 fi
                 ;;
        esac
             # split braces onto separate lines dropping all other chars
             # print the line number once per line that contains either { or }
    # done < <(sed -n "$start,$ { /[{}]/ s/\([{}]\)/\1\n/g;ta;b;:a;p;=}" "$file")
    done < <(sed -n "/@ForTestingOnly/,$ { /[{}]/ s/\([{}]\)/\1\n/g;ta;b;:a;p;=}" "$file")
    end=$line
    # sed -i "${start},${end}d" "$file"
    sed -i "/@ForTestingOnly/,${end}d" "$file"
done

      

Edit: Removed one call sed

(by commenting out and replacing multiple lines).

Edit 2:

Here's a breakdown of the main line sed

:

sed -n "/@ForTestingOnly/,$ { /[{}]/ s/\([{}]\)/\1\n/g;ta;b;:a;p;=}" "$file"

      



  • -n

    - print lines only when explicitly requested
  • /@ForTestingOnly/,$

    - from the line containing "@ForTestingOnly" to the end of the file
  • s/ ... / ... /g

    perform global (primary) substitution
  • \( ... \)

    - capture
  • [{}]

    - the characters that appear in the list mean square brackets
  • \1\n

    - replace what was captured plus newline character
  • ta

    - if a replacement was made, answer the label "a"
  • b

    - branch (no label means "to the end" and starts looping again for the next line) - this branch functions like an "else" for ta

    , I could use T

    instead ta;b;:a

    , but some versions sed

    don't supportT

  • :a

    - label "a"
  • p

    - print a string (actually, print a template buffer, which now consists of possibly several lines with "{" or "}" on each)
  • =

    - print the current line number of the input file

The second command sed

simply says to delete lines that start with the one that has the target line and end at the line found in the loop while

.

The command sed

at the top that I commented out says to find the target line and print the line number it is on and exit. This line is unnecessary, as the main command sed

takes care of running in the right place.

The inner loop while

looks at the output of the main command sed

and increments the counts for each curly brace. When the counters match, it stops.

The outer loop while

runs through all files in the current directory.

+2


a source


I fixed bugs in the old version. Newer versions have two scripts: awk script and bash.

Driver:

#!/bin/bash

AWK_SCRIPT=ann.awk

for i in $(find . -type f -print); do
    while [ 1 ]; do
        cmd=$(awk -f $AWK_SCRIPT $i)
        if [ -z "$cmd" ]; then
            break
        else
            eval $cmd
        fi
    done
done

      



new awk script:

BEGIN {
# line number where we will start deleting
start = 0;
}

{
        # check current line for the annotation
        # we're looking for
        if($0 ~ /@ForTestingOnly/) {
                start = NR;
                found_first_open_brace = 0;
                num_open = 0;
                num_close = 0;
        }

        if(start != 0) {
                if(num_open == num_close && found_first_open_brace == 1) {
                        print "sed -i \'\' -e '" start "," NR " d' " ARGV[1];
                        start = 0;
                        exit;
                }
                for(i = 1; i <= length($0); i++) {
                        c = substr($0, i, 1);
                        if(c == "{") {
                                found_first_open_brace = 1;
                                num_open++;
                        }
                        if(c == "}") {
                                num_close++;
                        }
                }
        }
}

      

Set the path to the awk script in the driver, then run the driver in the root directory.

0


a source







All Articles