1

I got my config files in /etc folder all messed up because I did not take care of shell expansion while doing rsync:

rsync --progress --delete -avhHe ssh /etc/logrotate.d/{httpd,mariadb,php-fpm,ppp,wpa_supplicant,yum} [email protected]:/etc

I think the problem is that the '.' following logrotate got expanded and subsequently some folders such as /etc/httpd got deleted.

I want to learn from this mistake by doing it correctly. How do I prevent shell expansion when rsync multiple files with ssh?

1 Answer 1

3

That command boils down to this:

rsync --delete --recursive /etc/logrotate.d/{httpd,mariadb,php-fpm,ppp,wpa_supplicant,yum} [email protected]:/etc

The . in there has its literal meaning - it's part of the name - so that isn't the problem. The part in {} is subject to brace expansion: each comma-separated part gets expanded and concatenated to the part of that argument that came before (/etc/logrotate.d/). (It'd also get anything after, if there were anything: a{BC}d expands to aBd aCd).

So this command is equivalent to

rsync --delete --recursive /etc/logrotate.d/httpd /etc/logrotate.d/mariadb /etc/logrotate.d/php-fpm /etc/logrotate.d/ppp /etc/logrotate.d/wpa_supplicant /etc/logrotate.d/yum [email protected]:/etc

or to pick just one directory out so it's short:

rsync --delete --recursive /etc/logrotate.d/httpd [email protected]:/etc

rsync interprets the "from" location as a single entity, and if it doesn't end with a / it makes a new file or directory with just the last part of that name inside the given destination path: here, that's httpd. So this makes a /etc/httpd on the destination and copies the contents of /etc/logrotate.d/httpd into it.

With --delete, it will then delete everything that wasn't in /etc/logrotate.d/httpd on the source. The problem— since /etc/logrotate.d/httpd probably doesn't exist at all, copying it and deleting any files that weren't present on the source means deleting everything in all of those directories. If it did exist, its contents won't be the same as /etc/httpd, so (almost) everything will be deleted.

So the problem is just that you have the logrotate.d part there at all, when you really meant to copy the same directories under /etc. What you probably meant was just:

rsync --progress --delete -avhHe ssh /etc/{httpd,mariadb,php-fpm,ppp,wpa_supplicant,yum} [email protected]:/etc

That copies /etc/httpd and its contents to /etc/httpd on the destination, and so on. If you meant to copy things inside logrotate.d, put that in the path on both sides.

One thing you may find useful is the -n or --dry-run option to rsync:

-n, --dry-run perform a trial run with no changes made

That will show you a preview of what would happen, but not actually make any changes on either end. You can use that to check it's what you want before running the real thing.


You asked how to prevent shell expansion in the arguments you gave to rsync. As above, I don't think that's actually what you want given the problem you had, but if you do ever need to: brace expansions don't take place inside quotes, so "a{BC}d" stays as a{BC}d literally.

1
  • 1
    Thanks for your very helpful answer, I think my mistake is that I assume the origin path will be used at the destination and tried combining files and folders from different directory levels. When it didn't work out, I tried using a single level, but forgot to change the destination to /etc/logrotate.d. In my panic, I mistakenly thought this to be a shell expansion issue. Thanks for pointing out the dry-run which I should rightly do first. :) Jun 23, 2014 at 2:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .