我在两台机器上维护大型文件系统树,经常需要将一个或几个文件从一台机器复制到另一台机器。
是否可以在两棵树的相同位置之间自动进行复制,并且不需要键入长的相同路径?
例如,我myroot/a/b/c
想将文件复制到remoteroot/a/b/c
而不指定a/b/c
.
像这样的东西:
myroot/a/b/c$: smartcopy myfile --destination-root remoteroot
答案1
该rsync
实用程序的-R
选项可以在某种程度上帮助您,但您需要通过所有的源路径和告知rsync
您希望将源路径的哪一部分视为“根”部分。您可以通过./
在源路径的所需位置插入 来提供该信息。
在您的示例中,“根”部分只是myroot/
,因此您的命令如下:
myroot/a/b/c$ rsync -R myroot/./a/b/c/myfile remoteroot
我假设您希望有一种简单的方法来在任何复杂的情况下指定当前目录路径之外的“根”部分。
一种稳健但仍然相对简单的方法是通过路径深度数。例如,bash
您可以执行以下操作:
/path/to/myroot/a/b/c$ rsync -R "$(idx=4; IFS=/; set -- $PWD ; printf '%s/' "${@:1:idx}" . "${@:idx+1}")"/myfile remoteroot
# ^--- 4th piece of path counted as number of slashes in $PWD
当然不是一句简洁的话,但命令替换可以在您的.bashrc
文件中成为一个函数,例如:
rootidx() {
local idx="$1"
# make Bash perform Word Splitting on / character
local IFS=/ # make IFS local here, so to not affect global IFS
# set function's arguments to each piece of current directory path,
# as split on $IFS
set -- $PWD
# print from first argument up to the one indicated by wanted index,
# then a ./, then from wanted index onwards
printf '%s/' "${@:1:idx}" . "${@:idx+1}"
}
用法如下:
/path/to/myroot/a/b/c$ rsync -R "$(rootidx 4)"/myfile remoteroot
或者,将“根”定位到指定的位置细绳对于完整路径,一种简单且兼容 POSIX 的方式可能如下所示:
/path/to/myroot/a/b/c$ rsync -R "${PWD%%/myroot/*}/myroot/./${PWD#*/myroot/}"/myfile remoteroot
它使用参数扩展将$PWD
注入到当前目录路径中该字符串首次出现的./
位置。myroot
然而,尽管参数扩展相当简单,但您需要小心指定一个匹配中间体当前目录路径的一部分,否则该特定扩展将无法按预期工作,而是会将整个当前目录与其自身以及中间的指定字符串连接起来。
该参数扩展也可以成为一个函数,这也有助于使其更加健壮,例如:
rootname() {
[[ "$PWD" == */"$1"/* ]] && \ # check presence of string as intermediate path
printf '%s' "${PWD%%/${1}/*}/${1}/./${PWD#*/${1}/}" || \ # apply the injection, or
printf '%s -not valid-' "$1" # try to produce an unlikely filename so to make `rsync` fail
}
用法如下:
/path/to/myroot/a/b/c$ rsync -R "$(rootname myroot)"/myfile remoteroot
最后,如果您有 GNU 工具以及能够理解**
路径名扩展的 shell,您也可以使用这些函数进行本地复制操作cp
。例如:
/path/to/myroot/a/b/c$ (shopt -s globstar; cd "$(rootidx 4)" && cp --parents **/myfile otherpath)
该shopt -s globstar
命令使bash
能**
扩展。
当然你也可以把它变成一个函数:
smartcp() {
# FIXME: some sanity checks over arguments here
# TODO: detect type of first argument, and use either rootidx or rootname accordingly
(shopt -s globstar; cd "$(rootidx "$1")" && cp --parents **/"${2}" "$3")
}
那么:
/path/to/myroot/a/b/c$ smartcp 4 myfile otherpath