如果我ls
在远程主机中没有 for 循环的文件,一切都很好,但是,如果我捕获ls
变量中的 并尝试echo
每个文件,它会失败,因为变量没有扩展/没有值。
我的意思是说:
IFS=$'\n'
servers=(
blue
green
red
)
for i in ${servers[@]}; do
ssh $i "
ls /dir/xyz_${i}*/details.txt
"
done
/dir/xyx_blue0/details.txt
/dir/xyz_blue1/details.txt
/dir/xyx_green2/details.txt
/dir/xyz_green4/details.txt
/dir/xyx_red1/details.txt
/dir/xyz_red8/details.txt
但我实际上需要循环遍历输出,ls
以便我可以对文件执行操作,但是变量不会扩展:
for i in ${servers[@]}; do
ssh $i "
for i in $(ls /dir/xyz_${i}*/details.txt); do
echo $i
done
"
done
not found: /dir/xyx_blue*/details.txt
not found: /dir/xyx_green*/details.txt
not found: /dir/xyx_red*/details.txt
$i
在远程主机上运行循环时如何扩展?
问题在于您想要在远程服务器上运行的命令替换是在本地运行的。这是因为替换发生在双引号内,这意味着本地 shell 将在调用之前计算其扩展
ssh
。另外,读取 的输出
ls
是不明智的(请参阅为什么 *不* 解析 `ls`(以及该怎么做)?)。反而:
这使用不带引号的“here-document”作为发送到远程服务器的脚本。该脚本首先设置
nullglob
shell 选项(假设远程 shell 是bash
),以避免在模式与任何文件名不匹配时在内循环中发生循环。/dir/xyz_$server*/details.txt
然后它迭代将用$server
当前服务器名称替换的模式。然后,循环打印出一条关于哪些文件名与模式匹配的短消息,并且代码
$pathname
通过转义$
.请注意,
$
in$server
未转义,因此将由本地 shell 扩展,这正是我们想要的,但需要对$
in$pathname
进行转义以抑制本地扩展。我们不需要设置
IFS
任何非默认值。我假设您这样做是为了能够像您一样编写数组分配,或者出于其他原因,但上面的代码都不需要这样做。我使用
-T
withssh
显式关闭伪 TTY 分配。如果您觉得有必要,也可以将脚本作为单个字符串提供:
在这种情况下,您还必须确保对每个嵌入的双引号进行转义(否则它们将结束构成远程 shell 脚本的双引号字符串)。
切勿对 ls 的输出进行 for 循环;从字面上看,这就是 * 通配符的用途,而您对 ls 的整个使用是完全多余的。
老实说,这解决了整个问题,因为您当前正在
$(…)
本地而不是目标主机上运行。