3

I'll spare you the entire multi-line duplicity command, but what I want to know is if it's possible is to tidy up a series of arguments in a script like this:

--exclude "$2/${4:-__nosuchfiles__}" --exclude "$2/${5:-__nosuchfiles__}" ...

the key thing is that $4, $5,... are names which often contain spaces. The present formulation works, but at the expense of those ugly defaults to __nosuchfiles__ to exclude nothing if the set of exclusion criteria is smaller than the number of excludes passed. It's also a hack in that somebody conceivably might create a folder called __nosuchfiles__, and that there is a limit on the number of exclusions that it can handle.

What I'd like, is to define

EXCLUDES='--exclude "Path 1" --exclude "Path 2"'

with as many --exclude as I need, none to lots, and then substitute $EXCLUDES into a duplicity command line like:

duplicity $STUFF $EXCLUDES $SOURCE $DESTINATION

of course, it doesn't work when the excludes specify names containing spaces, and I can't work out any formulation for defining and using such a set of exclusions in the script itself. (Bonus points if any answer also handles EXCLUDES="" to mean no excludes at all. )

Yes, I know I could use duplicity's --exclude-filelist, but I'd like to know if there is a general solution that does not depend on the command conveniently being able to read a list of names from a file instead of from the command line. Note also the prefixing of $2/ to all exclusion paths.

migrated from serverfault.com Oct 9 '16 at 14:32

This question came from our site for system and network administrators.

1

You need to build the duplicity command line using an array rather than simple string concatenation. Then you can use the "${array[@]}" syntax to provide the contents of the array to duplicity as individual (possibly space-containing) arguments. This gets you out of the business of having to think about escaping spaces or other unusual characters.

To see how this works without actually calling duplicity, you could use a simple script that prints out its arguments on separate lines, such as this:

#!/bin/bash

printf "BEGIN\n"

for x in "$@"
do
  printf "ARG: $x\n"
done

printf "END\n"

I've saved that as args.sh.

Now, we need to build a command using an array, like so:

#!/bin/bash

if test -z "$1"
then
  printf "Need at least one argument"
  exit 1
fi

prefix=$1
shift

for arg in "$@"
do
    args+=("--exclude")
    args+=("$prefix/$arg")
done

./args.sh "${args[@]}"

In action, this works as follows:

$ ./801811.sh foo bar "baz quux"
BEGIN
ARG: --exclude
ARG: foo/bar
ARG: --exclude
ARG: foo/baz quux
END

You'll need to do more to set up your duplicity command line, since you also need to account for $STUFF and $DESTINATION, but these can be handled in the same way by adding elements to the args array with args+=("whatever"), either before or after you add the exclusions, as appropriate. Then at the end, just call duplicity "${args[@]}".

0

Spaces are a red herring here since the expansion is within quotes. If we call a program from the shell like this:

program "$foo_path/$bar_name"

it receives a single argument, no matter what foo_path or bar_name contain.

Now let's take a look at:

--exclude "$2/${4:-__nosuchfiles__}" 

Here, you want to erase the entire argument pair if 4 is blank or doesn't exist, yielding the solution:

${4:+--exclude} ${4:+"$2/$4"}

The :+ parameter substitution operator is what you need: "if this is blank or unset, substitute nothing, otherwise substitute an alternative". We apply this individually to the --exclude and its argument.

Tools which use a getopt implementation (or otherwise hack the support themselves) can take both --exclude=ARG as well as --exclude ARG. If duplicity works this way, we can take advantage of it:

${4:+"--exclude=$2/$4"}

Complete demo program test.sh:

#!/bin/bash

myargs()
{
  for x in "$@" ; do printf "<%s>\n" "$x"; done
}

myargs ${1:+--foo-opt} ${1:+"x $1 x"}

Runs:

$ ./test.sh
$ ./test.sh ""      # blank treated like missing?
$ ./test.sh abc
<--foo-opt>
<x abc x>
$ ./test.sh "ab c"
<--foo-opt>
<x ab c x>

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.