bash + Linux +如何忽略字符“!”

时间:2016-06-29 10:00:18

标签: linux bash shell ssh

我想通过ssh

将小脚本发送到远程机器

脚本是

#!/bin/bash
sleep 1
reboot

但我找不到事件 - 因为“!”

 ssh 183.34.4.9 "echo -e '#!/bin/bash\nsleep 1\reboot>'/tmp/file"
 -bash: !/bin/bash\nsleep: event not found

如何忽略“!” char所以脚本将由ssh成功发送?

备注我不能在“!”之前使用“\”因为我得到了

more /tmp/file
#\!/bin/bash
sleep 1

3 个答案:

答案 0 :(得分:3)

在命令之前使用set +H禁用! style history substitution

set +H
ssh 183.34.4.9 "echo -e '#!/bin/bash\nsleep 1\reboot>'/tmp/file"

# enable hostory expnsion again
set -H

答案 1 :(得分:0)

我认为你的命令行格式不合理。你可以发送:

ssh 183.34.4.9 'echo -e "#!/bin/bash\nsleep 1\nreboot">/tmp/file'

当我说"没有很好地形成"我的意思是你把">"在" echo"内而你忘了添加" n"在" reboot"之前,你放了" \ reboot",它将被解释为" CR" (回车)后跟" eboot"命令(我不认为存在)。

但是这里的诀窍是用(")反转昏迷(')而反过来。

答案 2 :(得分:0)

Bash以交互方式运行(这意味着您从标准输入向它提供命令而不是exec(2)来自shell脚本的命令)因此您不需要包含该行{在这种情况下{1}} (更重要的是,bash应该忽略它,但不包括爆炸,因为它是活动历史记录机制的一部分)

但为什么呢?可执行文件中的前两个字符(任何能够从二级存储中#!/bin/bash编译的文件,而不是你的情况)具有特殊含义(对于内核和shell):它们是幻数 标识内核正在加载的可执行文件的类型。这允许内核根据二进制可执行格式选择正确的可执行加载例程(例如,允许您在Linux内核中执行BSD程序,反之亦然)

这个神奇数字的特殊值由由两个字符exec(2)# 组成(按此顺序),强制内核读取完整的第一行该文件并加载该行中指定的可执行文件,允许您直接从命令行为不同的解释器执行shell脚本。它是故意完成的,因为!字符通常在shell脚本中用作注释字符。只有在解释命令的shell不是交互式shell 时才会发生这种情况。当shell加载带有这些字符的脚本时,它通常会读取第一行,以检查它是否具有#标记,并通过复制执行此操作的内核函数来加载正确的解释器。尽管是对shell的评论,但这样做是为了允许将不存储在辅助存储上的可执行文件视为#!系统调用可以处理的唯一文件),但来自{{1} (就像你的一样)。

由于您的shell以交互方式运行,并且您确实希望在不更改shell的情况下执行其命令,因此您不需要该行,并且可以完全消除它而无需禁用爆炸字符。

很抱歉,但是有关使用exec(2)选项执行shell的解决方案可能不可行,因为执行命令的shell是目标计算机中的登录shell ,所以你无法为其提供特定参数(参数由login(8)程序选择,通常不包括任意参数,如stdin)。

最佳解决方案是完全消除-H行,因为您不会在目标中-H该程序。如果你想从输入行选择shell(如果用户有一个不同的shell安装为登录shell),最好在命令行中调用所需的shell并传递它(通过stdin,或者让它读取shell脚本作为文件)你要执行的shell命令(但同样没有#!/bin/bash行)。

确保你执行整个事情非常重要,所以最好将所有脚本内容传递到目标目标中,并且一旦确定你已经通过整个事情来执行它作为一个整体。然后,您的exec(2)第一行将被正确处理,因为可执行文件将通过内核生成的#!运行。

示例:

#!

更复杂的系统允许在发送之前签署shell脚本,并在执行之前验证脚本签名,因此可以防止可能的特洛伊木马攻击。但是这个解释超出了范围。

另一种方法是远程使用exec(2)命令并将所有要执行的命令传递给它。您将获得一个无会话执行环境,更适合您要求的任务(尽管您将通过电子邮件将脚本输出发送给运行该脚本的目标用户)