排除 SCP 文件模式的字符

Vin*_*nce 3 scp wildcards

有没有办法建立一个表达式scp,我可以用它来复制文件

file-name-version-1.2.1.jar
Run Code Online (Sandbox Code Playgroud)

不复制文件

file-name-version-1.2.1-sources.jar
file-name-version-1.2.1-javadoc.jar
Run Code Online (Sandbox Code Playgroud)

虽然不知道确切的版本号?file-name-version-*.*.*.jar由于明显的原因将不起作用。我正在寻找类似的语法

file-name-version-\d.\d.\d.jar
Run Code Online (Sandbox Code Playgroud)

或类似的东西。

Sté*_*las 5

要记住的一件事是文件名未经修改地传递给远程用户的登录 shell。

如果你这样做:

 scp user@remote-machine:'something' ...
Run Code Online (Sandbox Code Playgroud)

在远程机器上,sshd将运行useras的登录 shell :

 that-shell -c 'scp -f something'
Run Code Online (Sandbox Code Playgroud)

正是那个外壳将扩展那里的模式。

这意味着模式的扩展方式将取决于远程用户使用的登录 shell。当那个 shell 是 时bash,这也将取决于用户在他的 中~/.bashrc有什么,或者有什么/etc/bash.bashrc(例如bash-completion,当安装时从那里调用,打开 ksh 风格的扩展通配符),因为出于某种原因,bash在调用时读取那些ssh(即使那不是交互式外壳)。

这也意味着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
Run Code Online (Sandbox Code Playgroud)

只有当远程用户的登录 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'
Run Code Online (Sandbox Code Playgroud)

因为它的标准输入是从 重定向的/dev/null,第一个scp将立即退出。然后我们启动我们选择的 shell ( bash),使用extglob选项,并使用exec scp -f -- $LC_SCPFILES命令行来解释。

由于bash将在一个子进程中运行that-shell(这是我们加入了强制执行;exit后),bash将不获取的~/.bashrc,所以我们可以预期的默认行为bash存在(除非以某种方式that-shell,在启动时,设置BASHOPTSSHELLOPTSBASH_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' .
Run Code Online (Sandbox Code Playgroud)

+([0-9]),有extglobbash比赛的一个或多个十进制数字。所以上面的模式将匹配file-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...上述之前添加 a 来禁用分词。

还要注意,我们在bash这里使用的基础是它安装在所有 GNU 系统上,但是如果您知道zsh安装在远程系统上,则可以使用

zsh -o extendedglob -o globsust
Run Code Online (Sandbox Code Playgroud)

而是从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' .
Run Code Online (Sandbox Code Playgroud)

将复制file-name-version-1.jarand file-name-version-1.2.3.4.5.jar(<x-y>是从xto的任何十进制整数y<->是任何十进制整数,#就像正则*表达式运算符(前面的原子的 0 个或多个))