Bash命令行参数通过ssh传递给sed

Add*_*ons 1 linux ssh bash shell sed

我期待编写一个简单的脚本来同时在许多主机上执行SSH命令,并且哪些主机完全是从另一个脚本生成的.问题是,当我使用像sed这样的sometihng运行脚本时,它无法正常工作.

它应该运行sshall.sh {anything here},它将{anything here}在列表中的所有节点上运行该部分.

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
Run Code Online (Sandbox Code Playgroud)

当它运行类似whoami它的工作但我运行时:

[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

…
Run Code Online (Sandbox Code Playgroud)

请注意,当发送到远程客户端时,引号会在sed命令上消失.

  1. 我如何修复我的bash命令?
  2. 有没有更好的方法来实现这一目标?

Cha*_*ffy 6

将eval-safe引用的命令版本替换为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*]")
Run Code Online (Sandbox Code Playgroud)

为什么它可靠地工作(而其他方法不可行):

  • printf %q 知道如何引用由同一个shell进行评估的内容(因此始终支持空格,通配符,各种本地引用方法等).
  • 给出的参数ssh 不会单独传递给远程命令! 相反,它们被连接成一个传递给的字符串sh -c.
  • 但是:输出printf %q不能移植到所有POSIX派生的shell!它保证与本地使用的相同shell兼容- ksh将始终解析printf '%q'ksh中的输出,bash将解析bash中的输出printf '%q'等; 因此,你不能安全地在远程参数向量上传递这个字符串,因为它/bin/sh不是bash- 在那里运行.(如果您知道您的遥控器/bin/sh是由bash提供的,那么您可以ssh "$hostname" "$cmd_q"安全地运行,但仅限于此条件下).
  • bash -s读取脚本从stdin运行,这意味着在那里传递命令 - 而不是在参数向量上 - 确保它将被转发到参数中,并将其转义为shell安全的相同shell.