有没有办法构建一个表达式,scp
我可以用它来复制文件
file-name-version-1.2.1.jar
无需复制文件
file-name-version-1.2.1-sources.jar
file-name-version-1.2.1-javadoc.jar
但不知道确切的版本号?file-name-version-*.*.*.jar
由于明显的原因将无法工作。我正在寻找类似的语法
file-name-version-\d.\d.\d.jar
或类似的东西。
答案1
要记住的一件事是文件名被传递未修改的到远程用户的登录 shell。
如果你这样做:
scp user@remote-machine:'something' ...
在远程计算机上,sshd
将运行以下登录 shell user
:
that-shell -c 'scp -f something'
正是这个外壳将扩展其中的模式。
这意味着模式的扩展方式将取决于远程用户正在使用的登录 shell。如果该 shell 是bash
,则还取决于用户在 中拥有的内容~/.bashrc
,或者 中的内容/etc/bash.bashrc
(例如,bash-completion
安装时从那里调用会打开 ksh 样式的扩展通配符),因为出于某种原因,bash
在调用时会读取这些内容ssh
(即使这不是交互式 shell)。
这也意味着,如果something
是这样'blah; rm -rf "$HOME"'
,那将带来灾难性的后果。
这是 的错误设计之一ssh
。
如果你想强制特定的 shell 将其扩展something
为文件列表,你可以使用这个技巧:
LC_SCPFILES=something scp -o SendEnv=LC_SCPFILES "localhost:</dev/null
bash -O extglob -c 'exec scp -f -- \$LC_SCPFILES';exit" /dest/dir
仅当远程用户的登录 shell 是普通的 Unix shell(至少是 Bourne、csh、rc 或 Fish 系列)并且bash
安装在那里并sshd
允许传递名称以LC_
.
诀窍在于,大多数sshd
部署允许传递名称开头为的环境变量LC_
(需要这样,以便西班牙语用户可以从西班牙语的远程命令中获取错误消息(而不是远程系统上的默认语言或远程用户的语言)实例)。
所以在这里,我们传入something
一个LC_SCPFILES
环境变量。
在远程计算机上,sshd
将运行:
that-shell -c 'scp -f </dev/null
bash -O extglob -c '\''exec scp -f -- $LC_SCPFILES'\'';exit'
因为它的标准输入是从 重定向的/dev/null
,所以第一个scp
将立即退出。然后我们使用选项启动我们选择的 shell ( bash
),extglob
并使用exec scp -f -- $LC_SCPFILES
命令行进行解释。
因为bash
将在 的子进程中运行that-shell
(我们通过;exit
随后添加来强制执行),所以bash
不会提供 的源~/.bashrc
,因此我们可以预期那里的默认行为bash
(除非以某种方式that-shell
在启动时设置BASHOPTS
或环境变量)。SHELLOPTS
BASH_ENV
因为$LC_SCPFILES
没有引用,所以它将受到分词和文件名生成(通配符)的影响,但不会受到其他 shell 扩展的影响,并且它不会rm -rf "$HOME"
仅仅因为something
包含它而执行类似的操作。
现在我们确保该模式bash -O extglob
在远程计算机上通过 进行了扩展,我们可以使用它来匹配使用扩展 glob 运算符的模式(此处使用辅助函数):
safer_scp() (
file=$1; shift
export LC_SCPFILES="${file#*:}"
exec scp -o SendEnv=LC_SCPFILES "${file%%:*}:</dev/null
bash -O extglob -c 'exec scp -f -- \$LC_SCPFILES';exit" "$@"
)
safer_scp user@host:'file-name-version-+([0-9]).+([0-9]).+([0-9]).jar' .
带有on 的+([0-9])
,匹配一个或多个小数位。因此,上面的模式将匹配,例如。extglob
bash
file-name-version-1.2.3.jar
file-name-version-12.123.1234.jar
(请注意,safer_scp
上面假设第一个参数是host:file-patterns
,诸如safer_scp -r ...
或 之类的东西safer_scp host1:f1 host2:f2 ...
将不起作用。如果您想使用-r
,最简单的方法是定义一个safer_scp_r
函数,其中scp -f
上面的内容替换为scp -r -f
)。
IFS=;
请注意,您可以通过在上面添加一个来禁用分词exec scp...
。
另请注意,我们在这里使用它是bash
因为它安装在所有 GNU 系统上,但如果您知道zsh
它安装在远程系统上,您可以使用
zsh -o extendedglob -o globsust
相反,要受益于通配符的全部功能zsh
(递归、限定符...)
(-o shwordsplit
如果您还想要分词,请添加)。
例如:
safer_scp() (
file=$1; shift
export LC_SCPFILES="${file#*:}"
exec scp -o SendEnv=LC_SCPFILES "${file%%:*}:</dev/null
zsh -o extendedglob -o globsubst -c 'exec scp -f -- \$LC_SCPFILES';exit" "$@"
)
safer_scp user@host:'file-name-version-<->(.<->)#.jar' .
将复制file-name-version-1.jar
and file-name-version-1.2.3.4.5.jar
(是从到 的<x-y>
任何十进制整数,是任何十进制整数,就像正则表达式运算符(0个或多个前面的原子))x
y
<->
#
*
答案2
在这种特殊情况下
scp 'user@host:file-name-version-*[0-9].jar' .
对你来说可能就足够了。
答案3
我想你想要
file-name-version-[0-9].[0-9].[0-9].jar
答案4
为了灵活性,使用同步而不是SCP。
rsync --include='file-name-*.jar' \
--exclude='*-sources.jar' --exclude='*-javadoc.jar' \
example.com:/some/directory/ local/directory/