SSH find 命令无法识别存储在本地变量中的目录

SSH find 命令无法识别存储在本地变量中的目录

我能够通过 SSH 访问存储在本地变量中的目录$out_dir。但是,我无法弄清楚为什么该find | head命令不起作用。在尝试#2,我在命令末尾添加了第二个反斜杠find

尝试 #1 错误: find: `/dir/on/remote/server': No such file or directory

ssh $user@$ip << EOF
    discard=$( find $out_dir -exec basename {} \; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
    logout
EOF

尝试 #2 错误: syntax error near unexpected token `|'

ssh $user@$ip << EOF
    discard=$( find $out_dir -exec basename {} \\; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
    logout
EOF

答案1

您在尝试 #1 中得到“没有这样的文件或目录”的原因是,heredoc (the <<EOF) 的所有变量和命令插值都是在本地完成的,然后才发送到远程主机。

正如您正确地注意到的那样,$out_dir正在进行插值;你会看到你想看的目录。在您进行 ssh 调用之前,这会在您的本地计算机上发生。命令替换 ( $()) 也在本地发生(但您打算远程执行此操作)。换句话说,整个查找结果在发送到远程计算机之前都在本地进行处理——包含在其中的所有内容$()都由heredoc 处理。因此,它似乎${out_dir}不存在于您的本地计算机上——“没有这样的文件或目录”。

如果您想自己更好地了解这一点,让我们简化这个示例。尝试这个:

$ ssh foo@localhost <<EOF
  echo "using account: $(whoami)"
EOF                            
Pseudo-terminal will not be allocated because stdin is not a terminal.
<truncated>
using account: vagrant

显然 $() 的内容是在本地执行的,因为我的本地帐户是“vagrant”;如果工作正常,该帐户将是“foo”,因为我指定了ssh foo@localhost.

尝试 #2 已损坏,因为您已经转义了“\”(而不是“;”)。 Find 需要-exec终止,但现在不是。相反,命令突然结束,并且有一个悬空的“;”这会终止 bash 命令。 '|'期望转发一些输入,但没有指定任何内容。实际上,您创建了这个:

$ | cat
-bash: syntax error near unexpected token `|'

那么,什么是可行的解决方案?

好吧,find可以为您删除:

ssh $user@$ip <<EOF
  find $out_dir -delete
EOF

简单又直接。如果您担心选择某些文件并排除其他文件,请修改find.

我给出“修改find”建议,因为在您当前的版本中,您似乎正在尝试进行一些边缘情况处理:

  • 基本名称的使用
  • 指某东西的用途head -n -1

但我不希望这种处理按照您的预期进行。

首先,调用basename将删除大部分路径,并且在 ssh 命令中,您不会执行任何更改目录的操作。您的 ssh 执行将尝试删除与 ${user} 的 home 相关的所有内容 - 在指定 ${out_dir} 时,您似乎想要使用 home 以外的目录!再次,让我们find为您执行删除操作。

其次,我可能对这一点有所误解,但我怀疑您曾经head -n -1从目标文件列表中删除 ${out_dir} 。

$ find junk/
junk/
junk/one
junk/two
junk/three

正确的是,您不想删除junk/.

不过,试试这个:

$ seq 1 4
1
2
3
4
$ seq 1 4 | head -n -1
1
2
3

这不会保留第一行,而是保留最后一行。尝试尾巴:

$ seq 1 4 | tail -n +2
2
3
4

但同样,让我们find​​帮您删除。

如果您需要 find 仅删除文件,请查看-type f.但也有用于更高级过滤的排除和包含模式。

答案2

尝试这个:

ssh "$user@$ip" "out_dir=$out_dir;" '
    discard=$( find $out_dir -exec basename {} \; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
'

笔记:预期的远程命令可以变得更简单、更安全,但对此吹毛求疵只会混淆要点。

基本上,您应该通过以下部分A)应该扩展至当地的机器和那些b)应该仅有的扩展至偏僻的机器作为分离ssh 的参数,前者用双引号,后者用单引号。

ssh 会将所有“命令”参数与空格连接起来,然后将它们作为单个参数传递给-c远程计算机上用户登录 shell 的选项。

如果远程命令本身应包含单引号,则可以使用命令替换+此处文档组合。另外,如果“local”变量的值可以包含空格和其他特殊字符,则应该对它们进行转义:

out_dir='/path/to/ * happiness * '
out_dir=$(printf %s "$out_dir" | sed 's/[^a-zA-Z0-9_/.]/\\&/g')
ssh localhost out_dir="$out_dir;" "$(cat <<'EOT'
  echo '<$out_dir>' = "<$out_dir>"
EOT
)"

<<'EOF'请注意;中的单引号如果省略,则$out_dir此处文档中的 将会展开(在本地计算机上)。

bash 的最新版本具有以下${var@Q}形式,因此您可以简单地使用"out_dir=${outdir@Q};"而不是那个丑陋的echo | sed.

通过标准输入传递远程脚本很少(如果有的话!)是必要的。

相关内容