防止ssh破坏shell脚本参数

时间:2011-07-06 06:29:01

标签: linux bash ssh

我有一个脚本,它本质上是一个不同机器上同名可执行文件的包装器。为了举例,我将在这里包装printf。我当前的脚本如下所示:

#!/bin/bash
ssh user@hostname.tld. printf "$@"

不幸的是,当其中一个参数包含空格时会中断,例如我希望以下命令能够提供相同的输出。:

~$ ./wrap_printf "%s_%s" "hello world" "1"
hello_world1_
~$ printf "%s_%s" "hello world" "1"
hello world_1

涉及(转义)换行符时,问题会变得更糟。我怎么能在这里正确地逃避我的论点?

5 个答案:

答案 0 :(得分:7)

基于Peter Lyons的回答,但也允许在参数内引用:

#!/bin/bash
QUOTE_ARGS=''
for ARG in "$@"
do
  ARG=$(printf "%q" "$ARG")
  QUOTE_ARGS="${QUOTE_ARGS} $ARG"
done

ssh user@hostname.tld. "printf ${QUOTE_ARGS}"

这适用于我到目前为止测试的所有内容,除了换行符:

$ /tmp/wrap_printf "[-%s-]" "hello'\$t\""
[-hello'$t"-]

答案 1 :(得分:5)

#!/bin/sh
QUOTE_ARGS=''
for ARG in "$@"
do
  QUOTE_ARGS="${QUOTE_ARGS} '${ARG}'"
done
ssh user@hostname.tld. "${QUOTE_ARGS}"

这适用于空间。如果参数具有嵌入的单引号,则它不起作用。

答案 2 :(得分:2)

正确引用是非常困难的,并且以bash(以一般而且健壮的方式)执行它几乎是不可能的。

使用Perl:

#!/usr/bin/perl
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new('user@hostname');
$ssh->system('printf', @ARGV);

答案 3 :(得分:1)

基于Koert和Peter Lyons的答案,这里是ssh的包装器;我称之为“sshsystem”。 (也可在https://gist.github.com/4672115获得)

#!/bin/bash

# quote command in ssh call to prevent remote side from expanding any arguments
# uses bash printf %q for quoting - no idea how compatible this is with other shells.
# http://stackoverflow.com/questions/6592376/prevent-ssh-from-breaking-up-shell-script-parameters

sshargs=()

while (( $# > 0 )); do
    case "$1" in
    -[1246AaCfgKkMNnqsTtVvXxYy])
        # simple argument
        sshargs+=("$1")
        shift
        ;;
    -[bcDeFIiLlmOopRSWw])
        # argument with parameter
        sshargs+=("$1")
        shift
        if (( $# == 0 )); then
            echo "missing second part of long argument" >&2
            exit 99
        fi
        sshargs+=("$1")
        shift
        ;;
    -[bcDeFIiLlmOopRSWw]*)
        # argument with parameter appended without space
        sshargs+=("$1")
        shift
        ;;
    --)
        # end of arguments
        sshargs+=("$1")
        shift
        break
        ;;
    -*)
        echo "unrecognized argument: '$1'" >&2
        exit 99
        ;;
    *)
        # end of arguments
        break
        ;;
    esac
done


# user@host
sshargs+=("$1")
shift

# command - quote
if (( $# > 0 )); then
    # no need to make COMMAND an array - ssh will merge it anyway
    COMMAND=
    while (( $# > 0 )); do
        arg=$(printf "%q" "$1")
        COMMAND="${COMMAND} ${arg}"
        shift
    done
    sshargs+=("${COMMAND}")
fi

exec ssh "${sshargs[@]}"

答案 4 :(得分:0)

最简单,最快的方法是仅使用Bash的报价参数转换:${parameter@Q}。这可以在使用${array[@]@Q}进行数组扩展期间自动应用,但是当使用内置参数数组时,名称和方括号将被删除,因此它变为${@@Q}。因此,原始脚本只需要添加4个字符即​​可。

#!/bin/bash
ssh user@hostname.tld. printf "${@@Q}"

现在,任何转义都可以使用,即使终端颜色是这样的:

./wrap_printf "%s\e[39m\e[49m\n" $'\e[30m\e[42mBlack on Green' "Just Normal Text"