【问题标题】:wrapping commands through ssh: how to manage complex quotes?通过 ssh 包装命令:如何管理复杂的引号?
【发布时间】:2018-04-06 15:31:15
【问题描述】:

我使用 HPC 集群。计算节点不能上网,只有前端。

所以我想包装所有需要访问互联网的命令,以便在正面执行它们。

例如:对于 wget

#!/bin/bash
ssh frontal /bin/wget "$@"

-> 工作正常

我必须包装这个 bq (google BigQuery) 命令: bq --format=json query "SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sensing_time LIKE '2016%' AND mgrs_tile == '32ULU' ORDER BY sensing_time ASC LIMIT 1000;"

我设法重新引用命令并在 CLI 上成功启动它: ssh frontal '~/downloads_and_builds/builds/google-cloud-sdk/bin/bq --format=json query "SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sensing_time LIKE '"'"'2016%'"'"' AND mgrs_tile == '"'"'32ULU'"'"' ORDER BY sensing_time ASC LIMIT 1000;"'

现在我想编写一个名为 bq 的包装器,它能够获取参数并通过 ssh 启动此命令......这是我尝试过的:

#!/bin/bash
set -eu

# all parameters in an array
args=("$@")

# unset globing (there's a * in the SELECT clause)
set -f

# managing inner quotes
arg2=`echo "${args[2]}" | perl -pe 's/'\''/'\''"'\''"'\''/g'`

# put back double quotes (") suppressed by bash
args="${args[0]} ${args[1]} \"${arg2}\""

# build command with parameters
cmd="~/downloads_and_builds/builds/google-cloud-sdk/bin/bq $args"

echo ""
echo "command without external quotes"
echo "$cmd"
echo ""

echo "testing it ..."
ssh hpc-login1 "$cmd"
echo ""

# wrapping command between simple quotes (like on the CLI)
cmd="'"'~/downloads_and_builds/builds/google-cloud-sdk/bin/bq '"$args""'"
echo "commande with external quotes"
echo "$cmd"
echo ""

echo "testing it ..."
ssh hpc-login1 $cmd
echo "done"

这是该脚本的输出: $ bq --format=json query "SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sense_time LIKE '2016%' AND mgrs_tile == '32ULU' ORDER BY sense_time ASC LIMIT 1000;"

command without external quotes
~/downloads_and_builds/builds/google-cloud-sdk/bin/bq --format=json query "SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sensing_time LIKE '"'"'2016%'"'"' AND mgrs_tile == '"'"'32ULU'"'"' ORDER BY sensing_time ASC LIMIT 1000;"

testing it ...
Waiting on bqjob_r102b0c22cdd77c2d_000001629b8391a3_1 ... (0s) Current status: DONE   

commande with external quotes
'~/downloads_and_builds/builds/google-cloud-sdk/bin/bq --format=json query "SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sensing_time LIKE '"'"'2016%'"'"' AND mgrs_tile == '"'"'32ULU'"'"' ORDER BY sensing_time ASC LIMIT 1000;"'

testing it ...
bash: ~/downloads_and_builds/builds/google-cloud-sdk/bin/bq --format=json query "SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sensing_time LIKE '2016%' AND mgrs_tile == '32ULU' ORDER BY sensing_time ASC LIMIT 1000;": Aucun fichier ou dossier de ce type (in english: no file or directory of this kind)

如您所见,我设法获得了正确的命令字符串,就像在 CLI 上运行的那样,但它在我的脚本中不起作用:

  1. 第一次尝试成功但没有输出(我已尝试将其重定向到文件中:文件已创建但为空)
  2. 在第二次尝试中(使用外部简单引号,就像 CLI 命令一样),bash 将带引号的 arg 作为一个块,但找不到命令...

有人知道如何使用包装脚本通过 ssh 启动复杂的命令(带引号、通配符...)吗?

(即。一个名为 foo 的包装器能够替换 foo 命令并使用提供的参数通过 ssh 正确执行它)

【问题讨论】:

  • 从文件中读取查询不是一个选项吗? bq query < query_text.sql。最好不要乱跑。

标签: bash ssh google-bigquery double-quotes single-quotes


【解决方案1】:

ssheval 具有相同的语义:所有参数都与空格连接,然后作为 shell 命令进行评估。

你可以让它与execve语义(如sudo)一起工作,方法是让包装器转义参数:

remotebq() { 
  ssh yourhost "~/downloads_and_builds/builds/google-cloud-sdk/bin/bq $(printf '%q ' "$@")"
}

此引用彻底且一致,因此您不再需要担心添加额外的转义。它会按照你告诉它的方式运行(只要你的远程 shell 是bash):

remotebq --format=json query "SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sensing_time LIKE '2016%' AND mgrs_tile == '32ULU' ORDER BY sensing_time ASC LIMIT 1000;"

但是,完全按照您的指示运行的不利之处在于,现在您需要确切地知道要运行什么。

例如,您不能再将 '~/foo' 作为参数传递,因为这不是一个有效的文件:~ 是一个 shell 功能,而不是一个目录名称,当它被正确转义时,它不会被您的主目录。

【讨论】:

  • 太棒了!它完全解决了我的问题! printf '%q' ...我不知道。这是我多年来一直想知道的技巧,非常感谢!也谢谢你的建议~
  • @DuGnu 如果它帮助你解决了你的问题,请接受这个答案。
【解决方案2】:

执行此操作的基本方法,使用 shell here doc

#!/bin/bash

ssh -t server<<'EOF'
bq --format=json query "SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sensing_time LIKE '2016%' AND mgrs_tile == '32ULU' ORDER BY sensing_time ASC LIMIT 1000;"
command2
command3
...
EOF

【讨论】:

  • 是的,它可以与 bq 的完整路径一起使用(包装器位于 PATH 中的第一个),但它不能解决问题,因为您建议的内容是硬编码的。每次请求都不相同。而且你不能在heredoc部分放一个var,因为它没有被评估
  • 我用的是另一个账号,要求同时绑定,还在等待
  • @DuGnu 如果您不引用此处的 doc 令牌(即使用 EOF 而不是 'EOF',则变量将在客户端扩展。您需要注意转义任何您希望在服务器端发生的扩展。
【解决方案3】:

我看到你已经在使用 Perl 所以...

use Net::OpenSSH;

my $query = q(SELECT * FROM [bigquery-public-data:cloud_storage_geo_index.sentinel_2_index] WHERE sensing_time LIKE '2016%' AND mgrs_tile == '32ULU' ORDER BY sensing_time ASC LIMIT 1000;);

my $ssh = Net::OpenSSH->new($host);
$ssh->system('bq', '--format=json', 'query', $query)
  or die $ssh->error;

Net::OpenSSH 会负责引用所有内容。

【讨论】:

    猜你喜欢
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    • 2011-04-11
    • 1970-01-01
    • 2015-12-13
    • 2015-07-25
    • 1970-01-01
    相关资源
    最近更新 更多