sh 递归复制 (cp -r) - 如何排除子文件夹

Roj*_*ojj 8 shell ssh cp

我需要使用sshvia Ruby( net/ssh )运行远程脚本来递归复制文件夹并排除子文件夹。我正在寻找最快的方法,所以rsync不好。另外,我知道ssh使用sh而不是bash.

在 bash 我做:

cp -r srcdir/!(subdir) dstdir
Run Code Online (Sandbox Code Playgroud)

它工作正常。但是,当我通过启动脚本时ssh收到错误

sh: 1: Syntax error: "(" unexpected
Run Code Online (Sandbox Code Playgroud)

因为它正在使用sh.

我检查了sh手册页,但没有排除文件的选项。

这是我ssh使用sh正确的假设吗?任何替代建议?

编辑 1: 如果它有用,输出sudo cat /etc/shells如下:

# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/tmux
/usr/bin/screen
Run Code Online (Sandbox Code Playgroud)

编辑2: 好的。所以 bash 它是可用的,这似乎不是问题。我已经验证 ssh 实际上正在使用bash. 该问题似乎与括号或感叹号的转义有关。我试图从 shell (macos) 运行命令,这是实际的命令:

ssh -i .ssh/key.pem ubuntu@X.X.X.X 'mkdir /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N; cp -r /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/!\(constant\) /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N; ln -s /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/constant /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N/constant'
Run Code Online (Sandbox Code Playgroud)

通过这种方式,我收到了不同的错误

cp: cannot stat '/home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/!(constant)': No such file or directory
Run Code Online (Sandbox Code Playgroud)

编辑 3: 根据评论我改变了我的命令添加extglob

如果我使用

ssh -i .ssh/key.pem ubuntu@X.X.X.X 'shopt -s extglob; mkdir /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N; cp -r /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/!\(constant\) /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N; ln -s /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/constant /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N/constant'
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

cp: cannot stat '/home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/!(constant)': No such file or directory
Run Code Online (Sandbox Code Playgroud)

如果我不转义括号,我会得到

bash: -c: line 0: syntax error near unexpected token `('
Run Code Online (Sandbox Code Playgroud)

Ral*_*edl 10

我不知道你为什么认为 rsync 会很慢。拷贝的速度主要取决于磁盘的速度。Rsync 有许多选项来指定您想要包含和排除的内容,因此它为您提供比 shell globbing 更好的控制。

正如 bash 手册所述,!(patter)仅在 bash 中extglob被识别 if被设置。在您的示例中,您没有设置extglob. 此外,一个bash开始sh的仍然是bash,但为了兼容性会禁用一些扩展。

SSH 服务器将启动用户的登录 shell,如/etc/passwd. 您可以更改 shell,或使用该 shell 启动另一个更适合您需要的 shell。

  • @Rojj 这是苹果与橙子的比较。一方面,您将 -a 用于 rsync 而不是用于 cp。这涉及保留权限和其他属性,因此您实际上并没有做同样的事情。 (7认同)

ilk*_*chu 10

SSH 在远程系统上运行您的登录 shell,无论是什么。但是!(foo)requires shopt -s extglob,您可能没有在遥控器上设置。

试试这个,看看 SSH 是否在远程端运行 Bash:

ssh me@somehost 'echo "$BASH_VERSION"'
Run Code Online (Sandbox Code Playgroud)

如果打印出任何内容,但您的启动脚本没有设置extglob,您可以通过传递给的命令手动完成ssh

ssh me@somehost 'shopt -s extglob
    echo srcdir/!(subdir)'                                 
 # or
ssh me@somehost $'shopt -s extglob\n echo srcdir/!(subdir)'   
Run Code Online (Sandbox Code Playgroud)

extglob 影响命令行的解析,并且只在换行后生效,所以我们必须在那里放一个文字换行,一个分号是不够的。

ssh me@somehost 'shopt -s extglob; echo srcdir/!(subdir)'

也不是如果你用反斜杠转义括号,它们会失去它们的特殊属性,就像任何其他 glob 字符一样。在这种情况下,这不是您想要做的。

$ touch foo bar; shopt -s extglob; set +o histexpand
$ echo *
bar foo
$ echo !(foo)
bar
$ echo \*
*
$ echo !\(foo\)
!(foo)
Run Code Online (Sandbox Code Playgroud)


Sté*_*las 6

先说几点:

  • ssh 服务器不会开始sh解释客户端发送的命令行,它会在远程主机上运行用户的登录 shell,如that-shell -c <the-string-provided-by-the-client>. 远程用户的登录 shell 可以是任何东西。请记住,一些炮弹喜欢tcshfish或者rc有来自非常不同的语法sh
  • 它实际上是一个命令行,或者更确切地说是一个字符串(可以包含换行符,所以是多行)。即使你ssh host cmd arg1 'arg 2'在那里cmdarg1arg 2三个参数传递给sshssh串接与空间的参数和实际发送的cmd arg1 arg 2字符串sshd,以及远程shell会拆分成cmdarg1arg2
  • !(subdir)是 glob 运算符(和ksh也支持的glob 运算符)。与所有 glob 一样,它排除隐藏文件,因此请注意它可能排除了其他文件。zsh -o kshglobbash -O extglob

在这里,为了避免找出远程 shell 的正确语法的问题,您实际上可以告诉其他 shell 启动您想要的 shell 并通过 stdin 向它提供代码(如何执行任意简单的选项中列出的选项之一)在不知道远程用户的登录 shell 的情况下通过 ssh 命令?

ssh host 'bash -O extglob -O dotglob' << 'EOF'
cp -r srcdir/!(subdir) dstdir/
EOF
Run Code Online (Sandbox Code Playgroud)

bash -O extglob -O dotglob是所有主要 shell 都理解相同的命令行,包括类似 Bourne 的 shell、csh、rc、fish……只要bash安装并在用户中$PATH(默认$PATH,可能由用户的登录 shell 就像~/.zshenvfor zsh, ~/.cshrcfor csh, ~/.bashrcfor bash)。

POSIXly(尽管在实践中,您可能会发现更多的系统具有bash命令而不是pax命令),您可以执行以下操作:

ssh host sh << 'EOF'
cd srcdir && pax -rw -'s|^\.//\./subdir\(/.*\)\{0,1\}$||' .//. /path/to/destdir/
EOF
Run Code Online (Sandbox Code Playgroud)

-s将替换应用于正在传输的路径。当该替换扩展为空时,将排除该文件。问题是替换也适用于符号链接的目标。这就是为什么我们使用.//.上面的方法来降低符号链接受到影响的可能性。