2

I'm trying to write a bash script to put text at the beginning of all files in all subdirectories only if:

  1. The file does not contain that text
  2. The file contains the company's name, the word River, or the word Systems

Here's what I have so far:

for i in $(find -name *.cpp -type f -print)
do 
if ! grep -r 'This document contains technical data' $i && grep -r 'company name' $1 || grep-r 'River' $i || grep -r 'Systems' $i; then
#add text

Right now, the file that does not contain any keywords is not getting altered (as expected), but for files that have the text already and the keywords, they get altered. I tried using [] around the || statements, but the result was the same. Any suggestions?

jesse_b
  • 37,005

2 Answers2

3
find . -name '*.cpp' -type f \
  ! -exec grep -qF 'This document contains technical data' {} ';' \
  -exec grep -qFe River -e 'company name' -e Systems {} ';' \
  -exec sed -i '1i\
This document contains technical data' {} +

(replace sed -i with sed -i '' with FreeBSD/macos' sed)

Or more efficiently with GNU grep and GNU xargs:

grep --include='*.cpp' -rFlZ -e River -e 'company name' -e Systems . |
  xargs -r0 grep -FLZ 'This document contains technical data' |
  xargs -r0 sed -i '1i\
This document contains technical data.'

Some notes about your attempts:

  • find -print's output cannot be post-processed.
  • parameter expansions must be quoted in bash
  • -r (a GNU extension), is for recursive grep, for grep to do find's job in addition to its own job, you'd only want to use it when grepping directories (for it to find files in them, and then find the pattern in those files).
  • * is a special character to the shell, it triggers globbing there. You should make sure it's quoted so a literal *.cpp be passed to find.
  • if you want to check whether a pattern can be matched in a file, without needing to see all the lines where it's matched, use the -q option. Not only does it skip the printing, but it's also more efficient as grep stops looking at the first match. -l and -L (a GNU extension) have the same optimisation. The former prints the path of the file if the pattern is found and the latter if it's not found.
  • to search for Fixed strings (as opposed to do regular expression matching), use the -F option, then you don't need to escape regular expression operators.
  • no need to do grep foo file || grep bar file (or with find, '(' -exec grep -q foo {} ';' -o -exec grep -q bar {} ';' ')'), grep can find several pattern in a or fashion in one go with grep -e foo -e bar file or other approaches.
  • note that grep -F River also matches in Riverside or WindRiver. You can add the -w option to match only whole words (where words have to be delimited by non-word characters, word characters being alnums or underscores and non-words anything else).
2

This worked once I replaced the && with another if statement:

if ! grep -r 'This document contains technical data' $i; then
  if grep -r 'company name' $1 || grep-r 'River' $i || grep -r 'Systems' $i; then
     #add text
  fi
fi