eval printf从命令行工作,但在脚本中不工作



当我在终端中运行以下命令时,它起作用,而不是脚本:

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内核。为此,我写了一个小脚本

  1. 为遥控器发送命令以启动内核
  2. 通过SCP复制一个配置文件,我可以用它来连接我本地计算机的内核
  3. 读取配置文件,并在本地和远程之间打开适当的SSH隧道

对于那些不熟悉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语句使用所有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连接到遥控器。

正如其他人所说的那样, printf "-L ..."上的bash printf barfs。它认为您将其传递给-L选项。您可以通过添加--

来解决它
printf -- "-L $port:127.0.0.1:$port "

让我们做到这一点:

printf -- '-L %s:127.0.0.1:%s ' "$port" "$port"

,但是由于我们在这里,我们可以做得更好。首先,让我们不要使用基本的外壳工具处理JSON。我们不想依靠它以某种方式格式化。我们可以使用 JQ ,轻巧且灵活的命令行JSON处理器。

$ jq -r 'to_entries | map(select(.key | test(".*_port"))) | .[].value' bar.json
35932
37145
42704
39329
39253

在这里,我们使用to_entries将每个字段转换为键值对。然后,我们 select 条目,其中 .key匹配正则 .*_port。最后,我们提取相应的.values。

我们可以通过在数组中构造ssh命令来摆脱eval。尽可能避免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[@]}"

最新更新