当我在终端中运行以下命令时,它可以运行,但不能从脚本中运行:
eval $(printf "ssh foo -f -N "; \
for port in $(cat ~/bar.json | grep '_port' | grep -o '[0-9]\+'); do \
printf "-L $port:127.0.0.1:$port ";\
done)
我得到的错误告诉我,printf的用法是错误的,好像引号内的-L参数是printf本身的参数。 我想知道为什么会这样。我缺少明显的东西吗?
__
上下文(如果我的问题是XY问题):我想启动并连接到在远程计算机上运行的jupyter内核。为此,我编写了一个小脚本
对于不熟悉jupyter的用户来说,配置文件(bar.json)看起来大致如下:
{
"shell_port": 35932,
"iopub_port": 37145,
"stdin_port": 42704,
"control_port": 39329,
"hb_port": 39253,
"ip": "127.0.0.1",
"key": "4cd3e12f-321bcb113c204eca3a0723d9",
"transport": "tcp",
"signature_scheme": "hmac-sha256",
"kernel_name": ""
}
因此,在上面的命令中,printf语句创建一个ssh命令,其中包含所有5 -L
端口转发,以便我的本地计算机连接到远程计算机,并且eval应该运行该命令。这是完整的脚本:
#!/usr/bin/env bash
# Tell remote to start a jupyter kernel.
ssh foo -t 'python -m ipykernel_launcher -f ~/bar.json' &
# Wait a bit for the remote kernel to launch and write conf. file
sleep 5
# Copy the conf. file from remote to local.
scp foo:~/bar.json ~/bar.json
# Parse the conf. file and open ssh tunnels.
eval $(printf "ssh foo -f -N "; \
for port in $(cat ~/bar.json | grep '_port' | grep -o '[0-9]\+'); do \
printf "-L $port:127.0.0.1:$port ";\
done)
最后,jupyter console --existing ~/foo.json
连接到远程计算机。
答案 0 :(得分:3)
正如@other所说的那样,bash的printf内置printf "-L ..."
上的barfs。它认为您正在传递一个-L
选项。您可以通过添加--
来解决此问题:
printf -- "-L $port:127.0.0.1:$port "
让我们做到这一点
printf -- '-L %s:127.0.0.1:%s ' "$port" "$port"
但是既然我们在这里,我们可以做得更好。首先,我们不要使用基本的shell工具来处理JSON。我们不想依靠它以某种方式进行格式化。我们可以使用jq,这是一种轻巧灵活的命令行JSON处理器。
$ jq -r 'to_entries | map(select(.key | test(".*_port"))) | .[].value' bar.json
35932
37145
42704
39329
39253
在这里,我们使用to_entries
将每个字段转换为键值对。然后我们选择条目,其中.key
与正则表达式.*_port
相匹配。最后,我们提取相应的.value
。
我们可以通过在数组中构造eval
命令来摆脱ssh
。尽可能避免使用eval
。
#!/bin/bash
readarray -t ports < <(jq -r 'to_entries | map(select(.key | test(".*_port"))) | .[].value' bar.json)
ssh=(ssh foo -f -N)
for port in "${ports[@]}"; do ssh+=(-L "$port:127.0.0.1:$port"); done
"${ssh[@]}"