102

I am using egrep -R followed by a regular expression containing about 10 unions, so like: .jpg | .png | .gif etc. This works well, now I would like to replace all strings found with .bmp

I was thinking of something like

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

so the issue here is how do I do a similar union regular expression in sed and how do I tell it to save the changes to the file that it got the input from.

2
  • 2
    I found this question while searching for ways to search and replace across multiple files in a directory hierarchy. For others in my situation, try rpl. Commented May 17, 2011 at 23:23
  • thank you rpl works and is really easy to remember.. just rpl old_string new_string target_files. Commented Jun 24, 2015 at 14:23

6 Answers 6

197

Use this command:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: find matching lines using extended regular expressions

    • -l: only list matching filenames

    • -R: search recursively through all given directories

    • -Z: use \0 as record separator

    • "\.jpg|\.png|\.gif": match one of the strings ".jpg", ".gif" or ".png"

    • .: start the search in the current directory

  • xargs: execute a command with the stdin as argument

    • -0: use \0 as record separator. This is important to match the -Z of egrep and to avoid being fooled by spaces and newlines in input filenames.

    • -l: use one line per command as parameter

  • sed: the stream editor

    • -i: replace the input file with the output without making a backup

    • -e: use the following argument as expression

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': replace all occurrences of the strings ".jpg", ".gif" or ".png" with ".bmp"

Sign up to request clarification or add additional context in comments.

7 Comments

it all works except the | in the sed part. I don't understand why though since it makes sense... the -l part of xargs was giving me errors so I took it out, could that be related?
I found that this command adds a newline to the end of all the files that it processes.
@titanumdecoy: I wasn't able to reproduce this behaviour. what version of sed were you using and on which OS are you?
@DavidSchmitt: You probably want to use sed -r for extended regular expressions. At that point, the pattern will match what's used in egrep, and you may want to put it in a variable for reuse.
On OSX you have to provide sed -i with an empty file e.g.: sed -i '' to properly change stuff in place.
|
12

Honestly, much as I love sed for appropriate tasks, this is definitely a task for perl -- it's truly more powerful for this kind of one-liners, especially to "write it back to where it comes from" (perl's -i switch does it for you, and optionally also lets you keep the old version around e.g. with a .bak appended, just use -i.bak instead).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

rather than intricate work in sed (if even possible there) or awk...

2 Comments

sed uses -i, just like perl.
@Stobor - I swear I've had issues where the perl operation when I fed the regex replacement string did exactly what I wanted, unlike sed, even if I gave the regex option to sed.. I think I either forgot some flags to sed or it had limitations.
11

Another way to do this

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Some help regarding the above command

The find will do the find for you on the current directory indicated by .

-name the name of the file in my case its pom.xml can give wild cards.

-exec execute

sed stream editor

-i ignore case

s is for substitute

/4.6.0.../ String to be searched

/5.0.0.../ String to be replaced

2 Comments

perhaps not as powerful - but this is a lot easier for me to understand than the accepted answer
@icc97 - I agree that it's easier to understand, but I can't think of any reason why this would be less powerful.
6

I couldn't get any of the commands on this page to work for me: the sed solution added a newline to the end of all the files it processed, and the perl solution was unable to accept enough arguments from find. I found this solution which works perfectly:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

This will recurse down the current directory tree and replace search_regex with replacement_string in any files ending in .h or .m.

I have also used rpl for this purpose in the past.

Comments

1

try something using a for loop

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

not pretty, but should do.

2 Comments

xargs is definitely more appropriate here.
and using the |while read i pattern would enable streaming and avoid length restrictions when egrep's results become too long
1

My use case was I wanted to replace foo:/Drive_Letter with foo:/bar/baz/xyz In my case I was able to do it with the following code. I was in the same directory location where there were bulk of files.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

hope that helped.

UPDATE s|foo:/Drive_letter:|foo:/ba/baz/xyz|g

1 Comment

You can use other delimiters for the sed command, and doing so makes pathnames much nicer: sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.