views:

915

answers:

3

Hi all

I have a simple test bash script which looks like that:

#!/bin/bash

cmd="rsync -rv --exclude '*~' ./dir ./new"
$cmd # execute command

When I run the script it will copy also the files ending with a ~ even though I meant to exclude them. When I run the very same rsync command directly from the command line, it works! Does someone know why and how to make bash script work?

Btw, I know that I can also work with --exclude-from but I want to know how this works anyway.

Thanks for your help.

Chris

A: 

You can use a simple --eclude '~' as (accoding to the man page):

  • if the pattern starts with a / then it is anchored to a particular spot in the hierarchy of files, otherwise it is matched against the end of the pathname. This is similar to a leading ^ in regular expressions. Thus "/foo" would match a name of "foo" at either the "root of the transfer" (for a global rule) or in the merge-file's directory (for a per-directory rule). An unqualified "foo" would match a name of "foo" anywhere in the tree because the algorithm is applied recursively from the top down; it behaves as if each path component gets a turn at being the end of the filename. Even the unanchored "sub/foo" would match at any point in the hierarchy where a "foo" was found within a directory named "sub". See the section on ANCHORING INCLUDE/EXCLUDE PATTERNS for a full discussion of how to specify a pattern that matches at the root of the transfer.
  • if the pattern ends with a / then it will only match a directory, not a regular file, symlink, or device.
  • rsync chooses between doing a simple string match and wildcard matching by checking if the pattern contains one of these three wildcard characters: '*', '?', and '[' .
  • a '*' matches any path component, but it stops at slashes.
  • use '**' to match anything, including slashes.
Zsolt Botykai
+1  A: 

Try eval:

#!/bin/bash

cmd="rsync -rv --exclude '*~' ./dir ./new"
eval $cmd # execute command
Dennis Williamson
+1  A: 

The problem isn't that you're running it in a script, it's that you put the command in a variable and then run the expanded variable. And since variable expansion happens after quote removal has already been done, the single quotes around your exclude pattern never get removed... and so rsync winds up excluding files with names starting with ' and ending with ~'. To fix this, just remove the quotes around the pattern (the whole thing is already in double-quotes, so they aren't needed):

#!/bin/bash

cmd="rsync -rv --exclude *~ ./dir ./new"
$cmd # execute command

...speaking of which, why are you putting the command in a variable before running it? In general, this is a good way make code more confusing than it needs to be, and trigger parsing oddities (some even weirder than this). So how about:

#!/bin/bash

rsync -rv --exclude '*~' ./dir ./new
Gordon Davisson
I put it in a variable to debug. I started without single quotes and that was even worse (actually, that's why I put them there) due to expansion. E.g. "rsync -r --exclude *~ ./dir ./new" expanded to "rsync -r --exclude foo~ bar~ foobar.sh~ ./dir ./new"
Chris
Did you try it with the single quotes, but without putting it in a variable (as in my second recommendation)? It should work fine (and seemed to when I tested it).
Gordon Davisson