Missing trailing-' in remote-shell command - bash

In my bash shell scripting,
rsync -a --delete -e "ssh -i /keypath" /source_dir/ username#remotehost:/dest_dir/
whatever combinations [eval, double to single quotes] of above gives below err,
Missing trailing-' in remote-shell command.
rsync error: syntax or usage error (code 1) at ../main.c(335) [sender=2.6.8]
But the same command in directly working in command prompt.
Could anyone help in what im missing here
Thanks

Your problem is with embedded quotes in the bash script.
Bash FAQ #50
See the following solution (and upvote it if you find it useful, please):
https://superuser.com/questions/354361/rsync-complaining-about-missing-trailing-in-a-bash-script

Not sure what your problem is but for me most of weird rsync errors are caused by the .bashrc or .bash_profile (or other shell rc files) outputting something when logging in. For now I usually write the following statement as the first line in my .bashrc and .bash_profile:
# return if it's not an interactive shell
[[ $- == *i* ]] || return 0

An easy solution that worked for me is using single quotes instead of double quotes, i.e.
rsync -a --delete -e 'ssh -i /keypath' /source_dir/ username#remotehost:/dest_dir/

Related

command substitution not working in alias?

I wanted to make an alias for launching a vim session with all the c/header/makefiles, etc loaded into the buffer.
shopt -s extglob
alias vimc="files=$(ls -A *.?(c|h|mk|[1-9]) .gitconfig [mM]akefile 2>/dev/null); [[ -z $files ]] || vim $files"
When I run the command enclosed within the quotations from the shell, it works but when run as the alias itself, it does not. Running vimc, causes vim to launch only in the first matched file(which happens to be the Makefile) and the other files(names) are executed as commands for some reason(of course unsuccessfully). I tried fiddling around and it seems that the command substitution introduces the problem. Because running only the ls produces expected output.
I cannot use xargs with vim because it breaks the terminal display.
Can anyone explain what might be causing this ?
Here is some output:
$ ls
Makefile readme main.1 main.c header.h config.mk
$ vimc
main.1: command not found
main.c: command not found
.gitignore: command not found
header.h: command not found
config.mk: command not found
On an related note, would it be possible to do what I intend to do above in a "single line", i.e without storing it into a variable files and checking to see if it is empty, using only the output stream from ls?

csh doesn't recognize command with command line options beginning with --

I have an rsync command in my csh script like this:
#! /bin/csh -f
set source_dir = "blahDir/blahBlahDir"
set dest_dir = "foo/anotherFoo"
rsync -av --exclude=*.csv ${source_dir} ${dest_dir}
When I run this I get the following error:
rsync: No match.
If I remove the --exclude option it works. I wrote the equivalent script in bash and that works as expected
#/bin/bash -f
source_dir="blahDir/blahBlahDir"
dest_dir="foo/anotherFoo"
rsync -av --exclude=*.csv ${source_dir} ${dest_dir}
The problem is that this has to be done in csh only. Any ideas on how I can get his to work?
It's because csh is trying to expand --exclude=*.csv into a filename, and complaining because it cannot find a file matching that pattern.
You can get around this by enclosing the option in quotes:
rsynv -rv '--exclude=*.csv' ...
or escaping the asterisk:
rsynv -rv --exclude=\*.csv ...
This is a consequence of the way csh and bash differ in their default treatment of arguments with wildcards that don't match a file. csh will complain while bash will simply leave it alone.
You may think bash has chosen the better way but that's not necessarily so, as shown in the following transcript where you have a file matching the argument:
pax> touch -- '--file=xyzzy.csv' ; ls -- *.csv
--file=xyzzy.csv
pax> echo --file=*.csv
--file=xyzzy.csv
You can see there that the bash shell expands the argument rather than giving it to the program as is. Both sides have their pros and cons.

execute command from shell script fails

