rpf 脚本允许您通过命名配置文件创建、保存和运行不同的 rsync 配置。
例如,通过输入 rpf -c backup 创建一个名为 backup 的新配置文件。假设用户名是 user。
rpf 创建以下目录
- /home/user/.rpf
- /home/user/.rpf/shared,您可以在其中放置多个配置文件共享的配置文件
- /home/user/.rpf/profiles,其中所有配置文件都保存为子目录
rpf 还创建了 /home/user/.rpf/profiles/backup,其中包含文件 conf 和 excluded。
conf 文件定义了 rsync 的配置
# rsync config template
#
# Write each rsync option on separate line. For option details see man rsync.
# Empty lines and lines starting with # are ignored. Dynamic references
# (e.g. using command substitution) are not supported.
#
# Config files shared between different profiles should be saved in
# /home/user/.rpf/shared
#
# Example configuration:
#
--verbose
--archive
--human-readable
# exclude all files that match pattern in:
--exclude-from=/home/user/.rpf/profiles/backup/exclude
--relative
# perform trial run, make no changes
--dry-run
# source, e.g.
/home/user
# destination, e.g.
/mnt/usb_drive/users_backup
现在您可以根据需要编辑、添加或删除 rsync 选项。
在 exclude 中,您可以定义要从传输中排除的文件和目录的路径或模式。要排除 Trash 和 Downloads,请添加以下行
- /home/user/.local/share/Trash
- /home/user/Downloads
或者仅传输 Documents 和 Projects 并排除所有其他内容
+ /home/user/Documents
+ /home/user/Projects
- **
有关更精细的模式配置,请参阅 man rsync 的 FILTER RULES 部分,或在 Google 上搜索教程。
准备就绪后,您可以通过键入 rpf backup 来启动 rsync 传输。
就是这样。
有关其他 rpf 选项,请参阅 rpf --help。
安全
请注意,rpf 对于 conf 文件中的代码注入并不安全。任何附加代码(例如,; ./run_evil_script)也将被执行。因此,通过实施适当的权限来保护您的 .rpf/ 配置文件目录免受恶意用户的侵害。此外,利用此行为可能会导致意外的副作用。
脚本
#!/usr/bin/env bash
#
# Simple rsync profiler
#
# Author: petrberanek.mail@gmail.com (Petr Beranek)
#
# For usage details type `rpf --help'
#
set -o errexit
set -o nounset
__name=$(basename "${0}")
__version="0.1"
config_dir="${HOME}/.rpf"
profiles_dir="${config_dir}/profiles"
shared_dir="${config_dir}/shared"
help="\
Usage: ${__name} [OPTION...] PROFILE_NAME
${__name} is simple rsync profiler that stores your different rsync
configurations in named profiles.
Options:
-c, --create-profile PROFILE_NAME create new profile (profile data
are stored in ${config_dir}/PROFILE_NAME).
Profile name can contain alphanumeric
characters only.
-s, --show-profile-config PROFILE_NAME show content of profile
configuration file (stored in
${config_dir}/PROFILE_NAME)
-l, --list-profiles list all available profiles
-h, --help show this help
Example:
Create new profile by typing
${__name} -c PROFILE_NAME
edit its config files stored by default in
${profiles_dir}/PROFILE_NAME
and then run it by typing
${__name} PROFILE_NAME
That's it.
${__name} comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions. See
the GNU General Public Licence for details.
Email bug reports or enhancement requests to petrberanek.mail@gmail.com.
"
create_profile() {
# Create dir with given profile name and with default content.
#
# Arguments: $1 -- profile name
#
# Creates files: conf, exclude
#
# If dir with the same name already exists, exits with error.
#
local profile_name="${1}"
local profile_dir="${profiles_dir}/${profile_name}"
# create default rpf dirs if missing
if [[ ! -d "${profiles_dir}" ]]; then
echo "Creating ${profiles_dir}"
mkdir --parents "${profiles_dir}"
fi
if [[ ! -d "${shared_dir}" ]]; then
echo "Creating ${shared_dir}"
mkdir --parents "${shared_dir}"
fi
# don't overwrite existing profile
if [[ -d "${profile_dir}" ]]; then
echo "${__name}: error: profile already exists."
exit 1
fi
echo "Creating ${profile_dir}"
mkdir "${profile_dir}"
# create `conf' template
local conf="${profile_dir}/conf"
echo "Creating ${conf}"
cat << EOF > "${conf}"
# rsync config template
#
# Write each rsync option on separate line. For details see man rsync.
# Empty lines and lines starting with # are ignored. Dynamic references
# (e.g. using command substitution) are not supported.
#
# Config files shared between different profiles should be saved in
# ${shared_dir}
#
# Example configuration:
#
--verbose
--archive
--human-readable
# file with patterns of files and directories in source excluded
# from transfer
--exclude-from="${profiles_dir}/${profile_name}/exclude"
--relative
# perform trial run, make no changes
--dry-run
# source, e.g.
${HOME}
# destination, e.g.
/mnt/usb_drive/my_backup
EOF
# create `exclude' template
local exclude="${profile_dir}/exclude"
echo "Creating ${exclude}"
cat << EOF > "${exclude}"
# \`exclude' template
#
# Lines starting with # or ; are ignored. For details see man rsync,
# section FILTER RULES.
#
EOF
# all done
echo "OK"
echo "Edit profile config files in ${profile_dir} to fit your needs."
}
list_profiles() {
# Show all available rpf profiles.
#
# Assumes that all dirs in $profiles_dir are profiles.
#
for item in "${profiles_dir}"/*; do
if [[ -d "${item}" ]]; then
basename "${item}"
fi
done
}
show_help() { echo "${help}"; }
show_profile_config() {
# Show configuration file for given profile.
#
# Arguments: $1 -- profile name
#
local profile_name="${1}"
less "${profiles_dir}/${profile_name}/conf"
}
check_profile_name() {
# Check that name is not empty and contains alphanumeric chars only.
#
# Arguments: $1 -- profile name
#
# If test fails, exits with error.
#
if [[ -z "${1}" ]]; then
echo "${__name}: error: empty profile name."
exit 1
elif [[ "${1}" =~ [^a-zA-Z0-9] ]]; then
echo "${__name}: error: non-alphanumeric characters in profile name."
exit 1
fi
}
check_profile_exists() {
# Check that $profile_name exists and is a directory.
#
# Arguments: $1 -- profile name
#
# If test fails, exits with error.
#
local profile_name="${1}"
if [[ ! -d "${profiles_dir}/${profile_name}" ]]; then
echo "${__name}: error: profile ${profile_name} does not exist."
exit 1
fi
}
check_num_args() {
# Check that value of $1 = number of arguments (excluding $1)
#
# Arguments: $1 -- limit (positive int)
#
# If test fails, exits with error.
#
local num_args=$(( ${#} - 1 )) # do not count $1 in total num of args
if [[ "${1}" -ne "${num_args}" ]]; then
echo "${__name}: error: expected num args: ${1}, received: $num_args"
exit 1
fi
}
run_rsync() {
# Run rsync with configuration coresponding to given profile name.
#
# Arguments: $1 -- profile name
#
local profile_name="${1}"
local visual_div="=============================="
local parsed_args
parsed_args=$(grep --invert-match '^#' "${profiles_dir}/${profile_name}/conf" \
| tr '\n' ' ')
# Print debug info
echo "${visual_div}"
echo "${__name} version: ${__version}"
echo "args: ${parsed_args}"
echo "${visual_div}"
# Expand $parsed_args - each item from conf file becomes rsync argument
# shellcheck disable=SC2086
rsync ${parsed_args}
}
if [[ "${#}" == 0 ]]; then
show_help
exit 1
fi
while [[ "${#}" -gt 0 ]]; do
case "${1}" in
-c | --create-profile)
check_num_args 2 "${@}"
shift
check_profile_name "${1:-}" # If $1 is not declared, set it empty.
create_profile "${1}"
exit 0;;
-s | --show-profile-config)
check_num_args 2 "${@}"
shift
check_profile_name "${1:-}"
check_profile_exists "${1}"
show_profile_config "${1}"
exit 0;;
-l | --list-profiles)
check_num_args 1 "${@}"
list_profiles
exit 0;;
-h | --help)
check_num_args 1 "${@}"
show_help
exit 0;;
-*)
echo "${__name}: error: unknown option \`${1}'"
exit 1;;
*)
check_num_args 1 "${@}"
check_profile_name "${1:-}"
check_profile_exists "${1}"
run_rsync "${1}"
exit 0;;
esac
shift
done
最初发布于 Petr Beranek 的 GitHub 存储库,并根据 GPLv3 获得许可。
评论已关闭。