通过ssh包装命令:如何管理复杂的引号?

时间:2018-04-06 15:31:15

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

我使用HPC群集。计算节点无法访问互联网,只能访问正面。

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

ex:for 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 sensing_time LIKE' 2016%' AND mgrs_tile ==' 32ULU' ORDER BY sensing_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作为一个块并且找不到命令...
  3. 有人知道如何使用包装脚本通过ssh启动一个复杂的命令(带引号,通配符......)吗?

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

3 个答案:

答案 0 :(得分:2)

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

#!/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

答案 1 :(得分: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功能而不是目录名,当它正确转义时它不会被您的主目录替换。

答案 2 :(得分:0)

我看到你已经在使用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会照顾所有内容。