防止ssh中断Shell脚本参数传递

7

我有一个脚本,实际上是在另一台机器上的同名可执行文件的包装器。为了举例,这里我将包装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个回答

9

参考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"-]

似乎也适用于换行符(尽管如果它们出现在错误的位置,换行符会在终端上引起其他奇怪的显示问题) - Glyph

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

这适用于空格。如果参数中有嵌入的单引号,则无法使用。


可以的,谢谢。我会发布另一个答案,同时也解决引号问题。 - Ondergetekende

2

准确地引用内容很难,而在bash中以一种普遍和强大的方式完成几乎不可能。

使用Perl:

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

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.
# https://dev59.com/2FjUa4cB1Zd3GeqPV_L8

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[@]}"

1
最简单快捷的方法就是使用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"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接