![SSH find 命令无法识别存储在本地变量中的目录](https://linux22.com/image/170291/SSH%20find%20%E5%91%BD%E4%BB%A4%E6%97%A0%E6%B3%95%E8%AF%86%E5%88%AB%E5%AD%98%E5%82%A8%E5%9C%A8%E6%9C%AC%E5%9C%B0%E5%8F%98%E9%87%8F%E4%B8%AD%E7%9A%84%E7%9B%AE%E5%BD%95.png)
我能够通过 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
.
通过标准输入传递远程脚本很少(如果有的话!)是必要的。