0

I'm trying to put together a bash script that will search a bunch of files and if it finds a particular string in a file, it will add a new line on the line after that string and then move on to the next file.

#! /bin/bash
echo "Creating variables"
SEARCHDIR=testfile
LINENUM=1
find $SEARCHDIR* -type f -name *.xml | while read i; do
echo "Checking $i"

  ISBE=`cat $i | grep STRING_TO_SEARCH_FOR`
   if [[ $ISBE =~ "STRING_TO_SEARCH_FOR" ]] ; then
     echo "found $i"
     cat $i | while read LINE; do
      ((LINENUM=LINENUM+1))
      if [[ $LINE == "<STRING_TO_SEARCH_FOR>" ]] ; then
        echo "editing $i"
        awk -v "n=$LINENUM" -v "s=new line to insert" '(NR==n) { print s } 1' $i 
      fi
  done
  fi
LINENUM=1
done

the bit I'm having trouble with is

awk -v "n=$LINENUM" -v "s=new line to insert" '(NR==n) { print s } 1' $i 

if I just use $i at the end, it will output the content to the screen, if I use $i > $i then it will just erase the file and if I use $i >> $i it will get stuck in a loop until the disk fills up.

any suggestions?

fredtantini
  • 15,966
  • 8
  • 49
  • 55
vmos
  • 109
  • 1
  • 2
  • 10
  • 1
    Then just redirect to a temp file and then move it to your original one. Also, I guess your code could be reduced a lot using the right tools. A `find` and `awk` should be more or less enough, whereas you use many different things. – fedorqui Dec 30 '14 at 14:44

3 Answers3

1

Unfortunately awk dosen't have an in-place replacement option, similar to sed's -i, so you can create a temp file and then remove it:

awk '{commands}' file > tmpfile && mv tmpfile file

or if you have GNU awk 4.1.0 or newer, the -i inplace is added, so you can do:

awk -i inplace '{commands}' file

to modify the original

Community
  • 1
  • 1
user000001
  • 32,226
  • 12
  • 81
  • 108
1

If you can, maybe use a temporary file?

~$ awk ... $i > tmpfile
~$ mv tmpfile $i

Or simply awk ... $i > tmpfile && mv tmpfile $i

Note that, you can use mktemp to create this temporary file.

Otherwise, with sed you can insert a line right after a match:

~$ cat f
auie
nrst
abcd
efgh
1234

~$ sed '/abcd/{a\
new_line
}' f
auie
nrst
abcd
new_line
efgh
1234

The command search if the line matches /abcd/, if so, it will append (a\) the line new_line.

And since sed as the -i to replace inline, you can do:

if [[ $ISBE =~ "STRING_TO_SEARCH_FOR" ]] ; then
     echo "found $i"
     echo "editing $i"
     sed -i "/STRING_TO_SEARCH_FOR/{a
\new line to insert
}" $i 
fi
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
fredtantini
  • 15,966
  • 8
  • 49
  • 55
  • 2
    it is best to use `awk '...' file > tmp && mv tmp file`, like user00...0001 did. The `&&` trick prevents overwriting `file` with some bad output in case `awk` failed. – fedorqui Dec 30 '14 at 14:49
  • 1
    Thanks, I went with the sed option. I found I also had a problem with my loop but the sed method allowed me to uncover it. – vmos Dec 31 '14 at 15:12
1
#cat $i | while read LINE; do
#  ((LINENUM=LINENUM+1))
#  if [[ $LINE == "<STRING_TO_SEARCH_FOR>" ]] ; then
#    echo "editing $i"
#    awk -v "n=$LINENUM" -v "s=new line to insert" '(NR==n) { print s } 1' $i 
#  fi
# done

# replaced by
sed -i 's/STRING_TO_SEARCH_FOR/&\n/g' ${i}

or use awk in place of sed

also

# ISBE=`cat $i | grep STRING_TO_SEARCH_FOR`
# if [[ $ISBE =~ "STRING_TO_SEARCH_FOR" ]] ; then
#by
if [ $( grep -c 'STRING_TO_SEARCH_FOR' ${i} ) -gt 0 ]; then
# if file are huge, if not directly used sed on it, it will be faster (but no echo about finding the file)
NeronLeVelu
  • 9,908
  • 1
  • 23
  • 43