Search code examples
findzshexecbasename

Execute a comand for each file within a directory


I need to convert a series of images from one format to another, but only the images within $PWD, ignoring anything in subdirectories. But I need to remove the original extension or suffix, in order to pass it to another software down the pipeline. I'm trying to use the following command

find . -type f -iname "*.pdf" -exec convert {} $(basename {} .pdf).png \;

The intention is to keep the *.pdf files, and have new *.png files. However, the resulting files instead end up with the extension .pdf.png

From what I understand, the $(basename {} .pdf) is being executed before the find command, resulting in a literal {}, and that's why the output of convert is {}.png.

Should I add any particular combination of "`´', or maybe an explicit call to sh -c to make this work?

I'm using zsh as my shell

Any help is appreciated

Edit: As pointed in the comments, the code and my accepted answer will process files in any possible subdirectories. To avoid this (whis is my original intention, although I don't have any subdirs)

find . -maxdepth 1 -iname "*.pdf" -exec zsh -c 'convert $1 ${1:r}.png' _ {} \;


Solution

  • Using find:

    find . -type f -iname "*.pdf" -exec zsh -c 'convert $1 ${1:r}.png' _ {} \;
    

    In just the current directory, not any subdirectories:

    find . -maxdepth 1 -type f -iname "*.pdf" -exec zsh -c 'convert $1 ${1:r}.png' _ {} \;
    

    Using a shell loop. Unlike find, this does not launch a subshell for each file:

    setopt extendedglob
    for fl in (#i)**/*.pdf; do
        convert $fl ${fl:r}.png
    done
    

    In only the current directory:

    ...
    for fl in (#i)*.pdf; do
    ...
    

    Using a loop in an anonymous function so if fits comfortably in a single line
    (directory and subdirectories / current directory only):

    (){for a; convert $a $a:r.png} **/*.(pdf|PDF)
    
    (){for a; convert $a $a:r.png} *.(pdf|PDF)
    

    Using zmv
    (recursive directory and subdirectories / current directory only):

    autoload zmv # can be in ~/.zshrc
    zmv -p convert '(#i)(*/)#*.pdf' '${f:r}.png'
    
    zmv -p convert '(#i)*.pdf' '${f:r}.png'
    

    The r (root) expansion modifier is described in the history expansion section of the zsh manual. Like basename, it removes the extension from a filename.