Search code examples
bashfindshrefactoringexec

How to get bash find exec to directly execute commands instead of using a temp file?


I've written this BASH script:

find ./build/html -name '*.html' \( -exec echo ../emojize_pngorsvg.py \"{}\" \> \"{}.emojized\" \&\& rm \"{}\" \&\& mv \"{}.emojized\" \"{}\" >> ../emojize_commands.sh \; \)
cat ../emojize_commands.sh
chmod +x ../emojize_commands.sh
../emojize_commands.sh

It loops over all of the HTML files it finds in the ./build/html dir and it creates a single file called emojize_commands.sh which looks something like this:

../emojize_pngorsvg.py "./build/html/Home/Technical/Guides/Windows/Force-activate-a-Windows-evaluation/index.html" > "./build/html/Home/Technical/Guides/Windows/Force-activate-a-Windows-evaluation/index.html.emojized" && rm "./build/html/Home/Technical/Guides/Windows/Force-activate-a-Windows-evaluation/index.html" && mv "./build/html/Home/Technical/Guides/Windows/Force-activate-a-Windows-evaluation/index.html.emojized" "./build/html/Home/Technical/Guides/Windows/Force-activate-a-Windows-evaluation/index.html"
../emojize_pngorsvg.py "./build/html/Home/Technical/Guides/Windows/Various-Windows-Tips/index.html" > "./build/html/Home/Technical/Guides/Windows/Various-Windows-Tips/index.html.emojized" && rm "./build/html/Home/Technical/Guides/Windows/Various-Windows-Tips/index.html" && mv "./build/html/Home/Technical/Guides/Windows/Various-Windows-Tips/index.html.emojized" "./build/html/Home/Technical/Guides/Windows/Various-Windows-Tips/index.html"
../emojize_pngorsvg.py "./build/html/Home/Technical/Guides/Windows/Configure-RDP-to-connect-with-stored-credentials/index.html" > "./build/html/Home/Technical/Guides/Windows/Configure-RDP-to-connect-with-stored-credentials/index.html.emojized" && rm "./build/html/Home/Technical/Guides/Windows/Configure-RDP-to-connect-with-stored-credentials/index.html" && mv "./build/html/Home/Technical/Guides/Windows/Configure-RDP-to-connect-with-stored-credentials/index.html.emojized" "./build/html/Home/Technical/Guides/Windows/Configure-RDP-to-connect-with-stored-credentials/index.html"
# Much more lines here

Then it executes emojize_commands.sh - which works perfectly as expected!

I'm trying to remove the need for it to use a temporary file to do all of this. I'd rather it just execute the commands directly instead of needing to echo them to a file first.

I've tried removing echo and >> ../emojize_commands.sh (plus all of the other lines of code) so I am left with just this:

find ./build/html -name '*.html' \( -exec ../emojize_pngorsvg.py \"{}\" \> \"{}.emojized\" \&\& rm \"{}\" \&\& mv \"{}.emojized\" \"{}\" \; \)

But when I run it I get a bunch of "file not found" errors!

How can I get find to work how I want please?

The actual emoji script (emojize_pngorsvg) is taken from this question.


Solution

  • You could have the -exec run each line via bash -c instead of echoing it to a file to be run later. That would also enable (require, in fact) adjusting your quoting, ultimately allowing a simpler and clearer command. For example:

    find ./build/html -name '*.html' \
      -exec bash -c '../emojize_pngorsvg.py "{}" > "{}.emojized" && mv "{}.emojized" "{}"' \;
    

    I took the liberty of some additional simplification, too. Specifically, it is unnecessary to remove the original file before mving the new one to the original name. Also, the \( and \) in the original command served no useful purpose, so I dropped them.

    Also, I generally pipe find's output into xargs instead of using an -exec action. You could consider that, but it probably does not provide enough advantage in this case to justify the conversion.