I have a collection of pdf files I would like to convert to epub. I know Calibre's command ebook-convert will do the trick (nicely might I add). However I have more than one file.

I'd like to be able to run a script that runs ls > file then something runs through said file and parses it so each entry is a variable. Then uses the variables to as the input file for ebook-convert; and somehow have the variable as the output file but append a .epub to the end of the file name?


I'll expand on jasonwryan's answer, which explains the what but not the how or the why.

You can use the output of a command as arguments to another command. This is called command substitution.

some_text='Hello, world'
uppercased="$(echo "$some_text" | tr a-z A-Z)"
echo "Now, it's $uppercased"

However, $(ls …) is horrible for two reasons:

  • Do not parse the output of ls. It's not designed to be parsable.
  • "$(some-command)" gives you a string, which is the output of that command. If you have a command that returns a list of elements, you need to parse that string into individual elements. That's fiddly and unreliable unless you can find a simple separator. For file names, you can't easily find a separator, because file names on most unices can contain every character⁰.

Note that if you write $(some-command) rather than "$(some-command)", the output of the command is split into words and undergoes globbing. Until you understand the previous sentence, always put double quotes around command substitutions. When you do understand, always put double quotes unless you know why you can't put them.

Fortunately, generating the list of files that match a certain pattern is a feature that's built into the shell. It's called globbing.

echo *.pdf

To act on multiple files, you use a loop.

for x in *.pdf; do …; done

The remaining hurdle is to generate the name of the target file. The shell has some simple text manipulation commands, amongst which ${foo%SUFFIX} to get the value of a variable with a suffix removed. (There's also ${foo#PREFIX}.)

for x in *.pdf; do
  ebook-convert "$x" "${x%.pdf}.epub"

Note the double quotes around variable substitutions. The reason is the same as for command substitutions.

Except a null character, but most shells can't cope with these.

  • Thank you for fixing the suffix part of my script. I wasn't sure if it was necessary, ie., if ebook-convert managed that substitution; but your approach is both more thorough and safer. – jasonwryan Jun 10 '11 at 0:44
  • Thanks! Not only did you "show me the way" you also provided me a good insight into shell scripting. And have answered my "noob" question. Thanks again. – alburnett Jun 10 '11 at 2:12

This might be a simpler approach:

for file in *.pdf; do
    ebook-convert "$file" "${file%.pdf}.epub"
exit 0
  • I think it would almost work. Looks like I forgot to add the correct and full command syntax. ebook-convert file.pdf file.epub It uses the file extensions to figure out how to convert the files. Sorry for not being clear. – alburnett Jun 9 '11 at 21:52
  • I've updated the script: give that a go... – jasonwryan Jun 9 '11 at 22:39
  • @jasonwray Thank you too. I really appreciate the help. – alburnett Jun 10 '11 at 2:13

One-liner answer with the slight advantage that the conversion runs in parallel:

parallel ebook-convert {} {.}.epub ::: *.pdf

I came accustomed to not using for loops in the shell any more. They are all neatly replaceable by GNU Parallel.

Your Answer

