Bash论证字面解释

时间:2013-10-24 16:13:17

标签: bash shell arguments

我有一个简单的bash脚本,可以在给定的一组服务器上运行远程命令。

#!/bin/bash

echo "Command to be run:"
echo "$*"
read nothing

servers="server1 server2 server3"

for server in `echo $servers`
do
    echo $server
    ssh $server "$*"
    echo ""
done

问题是该命令可以包含任意数量的参数,因此使用$ *并且还可以包含许多不同的字符,包括引号和正则表达式。这里的基本需求是shell接受参数,无论它们是什么,字面意思是它们完整地传递给远程服务器而不删除引号或解释括号等。

我见过很多变化,但大多数处理特定的字符问题或者使所需的脚本或参数过于复杂,我希望至少保留不包含转义符等的参数。

使用“@”的例子:

./cmd tw_query --no-headings "search Host where name matches '(?i)peter' show summary, nodecount(traverse :::Detail where name matches 'bob')"

给出:

Command to be run:
tw_query --no-headings search Host where name matches '(?i)peter' show summary, nodecount(traverse :::Detail where name matches 'bob')

3 个答案:

答案 0 :(得分:2)

您似乎在寻找$@。说:

ssh $server "$@"

代替。来自manual

  

*

     

从一个开始扩展到位置参数。当扩展发生在双引号内时,它会扩展为单个单词   每个参数的值由第一个字符分隔   IFS特殊变量。也就是说,"$*"相当于"$1c$2c…",   其中c是IFS变量值的第一个字符。如果   未设置IFS,参数由空格分隔。如果IFS为空,   参数连接时没有插入分隔符。

     

@

     

从一个开始扩展到位置参数。当扩展发生在双引号内时,每个参数都扩展为a   单词。也就是说,"$@"相当于"$1" "$2" …。如果   双语扩张发生在一个词内,扩展了   第一个参数与原始的开头部分连接在一起   单词,并将最后一个参数的扩展与最后一个参数连接起来   原始单词的一部分。没有位置参数时   "$@"$@扩展为空(即删除它们)。

答案 1 :(得分:1)

您实际上不希望传递给远程服务器的参数完好无损,您希望它们完整地传递到远程命令。但这意味着它们需要包含在额外的quotes / escapes / etc层中,以便在远程shell解析它们之后它们将完整地出现

bash实际上在其printf内置中有一个功能,可以在字符串中添加引用/转义,但它适用于bash本身的解释 - 如果远程shell是别的,它可能不理解引用它选择的模式。所以在这种情况下我会推荐一种简单愚蠢的引用样式:只需在每个参数周围添加单引号,并用'\''替换参数中的每个单引号(这将结束当前引用的字符串) ,添加一个转义(文字)引用,然后启动另一个引用的字符串)。它看起来有点奇怪,但应该在任何符合POSIX标准的shell下正确解码。

转换为这种格式有点棘手,因为bash在搜索和替换模式中与引号不一致。这就是我想出的:

#!/bin/bash

quotedquote="'\''"
printf -v quotedcommand "'%s' " "${@//\'/$quotedquote}"
echo "Command to be run:"
echo "$quotedcommand"
read nothing

servers="server1 server2 server3"

for server in $servers
do
    echo $server
    ssh $server "$quotedcommand"
    echo ""
done

以下是它如何引用你的示例命令:

'tw_query' '--no-headings' 'search Host where name matches '\''(?i)peter'\'' show summary, nodecount(traverse :::Detail where name matches '\''bob'\'')'

引用命令本身看起来很奇怪,但只要您不尝试使用别名,这不会导致任何实际问题。但是有一个重要的限制:没有办法将shell元字符(如>用于输出重定向)传递给远程shell:

./cmd somecommand >outfile    # redirect is done on local computer
./cmd somecommand '>outfile'    # ">outfile" is passed to somecommand as an argument

如果您需要执行远程重定向等操作,事情会变得更复杂。

答案 2 :(得分:0)

$*$@的问题外,如果要在生产环境中使用,您可能需要考虑使用pdsh等工具。

否则,您可以尝试通过stdin将命令提供给脚本,而不是将它们放在参数中,以避免一级解析。

#!/bin/bash

read cmd
echo "Command to be run:"
echo $cmd
servers="server1 server2 server3"

for server in `echo $servers` do
    echo $server
    ssh $server "$cmd"
    echo "" 
done

并像这样使用

$ ./cmd <<'EOT' 
> tw_query --no-headings "search Host where name matches '(?i)peter' show summary,       nodecount(traverse :::Detail where name matches 'bob')"
> EOT
Command to be run:
tw_query --no-headings "search Host where name matches '(?i)peter' show summary, nodecount(traverse :::Detail where name matches 'bob')"

也许有点牵强,但它可以奏效。