Tobi
Tobi

Reputation: 21

Bash Globbing Pattern Matching for Imagemagick recursive convert to pdf

I have the following 2 scripts, that recursively convert folders of images to pdf's for my wifes japanese manga kindle using find and Imagemagick convert:

#!/bin/bash
_d="$(pwd)"
echo "$_d"
find . -type d -exec echo "Will convert in the following order: {}" \;
find . -type d -exec echo "Converting: '{}'" \;  -exec convert '{}/*.jpg' "$_d/{}.pdf" \; 

and the same for PNG

#!/bin/bash
_d="$(pwd)"
echo "$_d"
find . -type d -exec echo "Will convert in the following order: {}" \;
find . -type d -exec echo "Converting: '{}'" \;  -exec convert '{}/*.png' "$_d/{}.pdf" \; 

Unfortunately I am not able make one universal script that works for all image formats. How do I make one script that works for both ? I would also need JPG,PNG as well as jpeg,JPEG

Thx in advance

Upvotes: 1

Views: 490

Answers (5)

Tobi
Tobi

Reputation: 21

@shawn Your solution works, just as I stated in the comments, I am to stupid to name the resulting pdf properly (folder name) and save in the script caller directory. Nevertheless, it solves my case insensitive jpg, jpeg, png problems just fine. Here is shawns solution:

#!/bin/bash

# enable recursive globs
shopt -s globstar nocaseglob extglob 

for dir in **/*/; do
    printf "Converting (jpg|jpeg|png) in %s\n" "$dir"
    convert "$dir"/*.@(jpg|jpeg|png) "$dir/out.pdf"
done

@jhnc Your solution works out of the box, it does exactly what I intended, and I really like calling functions, or even standalone scripts to increase complexity. One drawback is, that I can not Ctrl-c the process, because it is thereby threaded, or runs in a subshell ? I think you were missing an exit statement at the end of the function, it never stopped.

#!/bin/bash

do_convert()(
    shopt -s nullglob
    for dir in "$@"; do
        files=("$dir"/*.{jpg,JPG,png,PNG,jpeg,JPEG})
        if [[ -z $files ]]; then
            echo 1>&2 "no suitable files in $dir"
            continue
        fi
        echo "Converting $dir"
        convert "${files[@]}" "$dir.pdf"
    done
    exit
)
export -f do_convert

pwd

echo "Will convert in the following order:"
find . -type d

# find . -type d -exec bash -c 'do_convert {}' \;
find . -type d -exec bash -c 'do_convert "$@"' -- {} \+ 

@ everyone else, it's already after midnight again, I guess this is a trivial question for you guys, and I am very grateful for your ALL your answers, I didn't have the time to try everything.

I find linux bash very challenging.

Upvotes: 1

Shakiba Moshiri
Shakiba Moshiri

Reputation: 23784

how about a one-liner

dry-run

find -name \*.jpg -or -name \*.png | xargs -I xxx echo  "xxx =>" xxx.pdf

run

find -name \*.jpg -or -name \*.png | xargs -I xxx echo xxx xxx.pdf

help

  • -name match name
  • -or logical or => both jpg and png
  • xargs map input into a name to execute a command on
  • -I select a name, it is like {} in file

NOTE

  • instead of $(pwd) which is a command substitution you can use variable $PWD
  • xxx maps into a name and xxx.pdf still has the matched extension found by find. which means filename.png becomes filename.png.pdf. If this is not desired, you can sed it
  • to run convert command in parallel you can use -P 0 with xargs -- see xargs --help

With sed to remove extensions

dry-run

find -name \*.jpg -or -name \*.png | sed 's/.\(png\|jpg\)$//g' | xargs -I xxx echo "xxx =>" xxx.pdf

Upvotes: 1

xpusostomos
xpusostomos

Reputation: 1639

A lot of ways to skin this cat. My thought is:

for F in `find . -type f -print`
do
  TYPE=`file -n --mime-type $F`
  if [ "$TYPE" = image/png ]
  then
     ## do png conversion here
  elif [ "$TYPE" = image/jpg ]
  then
     ## do jpg conversion here
  fi
done

Upvotes: -2

Shawn
Shawn

Reputation: 52344

I wouldn't use find at all, just a loop:

#!/use/bin/env bash

# enable recursive globs
shopt -s globstar

for dir in **/*/; do
    printf "Converting jpgs in %s\n" "$dir"
    convert "$dir"/*.jpg "$dir/out.pdf"
done

If you want to combine .jpg and .JPG in the same pdf, add nocaseglob to the shopt line. Add .jpeg to the mix? Add extglob and change "$dir"/*.jpg to "$dir"/*.@(jpg|jpeg)

Upvotes: 2

jhnc
jhnc

Reputation: 16662

You can do more complicated actions if you turn the find exec into a bash function (or even a standalone script).

#!/bin/bash

do_convert()(
    shopt -s nullglob
    for dir in "$@"; do
        files=("$dir"/*.{jpg,JPG,PNG,jpeg,JPEG})
        if [[ -z $files ]]; then
            echo 1>&2 "no suitable files in $dir"
            continue
        fi
        echo "Converting $dir"
        convert "${files[@]}" "$dir.pdf"
    done
)
export -f do_convert

pwd

echo "Will convert in the following order:"
find . -type d

# find . -type d -exec bash -c 'do_convert {}' \;
find . -type d -exec bash -c 'do_convert "$@"' -- {} \+ 
  • nullglob makes *.xyz return nothing if there is no match, instead of returning the original string unchanged
  • p/*.{a,b,c} expands into p/*.a p/*.b p/*.c before the * are expanded
  • x()(...) instead of the more normal x(){...} uses a subshell so we don't have to remember to unset nullglob again or clean up any variable definitions
  • export -f x makes function x available in subshells
  • we skip conversion if there are no suitable files
  • with the slightly more complicated find command, we can reduce the number of invocations of bash (probably doesn't save a great deal in this particular case)

Upvotes: 1

Related Questions