排除 SCP 文件模式的字符

排除 SCP 文件模式的字符

有没有办法构建一个表达式,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或环境变量)。SHELLOPTSBASH_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]),匹配一个或多个小数位。因此,上面的模式将匹配,例如。extglobbashfile-name-version-1.2.3.jarfile-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.jarand file-name-version-1.2.3.4.5.jar(是从到 的<x-y>任何十进制整数,是任何十进制整数,就像正则表达式运算符(0个或多个前面的原子))xy<->#*

答案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/

相关内容