What you want to do is reasonable, but using rsync
to do it on its own is not. So the answer is no.
The reason is simple: rsync
keeps no history of what was in each directory and has no way of knowing what needs to be deleted and what not. Not without additional support.
You should ask yourself why you like to do this with rsync
and make that more clear. There are other programs that use librsync1.so
that are more intelligent.
With the relaxed constraints that you don't need
rsync
per se, you can take a look at
rdiff-backup:
mkdir a
touch a/xx
touch a/yy
rdiff-backup a b
ls b
This shows xx
and yy
are in b
.
touch b/zz
rm a/xx
rdiff-backup a b
This shows xx
and zz
are in b
. rdiff-backup
also keeps a directory rdiff-backup-data
in b
so you can rollback any changes, you should purge this on a regular basis using the rdiff-backup
commands. (The example is with local files to show extra data in the target does not get deleted, but rdiff-backup works over a network as well).
Another alternative is to setup some distributed revision control system (mercurial, bazaar, git). With mercurial e.g. you can have a script (I use a Makefile for that), that pushes all the changes to the server and then does an update of the checked out files there, ignore any additional files that are on the remote server (but have not been put under revision control).
On the server you would do:
hg init
hg add file_list_excluding_that_should_not_should_be_deleted_if_not_on_client
hg commit -m "initial setup"
On the client:
hg clone ssh://username@server/dir_to_repository
Now if you remove a file on the client and do:
hg commit -m "removed file"
ssh username@server "cd dir_to_repository; hg update --clean"
Your removed file is removed on the server, but any other data (not added to the repository) does not get deleted.
Believe it might be better to use compgen
instead of find
in this case.
You probably already have a completion script with system. Try e.g.
locate bash_completion
On Debian variants this is probably:
/usr/share/bash-completion/bash_completion
where you find e.g. _filedir
. So the simplest way then would be something in the direction of:
*)
pushd "/some/path" >/dev/null
_filedir
popd >/dev/null
If that is not an option this could be a starter:
_comp_by_path()
{
local opt cur dir
local IFS=$'\n' x tmp
local -a tokens
opt="$1"
cur="$2"
dir="$3"
# Enter target directory
pushd "$dir" >/dev/null
# Get directories, filtered against current
[[ "$opt" != "-f" ]] && \
x=$( compgen -d -- "$cur" ) &&
while read -r tmp; do
tokens+=( "$tmp" )
done <<< "$x"
# Get files, filtered against current
[[ "$opt" != "-d" ]] && \
x=$( compgen -f -- "$cur" ) &&
while read -r tmp; do
tokens+=( "$tmp" )
done <<< "$x"
# If anything found
if [[ ${#tokens[@]} -ne 0 ]]; then
# Make sure escaping is OK
compopt -o filenames 2>/dev/null
COMPREPLY+=( "${tokens[@]}" )
fi
# Go back
popd >/dev/null
}
_GetOptMyCommand()
{
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-h -l --help --list --" -- "$cur" ) );;
*)
_comp_by_path "any" "$cur" "/some/path"
esac
}
complete -F _GetOptMyCommand my_command
A variant using find
could be something in direction of this:
_zaso()
{
local dir="$1"
pushd "$dir" >/dev/null
find * -maxdepth 0 2>/dev/null
popd >/dev/null
}
_comp_with_find()
{
local cur dir
local IFS=$'\n'
cur="$1"
dir="$2"
compopt -o filenames 2>/dev/null
COMPREPLY=( $( compgen -W "$(_zaso "$dir")" -- "$cur" ) );
}
Also note that printf
in Bash has a %q
option. So to generate quoted strings this is an option to play with:
find * -maxdepth 0 2>/dev/null && \
while read -r tmp; do
printf "%q\n" "$tmp"
done <<< "$x"
Also not that file names can have newline characters in which a lot of this will break. Have not found a way to use \0
with compgen
.
Best Answer
Edit: Sliced it up some more.
You might find this useful, from Debian Administration: An introduction to bash completion.
Complete script:
/some/location/my_ssh_autocomplete_script
(only meant as a short starter):Download script
/some/location/my_ssh_download_script
:In
.bash_aliases
:In
.bash_completion
: