bashscript for file search and replace!
Hey I t开发者_开发百科ry to write a littel bash script. This should copy a dir and all files in it. Then it should search each file and dir in this copied dir for a String (e.g @ForTestingOnly) and then this save the line number. Then it should go on and count each { and } as soon as the number is equals it should save againg the line number. => it should delete all the lines between this 2 numbers. I'm trying to make a script which searchs for all this annotations and then delete the method which is directly after this ano. Thx for help...
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 the @ForTestingOnly anot. but I like to delete this and the following method...
Give this a try. It's oblivious to braces in comments and literals, though, as David Gelhar warned. It only finds and deletes the first occurrence of the "@ForTestingOnly" block (under the assumption that there will only be 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 to sed
(by commenting out and replacing a few lines).
Edit 2:
Here's a breakdown of the main sed
line:
sed -n "/@ForTestingOnly/,$ { /[{}]/ s/\([{}]\)/\1\n/g;ta;b;:a;p;=}" "$file"
-n
- only print lines when explicitly requested/@ForTestingOnly/,$
- from the line containing "@ForTestingOnly" to the end of the files/ ... / ... /g
perform a global (per-line) substitution\( ... \)
- capture[{}]
- the characters that appear in the list bewteen the square brackets\1\n
- substitute what was captured plus a newlineta
- if a substitution was made, branch to label "a"b
- branch (no label means "to the end and begin the per-line cycle again for the next line) - this branch functions as an "else" for theta
, I could have usedT
instead ofta;b;:a
, but some versions ofsed
don't supportT
:a
- label "a"p
- print the line (actually, print the pattern buffer which now consists of possibly multiple lines with a "{" or "}" on each one)=
- print the current line number of the input file
The second sed
command simply says to delete the lines starting at the one that has the target string and ending at the line found by the while
loop.
The sed
command at the top which I commented out says to find the target string and print the line number it's on and quit. That line isn't necessary since the main sed
command is taking care of starting in the right place.
The inner while
loop looks at the output of the main sed
command and increments counters for each brace. When the counts match it stops.
The outer while
loop steps through all the files in the current directory.
I fixed the bugs in the old version. The new versions has two scripts: an awk script and a bash driver.
The driver is:
#!/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
the new awk script is:
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 dir.
精彩评论