Tim
Tim

Reputation: 321

replace words in filename BASH complications in OSX

I found this answer to my question which uses the following code to remove words from file names using sed:

for fyle in $(find . -name "*.*")
do 
  mv -i $fyle `echo $fyle | sed -e 's/FOO//gI' -e 's/BANG//gI' `
done

But on my mac it chokes on filenames whose paths have spaces.

I tried to fix it by use of double quotes, but couldn't get it to work: the variable fyle now includes the entire list of files, not one at a time.

Because the original poster seemed happy with the code, maybe my problem is because of my OSX flavour of bash?

How can I modify the code above to work well?

Upvotes: 0

Views: 111

Answers (4)

ghoti
ghoti

Reputation: 46866

As others have said, quotes are your friends.

But since you're using bash, you don't really need to use sed just to remove strings from filenames. Something like this might work, using parameter expansion:

find . -name '*.*' -exec bash -c 'out="${1//FOO/}; out="${out//BANG/}; mv -v "$1" "$out"' -- {} \;

But even find is unnecessary in this case, so you can avoid subshells entirely:

shopt -s globstar
for f in **/*FOO*.* **/*BANG*.*; do
  out=${f//FOO/}
  out=${out//BANG/}
  mv -v "$f" "$out"
done

This of course assumes that the keywords you're searching for are before a dot in the filename. It also assumes that you're running bash v4, for globstar.

Upvotes: 4

miken32
miken32

Reputation: 42712

That seems very complicated. Will this work? Uses Bash's built-in string substitution and file globbing to replace bar with baz:

for i in *.txt; do mv "$i" "${i/bar/baz}"; done

Here it is in action:

mike ~/foobar $ touch "foo bar baz.txt"
mike ~/foobar $ touch "foobar.txt"
mike ~/foobar $ touch "carbar.txt"
mike ~/foobar $ for i in *.txt; do mv "$i" "${i/bar/baz}"; done
mike ~/foobar $ ls
carbaz.txt      foo baz baz.txt foobaz.txt
mike ~/foobar $

As chepner mentioned below, this will not recurse into directories.

Upvotes: 0

anubhava
anubhava

Reputation: 785611

You should not iterate the find command's output in for loop otherwise shell expansion for filenames with space/newline will occur and your results will be wrong.

Use -print0 option in find and iterate using process substitution in a while read loop with null IFS.

while IFS= read -rd '' fyle; do
   mv -i "$fyle" $(sed 's/FOO//gI; s/BANG//gI' <<< "$fyle")
done < <(find . -type f -name "*.*")

Upvotes: 4

macgregor
macgregor

Reputation: 147

I believe you need to double-quote the variable reference in the mv command to account for the spaces...

mv -i "$fyle" ...

Upvotes: 2

Related Questions