Bash命令行参数通过ssh传递给sed [英] Bash command line arguments passed to sed via ssh
问题描述
我希望编写一个简单的脚本来同时在许多主机上执行SSH命令,而哪些主机正是从另一个脚本生成的.问题是,当我像sed这样使用sometihng运行脚本时,它无法正常工作.
I am looking to write a simple script to perform a SSH command on many hosts simultaneously, and which hosts exactly are generated from another script. The problem is that when I run the script using sometihng like sed it doesn't work properly.
它应该像 sshall.sh {anything}
一样运行,并且它将在列表中的所有节点上运行 {anything here}
部分.
It should run like sshall.sh {anything here}
and it will run the {anything here}
part on all the nodes in the list.
sshall.sh
#!/bin/bash
NODES=`listNodes | grep "node-[0-9*]" -o`
echo "Connecting to all nodes and running: ${@:1}"
for i in $NODES
do
:
echo "$i : Begin"
echo "----------------------------------------"
ssh -q -o "StrictHostKeyChecking no" $i "${@:1}"
echo "----------------------------------------"
echo "$i : Complete";
echo ""
done
当它与 whoami
之类的东西一起运行时,它可以工作,但是当我运行时:
When it is run with something like whoami
it works but when I run:
[root@myhost bin]# sshall.sh sed -i '/^somebeginning/ s/$/,appendme/' /etc/myconfig.conf
Connecting to all nodes and running: sed -i /^somebeginning/ s/$/,appendme/ /etc/myconfig.conf
node-1 : Begin
----------------------------------------
sed: -e expression #1, char 18: missing command
----------------------------------------
node-1 : Complete
node-2 : Begin
----------------------------------------
sed: -e expression #1, char 18: missing command
----------------------------------------
node-2 : Complete
…
请注意,引号在发送到远程客户端时在sed命令上消失.
Notice that the quotes disappear on the sed command when sent to the remote client.
- 我该如何解决我的bash命令?
- 是否有更好的方法来实现这一目标?
推荐答案
将命令的 eval
安全引用版本替换为heredoc:
Substitute an eval
-safe quoted version of your command into a heredoc:
#!/bin/bash
# ^^^^- not /bin/sh; printf %q is an extension
# Put your command into a single string, with each argument quoted to be eval-safe
printf -v cmd_q '%q ' "$@"
while IFS= read -r hostname; do
# run bash -s remotely, with that string passed on stdin
ssh -q -o 'StrictHostKeyChecking no' "$hostname" "bash -s" <<EOF
$cmd_q
EOF
done < <(listNodes | grep -o -e "node-[0-9*]")
为什么这可靠地起作用(而其他方法却不行):
Why this works reliably (and other approaches don't):
-
printf%q
知道如何引用同一外壳程序要评估的内容(因此将始终支持空格,通配符,各种本地引用方法等). - 赋予
ssh
的参数不会单独传递给远程命令!而是将它们串联成一个字符串,该字符串传递给sh -c
. - 但是:
printf%q
的输出不能移植到所有POSIX派生的shell!保证与本地使用的同一外壳程序兼容 – ksh将始终解析ksh中printf'%q'
的输出,bash将解析的输出bash中的printf'%q'
等;因此,您不能在远程参数向量上安全地传递此字符串,因为它是/bin/sh
而不是bash
的地方.(如果您知道您的远程/bin/sh
由bash提供,则然后您可以安全地运行ssh"$ hostname""$ cmd_q"
,但仅在这种情况下). -
bash -s
从stdin读取要运行的脚本,这意味着将您的命令(而不是参数向量)传递到该位置可确保将其解析为参数逃脱它的目的是安全的shell .
printf %q
knows how to quote contents to be eval'd by that same shell (so spaces, wildcards, various local quoting methods, etc. will always be supported).- Arguments given to
ssh
are not passed to the remote command individually! Instead, they're concatenated into a string passed tosh -c
. - However: The output of
printf %q
is not portable to all POSIX-derived shells! It's guaranteed to be compatible with the same shell locally in use -- ksh will always parse output fromprintf '%q'
in ksh, bash will parse output fromprintf '%q'
in bash, etc; thus, you can't safely pass this string on the remote argument vector, because it's/bin/sh
-- notbash
-- running there. (If you know your remote/bin/sh
is provided by bash, then you can runssh "$hostname" "$cmd_q"
safely, but only under this condition). bash -s
reads the script to run from stdin, meaning that passing your command there -- not on the argument vector -- ensures that it'll be parsed into arguments by the same shell that escaped it to be shell-safe.
这篇关于Bash命令行参数通过ssh传递给sed的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!