1

In a bash script I am rasterizing a PDF page by page into single files, in the end the single resulting PNGs are merged again into a PDF like this:

convert -monitor /path/to/1.png /path/to/2.png /path/to/3.png ... output.pdf

The only problem that script still has is the inability to correctly handle files with spaces. Here are some of the things I tried:

newfile=$(sed -r -e 's| |\\ |g' <<< "$tmppath$curr.png")
echo "DEBUG: newfiles : $newfiles"
filearray[$curr-1]="$newfiles"
echo "DEBUG: filearray: ${filearray[*]}"

This yields (per page/file):

DEBUG: newfiles : /tmp/pngpdf/file\ with\ spaces/1.png
DEBUG: filearray: /tmp/pngpdf/file\ with\ spaces/1.png

Later on, I have two debug messages

echo "DEBUG: filearray: ${filearray[*]}"
echo "DEBUG:            ${filearray[0]}, ${filearray[1]}, ${filearray[2]}, ..."

to see how filearray looks like with multiple files/pages:

DEBUG: filearray: /tmp/pngpdf/file\ with\ spaces/1.png /tmp/pngpdf/file\ with\ spaces/2.png /tmp/pngpdf/file\ with\ spaces/3.png /tmp/pngpdf/file\ with\ spaces/4.png /tmp/pngpdf/file\ with\ spaces/5.png
DEBUG:            /tmp/pngpdf/file\ with\ spaces/1.png, /tmp/pngpdf/file\ with\ spaces/2.png, /tmp/pngpdf/file\ with\ spaces/3.png, ...

And I can clearly see the following:

  1. For every file there is exactly one array element.
  2. Every space is preceded by a \.

I am putting the whole command into a variable first to see what would get executed:

execcmd="convert -monitor ${filearray[@]} output.pdf"

An example might look like that:

convert -monitor /tmp/pngpdf/file\ with\ spaces/1.png /tmp/pngpdf/file\ with\ spaces/2.png /tmp/pngpdf/file\ with\ spaces/3.png /tmp/pngpdf/file\ with\ spaces/4.png /tmp/pngpdf/file\ with\ spaces/5.png output.pdf

But executing that with $execcmd convert is throwing numerous errors at me:

convert.im6: unable to open image `/tmp/pngpdf/file\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `/tmp/pngpdf/file\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `with\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `with\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `spaces/1.png': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: unable to open file `spaces/1.png' @ error/png.c/ReadPNGImage/3667.
convert.im6: unable to open image `/tmp/pngpdf/file\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `/tmp/pngpdf/file\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `with\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `with\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `spaces/2.png': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: unable to open file `spaces/2.png' @ error/png.c/ReadPNGImage/3667.
convert.im6: unable to open image `/tmp/pngpdf/file\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `/tmp/pngpdf/file\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `with\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `with\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `spaces/3.png': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: unable to open file `spaces/3.png' @ error/png.c/ReadPNGImage/3667.
convert.im6: unable to open image `/tmp/pngpdf/file\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `/tmp/pngpdf/file\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `with\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `with\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `spaces/4.png': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: unable to open file `spaces/4.png' @ error/png.c/ReadPNGImage/3667.
convert.im6: unable to open image `/tmp/pngpdf/file\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `/tmp/pngpdf/file\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `with\': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: no decode delegate for this image format `with\' @ error/constitute.c/ReadImage/544.
convert.im6: unable to open image `spaces/5.png': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: unable to open file `spaces/5.png' @ error/png.c/ReadPNGImage/3667.
convert.im6: no images defined `output.pdf' @ error/convert.c/ConvertImageCommand/3044.

Obviously, it does not recognize what I want to do correctly. The backslashes are themselves escaped and, thus, the spaces re-gain their argument-delimiting powers. When putting that command into bash directly it runs smoothly (as expected):

$ convert -monitor /tmp/pngpdf/file\ with\ spaces/1.png /tmp/pngpdf/file\ with\ spaces/2.png /tmp/pngpdf/file\ with\ spaces/3.png /tmp/pngpdf/file\ with\ spaces/4.png /tmp/pngpdf/file\ with\ spaces/5.png output.pdf
Load/Image//tmp/pngpdf/file with spaces[1.png]: 584 of 585, 100% complete
Load/Image//tmp/pngpdf/file with spaces[2.png]: 584 of 585, 100% complete
Load/Image//tmp/pngpdf/file with spaces[3.png]: 584 of 585, 100% complete
Load/Image//tmp/pngpdf/file with spaces[4.png]: 584 of 585, 100% complete
Load/Image//tmp/pngpdf/file with spaces[5.png]: 584 of 585, 100% complete
Mogrify/Image//tmp/pngpdf/file with spaces[5.png]: 4 of 5, 100% complete
resize image[output.pdf]: 180 of 181, 100% complete
resize image[output.pdf]: 180 of 181, 100% complete
resize image[output.pdf]: 180 of 181, 100% complete
resize image[output.pdf]: 180 of 181, 100% complete

That leaves me very puzzled and I have no idea how to fix that. I tried saving the single filenames quoted in filearray as well such that I would get DEBUG: filearray: "/tmp/pngpdf/file with spaces/1.png" "/tmp/pngpdf/file with spaces/2.png" "/tmp/pngpdf/file with spaces/3.png" "/tmp/pngpdf/file with spaces/4.png" "/tmp/pngpdf/file with spaces/5.png" to no avail. Probably bash escapes these special characters as well. I am sure that it must have to do with how bash works, but it seems my understanding is not yet good enough to fix that on my own. So, I'd be very glad if someone could enlighten me. :)

PS: I've been looking for an answer to my question in several places (Shell script issue with filenames containing spaces, Using a generated list of filenames as argument list — with spaces, Looping through files with spaces in the names?, Bash scripting and files with spaces in them, Why does my shell script choke on whitespace or other special characters?, Trouble in script with spaces in filename). Unfortunately, I was not able to locate an answer to my question in there. Maybe it's just too late ;)

Kreuvf
  • 123
  • Have you tried using find with the -exec option? – saiarcot895 Jun 26 '15 at 22:59
  • @saiarcot895: No, I have not. Will do after some rest; if it works, it's good, but also just a workaround. :/ I am looking for the way it's meant to be done in bash. – Kreuvf Jun 26 '15 at 23:07

1 Answers1

5

execcmd="convert -monitor ${filearray[@]} output.pdf"

But executing that with $execcmd convert is throwing numerous errors at me

Don't call $execcmd, as it has already lost the differentiation between filenames and space-separated parts of filenames. Instead, execute the command itself with quoted arguments:

    convert -monitor "${filearray[@]}" output.pdf
Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • 1
    Wow, I was quite sure that I already tried that (and it didn't work). The question now is: why does that work? Shouldn't this just expand the whole array and pass the whole of it as one and only one argument? – Kreuvf Jun 26 '15 at 23:12
  • 1
    No. "$@" and "${var[@]}" are specially handled to expand as a set of zero or more quoted values. This is distinct from "$*", for example, which expands the argument list but leaves it as a single quoted value. It's the same situation that allows for A in "$@"; do echo "Arg <$A>"; done to iterate across the command line arguments. – Chris Davies Jun 27 '15 at 00:13