6

I want to loop over files with

for f in `grep -rsl "foo" . `: do sed -i -- "s/foo/bar/g" $f; done;

But since filenames contain spaces, a filename is split whenever a space is found.

How can I pass the filename with its spaces to the do block?

1

2 Answers 2

6

For dealing with difficult file names, it is best to separate the file names with NUL characters. GNU grep supports this with the --null option and xargs supports this with the -0 option. Thus, try:

grep --null -rslZ "foo" | xargs -0 sed -i -- "s/foo/bar/g"

Using a shell loop

grep --null -rslZ "foo" | while IFS= read -r -d $'\0' file
    do 
        sed -i -- "s/foo/bar/g" "$file"
    done
8
  • So no way to make it work with for..in..do..done ?
    – doc_id
    Apr 22, 2015 at 19:32
  • @slhck Thanks. I updated the answer to use --null which is supported by both GNU and FreeBSD. The OpenBSD man page however does not list any support for NUL.
    – John1024
    Apr 22, 2015 at 19:34
  • @rahmanisback No. You can safely forget about for for looping over filenames with spaces.
    – lcd047
    Apr 22, 2015 at 19:36
  • 1
    @rahmanisback No, that's an anti-pattern. When you're dealing with files, it only works properly on glob expansions, e.g. for f in *.txt. Don't try to do anything fancy there—it'll almost certainly fail.
    – slhck
    Apr 22, 2015 at 19:36
  • For standard greps and the like: find . -type f -print0 | xargs -0 grep -sl 'foo' | tr '\n' '\0' | xargs -0 sed -i -e 's/foo/bar/g' --
    – lcd047
    Apr 22, 2015 at 19:36
0

You can safely forget about for for looping over filenames with spaces.

Not necessarily...

IFS=$'\n' && for NAV in `ps aux | egrep -o 'Google Chrome' | sort | uniq`; do
   open -a "$NAV" http://www.google.com
done

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .