1

I intend to compress a few thousand PDF files in a folder recursively.

I tried with following loop:

#!/bin/bash
find "/home/user/original" -type f -name *.pdf | while read -r file
do
  gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/prepress -dNOPAUSE -dBATCH -dQUIET -sOutputFile="/home/user/processed$file" "$file"
done

(processed$file is used because $file brings a / at beginning & I've also tried processed/$file)

Anyway, running the loop gives the following error:

GPL Ghostscript 9.26: **** Could not open the file /home/user/processed/home/user/original/test001.pdf .
**** Unable to open the initial device, quitting.

For some reason its looking for pdf in path/to/output/path/to/input. I tried changing to ./ links instead of / but to no avail.

If I run the following on its own, it outputs a compressed pdf nicely

gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/prepress -dNOPAUSE -dBATCH -dQUIET -sOutputFile="/home/user/processed/output.pdf" "input.pdf"

Any ideas why the loop isn't working?
P.S. All directories are 777 for now to make sure there aren't any permission errors

  • Welcome to U&L! Have you looked at the output of find directly, before piping it to while read ...? – JigglyNaga Jan 14 at 13:52
  • Thnaks. Yes, find outputs list of pdf files nicely. – Umer Jan 14 at 14:01
  • Can you use cd /home/user/original and find . -type f ... instead? That should give you paths starting with ./, which you can use like /home/user/processed/$file (note extra slash). – dirkt Jan 14 at 14:21
  • I'll give that a try. – Umer Jan 14 at 14:24
  • I tried changing to find . -type [...] and decended to /home/user/original and ran the script. It now gives the following error: "Could not open the file /home/user/processed/./filename.pdf". Is this a permission error or is it trying to read in output folder!? – Umer Jan 14 at 14:36
0

After testing a few times, I have observed the following behaviour with Ghostscript. When you specify the output file as /home/user/processed/home/user/original/test001.pdf, the gs command expects that the path leading up to the file (/home/user/processed/home/user/original/) already exists. Since the folder structure from your source does not currently exist at the destination, the command throws an error and shows that it is unable to open the destination file.

To fix that, you can first recreate your folder structure with the following commands:

cd /home/user/original
find . -type d -exec mkdir -p -- /home/user/processed/{} \;

Once this is done, you can run your script to generate the PDF files. I am able to use your gs command to generate a PDF file, so I'm assuming that there are no further problems with it.

After the script's completion, if you suspect that there are empty directories at the destination, and want to get rid of them, use the following find command:

find /home/user/processed/ -type d -empty -delete
  • Ah this works beautifully. Thanks a ton. :) – Umer Jan 15 at 7:17
1

The Problem

By default, find performs the -print action:

 -print

True; print the full file name on the standard output, followed by a newline.

The "full file name" means that you'll see the absolute path to each file:

/home/user/original/test001.pdf
/home/user/original/test002.pdf
...
/home/user/original/test999.pdf

So when you use

gs -sOutputFile="/home/user/processed$file"

...inside the loop, the variable $file contains /home/user/original/test001.pdf, and the whole expression expands to the two paths concatenated:

gs -sOutputFile="/home/user/processed/home/user/original/test001.pdf"

This is reflected by the error message you saw:

Could not open the file /home/user/processed/home/user/original/test001.pdf

Using the basename

If you only want the file's basename (because all files are in the same source folder), you can tell find to use a different output format.

find "/home/user/original" -type f -name *.pdf -printf '%f\n'
   -printf format

True; print format on the standard output, interpreting `\' escapes and `%' directives.

[...]

        \n     Newline.

        %f     File's name with any leading directories removed (only
                 the last element).

Using the relative name

Alternately (if the input files are in different directories), you will need to trim some of the directory path. You could use eg. cut for that:

find "/home/user/original" -type f -name *.pdf | cut -d/ -f5- | while read -r file
do
    gs [...] -sOutputFile="/home/user/processed/$file" "/home/user/original/$file"

This removes everything up to and including the 4th / of the input. However, it won't handle the creation of new output directories to match the structure of the input tree.

  • Ah well that does make sense but unfortunately, I want to maintain directory structure in processed folder as well. /home/user/original/foo/bar/01.pdf should go to /home/user/processed/foo/bar/01.pdf. – Umer Jan 14 at 14:18
  • Also, above doesn't work as find only outputs pdfs without path so I end up getting 3kb pdf files. – Umer Jan 14 at 14:20
  • FYI, this was an extremely helpful reply. It identified the root cause of the error and that really helped. – Umer Jan 16 at 10:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.