Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Here's my command (break intentional):

grep FOO "/Users/gjtorikian/blah" -l | xargs sed -i '' '/FOO/{s/FOO/BAR/g; w /dev/stdout
}'

At the high-level: grep for FOO in the blah directory; pipe in just the filename (because of -l) to sed; sed performs an inline replace (-i '') and prints only the changed term to /dev/stdout.

If I were to omit the -l and pipe, I get this back from grep:

/Users/gjtorikian/blah/baz.cs:1:FOO
/Users/gjtorikian/blah/bar.js:1:FOO

What I want is sed to perform the inline replace, and then show me the file and term replaced; for example:

/Users/gjtorikian/blah/baz.cs:1:BAR
/Users/gjtorikian/blah/bar.js:1:BAR

Is such a thing possible? If it matters, I would prefer to keep it with only grep/sed. Do I have to do a second grep after the sed ?

share|improve this question
    
I don't see a way to get the current file name in sed, so you probably will need a second grep. –  Kevin Feb 25 '12 at 20:50
    
Also, you don't need the '', that's the default. –  Kevin Feb 25 '12 at 20:51
2  
On OS X, '' is not the default. –  GJTorikian Feb 25 '12 at 22:57
add comment

3 Answers

One possibility would be to pass the output of grep to a separate sed filter.

grep -l FOO "/Users/gjtorikian/blah/"* |
{ tee /dev/fd/3 |
  xargs sed -i -e '/FOO/{' -e 's/FOO/BAR/g' -e 'w /dev/stdout'
} 3>&1 | sed 's/:FOO$/:BAR/'

You could make sed print the line number (with the = command) when it finds a match and do further postprocessing.

It would probably be clearer to use awk.

for x in "/Users/gjtorikian/blah/"*; do
  awk '
    sub(/FOO/, "BAR") {found=1; print FILENAME ":" NR ":" "BAR" >"/dev/stderr"}
    1 {print}
    END {exit(!found)}
' "$x" >"$x.tmp" && mv "$x.tmp" "$x"
done
share|improve this answer
add comment

I think you're making the problem too complicated by trying to do it in one go. You simply can do a

grep -H -n /Users/gjtorikian/blah | sed 's/\(^.*?:[0-9]+?:\).*FOO.*/\1BAZ/'

to get the list of files with line numbers and replacements (this should work, as long as your filenames don't contain colons, but that's a bad idea in Mac OS anyway...). Afterwards you can issue an

sed -i '' 's/FOO/BAR/g' /Users/gjtorikian/blah

No grep and xarg is needed here (you may do a "find ... | xarg" if you have a lot of files though). If you're concerned about the duplications, you can put the two lines into a script or function and do variable substitutions there.

share|improve this answer
add comment

As I'm not native English speaker I probably didn't get it.

To 'grep' a directory you need '-r'. Usage of '-l' prints just filename and it stops grepping after first occurence.

# pattern=/home ; grep -l "$pattern" /etc/[a-z]* 2>/dev/null | while read line ; do echo "$line:$pattern" ; done
/etc/adduser.conf:/home
/etc/fstab:/home
/etc/fstab~:/home
/etc/libuser.conf:/home
/etc/mpd.conf:/home
/etc/mpd.conf~:/home
/etc/mtab:/home
/etc/netscsid.conf:/home
/etc/passwd:/home
/etc/passwd-:/home
/etc/sudoers:/home
/etc/sudoers.tmp~:/home
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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