Server Fault is a question and answer site for system and network administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a list of files that I want to check for a particular string. If the string exists in the file, I want to replace it with a new version that I have. What is the easiest way of going about this?

share|improve this question

closed as off-topic by mdpc, Jim B, Andrew Schulman, Sven Dec 22 '14 at 7:46

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions must be relevant to professional system administration. Server Fault is dedicated to professional system and network administrators. End user and enthusiast questions are off-topic (contact your system administrator or hire a professional to help you out). Please see the Help Center for more information." – mdpc, Jim B, Andrew Schulman, Sven
If this question can be reworded to fit the rules in the help center, please edit the question.

for file in "`grep -l "foo" bar gazi`"; do cp -p --backup=numbered replace "$file"; done

where replace is the path to the replacement file, bar and gazi represent a list of files to be checked, and foo is the search string of interest.

If you require additional assistance formulating an appropriate regular expression for grep (above) please do not hesitate to ask.

share|improve this answer
+1. But you might want to add "cp $file /tmp/$file.backup" first, just in case things go terribly wrong. – Jed Daniels Dec 1 '10 at 18:32
A very valid point, although this can be streamlined using the --backup operator for cp: cp -p --backup=numbered replace $file which will append ~n~ to a copy of the original $file if it exists before replacing it. – Tok Dec 1 '10 at 18:54
Note that without quotes around the "$file", it will fail for files that have spaces in them. – Sean Reifschneider Dec 1 '10 at 18:59
Good point, updated in the comment. Thanks. – Tok Dec 1 '10 at 19:31
The shell will tokenize the output of the grep on whitespace, so $file will never have a space in it anyway. If grep returns a filename with spaces in it, it will split the parts and set them to file individually. – mark Dec 1 '10 at 19:38

grep can search a file for a string and will return 0 if it finds it, you could use this as part of a conditional to replace the file, something like:

 if [ !`/bin/grep -q teststring /path/to/file` ]; then
    cp newfile oldfile
share|improve this answer
The brackets and backticks aren't necessary. – Dennis Williamson Dec 1 '10 at 23:35

Presumably you have a file with the list of files in it? I don't know if you've got a static file you're replacing matching files with, or a potential replacement per file. This handles the second case, for the first, merely replace $ with the static file name.

for i in `cat /var/tmp/listoffiles` ; do
    if grep string $i > /dev/null 2>&1 ; then
        cp $i $i.old
        cp $ $i
share|improve this answer
+1. And I'd give an extra +1 if I could for the backup to .old, just in case things go badly. – Jed Daniels Dec 1 '10 at 18:33
Note that without quotes around the "$i" this will fail on files that have spaces in it. – Sean Reifschneider Dec 1 '10 at 18:58
Actually, quotes around $i won't protect you in that case since the shell will split the output of cat /var/tmp/listoffiles on spaces. If you have spaces in the names of the files, you have to do things differently...perhaps with a shell function and xargs? – mark Dec 1 '10 at 19:05

While I agree with all of the grep tests, sed can do this without a test really.


If the string isn't there, it won't do anything. Just worth mentioning. From your question I wasn't sure if you were trying to replace the whole file or just a string within it.

share|improve this answer
I believe he is trying to replace the whole file, not just the string. – Jed Daniels Dec 1 '10 at 19:04

The correct way to process files in a loop in a shell script is to pipe the command into while or redirect a file into the done statement of a while loop. This will handle any name except those that contain newlines. You should not use the form for f in $(cmd).

In this example, find is the command that produces the list of files:

find . -type f -name "*.txt" | while read -r filename
    if grep -qs "$string" "$filename"  # or use >/dev/null 2>&1 instead of -qs
        cp "$replacement" "$filename"

or, here the list is contained in a file:

while read -r filename
done < "$file_with_list"

Another method is to use xargs.

For a command that produces the list of files (e.g. find):

find . -type f -name "*.txt" -print0 | xargs -0 -I % env string=$string sh 'grep -qs "$string" "%" && cp replacement_file "%"'

The preceding method will work in cases where filenames may include newlines.

If the list is in a file:

xargs -a "$file_with_list" -I % env string=$string sh 'grep -qs "$string" "%" && cp replacement "%"'

All of these examples will work in cases where filenames may include spaces.

share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.