Thursday, April 7, 2022

[SOLVED] modify every file found with `find`

Issue

I have a folder with a bunch of .svg files. I want to modify each svg file inside the folder in the following way:

  • swap #000000 with currentColor
  • add class attribute with value based on filename (eg. for file named icon-super-cool.svg I want class="icon-super-cool"

here's where I've gotten to after hours of googling:

find ./ -name \*.svg -exec sed -i '' -e "s/#000000/currentColor/g;s/<svg/<svg class=\"icon ${name}\"/g" {} +

The colour replace part works fine. When it comes to the class attribute, every single icon get the same class attribute with ${name} of the 1st file it processes.

Any help and explanation of what I am doing wrong would be much appreciated.

PS. working on a mac environment.


Solution

There are several issues with your attempt:

  1. In the exec action of find the name of the found file is represented by {}, not ${name}.
  2. Even with {} the class will end with .svg, not what you want.
  3. Even with {} the class will contain the full file path (./foo/bar/baz.svg), not what you want.

The following assumes you want the class name to be icon-name where name is the basename of the file without the .svg extension:

find . -type f -name '*.svg' -exec bash -c \
  'b=$(basename -s.svg "$1"); \
   sed -i "" "s/#000000/currentColor/g;s/<svg/<svg class=\"icon-$b\"/g" "$1"' _ {} \;

Or, as suggested by Charles, to reduce the number of bash runs:

find . -type f -name '*.svg' -exec bash -c \
  'for f in "$@"; do b=$(basename -s.svg "$f"); \
   sed -i "" "s/#000000/currentColor/g;s/<svg/<svg class=\"icon-$b\"/g" "$f"; done' _ {} +

If performance is really an issue, and you want to process several files at once ({} + instead of {} \;), things are difficult with sed because the class replacement string is not constant (not impossible, though, but this would be horrible). But GNU awk can make it:

find . -type f -name '*.svg' -exec awk -i inplace '\
  BEGINFILE {s=gensub(/^(.*\/)?(.*)\.svg$/,"<svg class=\"icon-\\2\"",1,FILENAME)} \
  {gsub(/#000000/,"currentColor"); gsub(/<svg/,s); print}' {} +

The first block (BEGINFILE) preprocesses each file name to build the class replacement string and the second performs the replacements. This should be the fastest of all (not tested).

The two first solutions should work even with the default versions of find, sed, basename and bash that come with macOS. For the last one you absolutely need a decently recent version of GNU awk (see MacPorts or HomeBrew).



Answered By - Renaud Pacalet
Answer Checked By - Marie Seifert (WPSolving Admin)