通过 SSH 包装命令:如何管理复杂的报价?



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

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

例如:对于 WGET

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

->工作正常

我必须包装这个bq(谷歌大查询)命令: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] 其中sensing_time像'2016%'和mgrs_tile== '32ULU'顺序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 作为块并且找不到命令......

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

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

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

你可以通过让包装器转义参数来让它与execve语义(如 sudo)一起工作:

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

这句话彻底且一致,因此您不再需要担心添加额外的转义。它将完全按照您告诉它的方式运行(只要您的远程 shellbash):

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

执行此操作的基本方法,在这里使用 shell 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

我看到你已经在使用 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会负责引用一切。

最新更新