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) 命令并将您想要执行的所有命令传递给它。您将获得一个无会话的执行环境,更适合您要求的任务(尽管您将通过电子邮件将脚本输出发送给运行脚本的目标用户)