【问题标题】:bash + Linux + how to ignore the character "!"bash + Linux + 如何忽略字符“!”
【发布时间】:2016-06-29 10:00:18
【问题描述】:

我想通过 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 成功发送?

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

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

【问题讨论】:

    标签: linux bash shell ssh


    【解决方案1】:

    在您的命令前使用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
    

    【讨论】:

    • 是否可以在同一行执行 chmod +x ?
    • 是的(我不想运行 ssh 两次,因为 chmod +x )
    • 我的意思是如果我可以在 "#!/bin/bash\nsleep 1\reboot>'/tmp/file" 之后添加 chmod +x /tmp/file 作为 #!/bin/ bash\nsleep 1\reboot>'/tmp/file ; chmod +x /tmp/文件
    • 您可以使用:ssh 183.34.4.9 "echo -e '#!/bin/bash\nsleep 1\nreboot' > /tmp/file; chmod +x /tmp/file"
    【解决方案2】:

    我认为您的命令行格式不正确。你可以发送这个:

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

    当我说“格式不正确”时,我的意思是您将“>”放在“echo”中,而您忘记在“rebo​​ot”之前添加“n”,而您输入了“\reboot”,这将被解释为“ CR”(回车)后跟“eboot”命令(我认为不存在)。

    但这里的诀窍是用 (") 反转逗号更改 ​​('),反之亦然。

    【讨论】:

    • 关于你的问题“chmod +x 在同一行”...谷歌搜索“bash more than one command in one line”怎么样?...无论如何,你可以添加“; chmod +x /tmp/file”在你的命令行的末尾是这样的:ssh 183.34.4.9 'echo -e "#!/bin/bash\nsleep 1\nreboot">/tmp/file; chmod +x /tmp/file'
    【解决方案3】:

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

    但是为什么呢?可执行文件中的前两个字符(任何能够从二级存储中被exec(2)ed 的文件,而不是您的情况)具有特殊含义(对于内核和外壳):它们是幻数 标识内核正在加载的可执行文件的类型。这允许内核根据二进制可执行格式(以及例如允许您在 linux 内核中执行 BSD 程序,反之亦然)选择适当的可执行加载例程

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

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

    抱歉,关于使用 -H 选项执行 shell 的解决方案可能不可行,因为执行命令的 shell 是目标机器中的登录 shell,因此您无法提供它的特定参数(参数由 login(8) 程序选择,通常不包括任意参数,如-H)。

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

    注意

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

    例子:

    DIRECTORY=/bla/bla
    FILE=/path/to/file
    OUTPUT=/path/to/output
    
    # this is the command we want to pass through the line
    cat <<EOF | ssh user@target "cat >>/tmp/shell.sh"
    cd $DIRECTORY
    foo $FILE >$OUTPUT
    exit 0
    EOF 
    # we have copied the script file in a remote /tmp/shell.sh
    # and we are sure it has passed correctly, so it's ready 
    # for local execution there.
    # now, execute it.
    # the remote shell won't be interactive, and you'll ensure that it is /bin/bash
    ssh user@target "/bin/bash /tmp/shell.sh" >remote_shell.out 
    

    更复杂的系统是允许在发送前对 shell 脚本进行签名,并在执行脚本前验证脚本签名,从而保护您免受可能的特洛伊木马攻击。但这超出了这个解释的范围。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-10
      • 1970-01-01
      • 2014-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多