Bash escaping spaces in filename, in variable

2019-05-07 06:17发布

问题:

I'm quite new to Bash so this might be something trivial, but I'm just not getting it. I'm trying to escape the spaces inside filenames. Have a look. Note that this is a 'working example' - I get that interleaving files with blank pages might be accomplished easier, but I'm here about the space.

#! /bin/sh

first=true
i=combined.pdf
o=combined2.pdf
for f in test/*.pdf
do
    if $first; then
        first=false
        ifile=\"$f\"
    else
        ifile=$i\ \"$f\"
    fi
    pdftk $ifile blank.pdf cat output $o
    t=$i
    i=$o
    o=$t
    break
done

Say I have a file called my file.pdf (with a space). I want the ifile variable to contain the string combined.pdf "my file.pdf", such that pdftk is able to use it as two file arguments - the first one being combined.pdf, and the second being my file.pdf.

I've tried various ways of escaping (with or without first escaping the quotes themselves, etc.), but it keeps splitting my and file.pdf when executing pdftk.

EDIT: To clarify: I'm trying to pass multiple file names (as multiple arguments) in one variable to the pdftk command. I would like it to recognise the difference between two file names, but not tear one file name apart at the spaces.

回答1:

Putting multiple arguments into a single variable doesn't make sense. Instead, put them into an array:

args=(combined.pdf "my file.pdf");

Notice that "my file.pdf" is quoted to preserve whitespace.

You can use the array like this:

pdftk "${args[@]}" ...

This will pass two separate arguments to pdftk. The quotes in "${args[@]}" are required because they tell the shell to treat each array element as a separate "word" (i.e. do not split array elements, even if they contain whitespace).

As a side note, if you use bashisms like arrays, change your shebang to

#!/bin/bash


回答2:

Try:

find test/*.pdf | xargs -I % pdftk % cat output all.pdf

As I said in my comments on other answers xargs is the most efficient way to do this.

EDIT: I did not see you needed a blank page but I suppose you could pipe the find above to some command to put the blank page between (similar to a list->string join). I prefer this way as its more FP like.