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 ...
a source to share
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" forta
, I could useT
insteadta;b;:a
, but some versionssed
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.
a source to share
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.
a source to share