When I run my script it fails:
# sh -x ./rsync.sh
...
+ rsync --safe-links --password-file=/etc/rsync.secret -rtvun --include='*.log*.gz' --filter='-! */' --prune-empty-dirs /some/path foo#hostname::foo
Unknown filter rule: `'-!'
rsync error: syntax or usage error (code 1) at exclude.c(904) [client=3.1.1]
but when I copy&paste the rsync --safe-links .... line and execute it it works fine.
rsync.sh:
#!/bin/bash
rsync_opts="--safe-links --password-file=/etc/rsync.secret -rtvu"
while read path hostname volume extra_opts; do
rsync ${rsync_opts} ${extra_opts} ${path} ${volume}#${hostname}::${volume}
done < /etc/rsync.paths
rsync.paths:
/some/path/ hostname foo --include='*.log*.gz' --filter='-! */' --prune-empty-dirs
Does anyone knows why it fails from the script and works fine when I run the command manually ? How can I fix it ?
UPDATE:
It works fine when I use eval "rsync ..." but still I don't know why it doesn't work withou it :/
Your problem is that your script reads the text --filter='-! */' --prune-empty-dirs into the variable $extra_opts, which is passed literally to the command, including the apostrophes. So it is split into the following arguments: --filter='-!, */' and --prune-empty-dirs. When you eval it though, then the whole line is passed to the shell interpreter which interprets the apostrophes the way you want, that's why it works.
Also, since your script uses bash, you should debug it with bash -x instead of sh -x.

How to properly pass run-time determined command line switches that include *'s in bash?

I am writing a simple script that rsync's a remote site to my local computer, and dynamically generates --exclude=dir flags depending on what option is specified on the command line.
#!/bin/bash -x
source="someone#somewhere.org:~/public_html/live/"
destination="wordpress/"
exclude_flags='--exclude=cache/* '
if [ "$1" == "skeleton" ] ; then
exclude_flags+='--exclude=image-files/* '
fi
rsync --archive --compress --delete $exclude_flags -e ssh $source $destination
I'm running into trouble when I try to interpolate the $exclude_flags variable on the last line. Since the variable has spaces in it, bash is automatically inserting single quotes before and after the interpolation. Here is the command which bash tried to execute (the relevant output of /bin/bash +x):
+ /usr/bin/rsync --archive --compress --delete '--exclude=cache/*' '--exclude=image-files/*' -e /usr/bin/ssh someone#somewhere.org:~/public_html/live/ wordpress/
As you can see, bash has inserted a bunch of single quotes around the individual tokens of $exclude_flags, which is causing rsync to choke.
I have tried:
What I have listed above.
Putting it in double quotes ... "$exclude_flags" .... This almost fixes the problem, but not quite. The single quotes only appear around the full content of $exclude_flags, rather than around each token.
Making $exclude_flags an array, and then interpolating it using ${exclude_flags[#]}. This gives the same output as #2.
Wrapping the whole rsync line in back-tick quotes. This gives the same output as #1.
Any ideas? This seems like a really simple and common problem in bash, so I'm sure that I'm doing something wrong, but google didn't help at all.
Thank you.
The proper way to store multiple command-line options in a variable in bash is to use an array:
source="someone#somewhere.org:~/public_html/live/"
destination="wordpress/"
options=( '--exclude=cache/*' )
if [[ "$1" == "skeleton" ]] ; then
options+=( '--exclude=image-files/*' )
fi
rsync --archive --compress --delete "${exclude_flags[#]}" -e ssh "$source" "$destination"

bash: rsync with options as variable

I am writing bash script, which in some part will rsync files over ssh. Unfortunately I am facing problem with keeping rsync options as variable. Please take a look below:
# variables
directory="/etc"
backupDirectory="/backup"
incrementalBackup="/incremental"
options="-a -e 'ssh -p 10022' -b --backup-dir=$incrementalBackup --delete"
# rsync
rsync $options user#server:$directory $backupDirectory
Unfortunately above script fails with rsync error:
Unexpected remote arg: user#server:/etc
rsync error: syntax or usage error (code 1) at main.c(1201) [sender=3.0.6]
What I saw during script debugging is the fact, that ssh options ('ssh -p 10022') are treated as rsync options.
The question is how to pass correctly those additional ssh settings into rsync?
Thanks in advance for a tip.
Use an array; it's why they were added to bash:
# variables
directory="/etc"
backupDirectory="/backup"
incrementalBackup="/incremental"
options=(-a -e 'ssh -p 10022' -b --backup-dir="$incrementalBackup" --delete)
# rsync
rsync "${options[#]}" user#server:"$directory" "$backupDirectory"
eval is not a safe option to use; it isn't limited to just evaluating the quotations you intend it to, but will evaluate any code. It might work for your current situation, but changes to the value of options might bring unforeseen consequences, and it's generally a bad idea to get into the habit of using eval when it isn't necessary.
Use eval. Try:
eval rsync $options user#server:$directory $backupDirectory

Resources