I am using fish shell and I have a helper function to generate a rsync command with my parameters all set. The final rsync command should be like this (the command is in one line, I made it multiline because it is easier to read):

rsync -razs -e="ssh -p2222" --update --progress \ 
    --exclude=".*" --exclude="__pycache__" 
    --delete --dry-run \
    $source_dir $dest_dir

From the terminal, it works ok but when I try to use my helper function, the "exclude" parameters seem to have no effect. The generated command looks exactly the same except that the ssh command is not enclosed in quotes. However, this does not seem to be a problem since I can connect to the server. As I said, the only problem is that the excludes are ignored.

The generated command looks like this:

rsync -razs -e=ssh -p2222 --update --progress \
    --exclude=".*" --exclude="__pycache__" \
    --delete --dry-run \
    /home/some_folder user@host:/home/

Any idea?

The function looks like this:

function ssync -d "rsync through ssh tunnel" 

    set origin $argv[1]
    set destination $argv[2]
    set exclude ".*" "__pycache__"

    set ssh_config "ssh -p2222"
    set params -razs -e="$ssh_config" --update --progress --exclude=\"$exclude\"

    if test (count $argv) -gt 2
        set option $argv[3]
        set params $params --delete
        if [ $option = "--delete" ]
            set_color red; echo "Warning: Unsafe run!";
            read -l -P "Confirm? [y/N] " yesno;
            switch $yesno
                case '' N n
                    echo "Sync canceled by user"
                    return 0
                case Y y
                    echo "Proceeding..."
            set params $params --dry-run

    echo "rsync $params $origin $destination"
    rsync $params $origin $destination;

[EDIT]: Thanks to Glenn's answer, I understand that the use of quote literals in the function is what causes the problem. However, it has the very convenient effect of separating an argument with multiple values separated by spaces like arg1 arg2 into something like --exclude="arg1" --exclude="arg2". Any way to have the advantages without the inconvenients?


You're adding literal quote characters

... --exclude=\"$exclude\"

That will get rsync to look for files that literally have quotes in the filename.

You just want to use quotes to surround the word

... "--exclude=$exclude"

Keep in mind that the purpose of quotes is to instruct the shell to tokenize the command the way you want it to. The actual quote characters are removed before the shell actually executes the command.

OK, you have a list of items in $exclude. Use braces instead: demo:

$ set exclude ".*" file1 file2
$ set params --some stuff --other stuff --exclude={$exclude}
$ echo $params
--some stuff --other stuff --exclude=.* --exclude=file1 --exclude=file2

Note that if $exclude is empty, the params will contain no --exclude options:

$ set -e exclude
$ set params --some stuff --other stuff --exclude={$exclude}
$ echo $params
--some stuff --other stuff
  • Nice, thank you! It works perfectly – qmeeus Mar 30 '19 at 14:48

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.