通过 SSH 执行 bash 命令,同时保持交互模式

通过 SSH 执行 bash 命令,同时保持交互模式

也许这是我想要实现的一件奇怪的事情,但是:
我想通过 SSH 连接到远程主机,然后自动执行一些 bash 命令,例如“alias”,以便随后在交互模式下使用它们。远程主机不允许个人环境。

这可能吗? “期望”会这样做吗?或者还有其他方法可以实现这一目标吗?
基本上我希望我的 bashrc 不修改远程主机上的任何文件。

答案1

换句话说,您想要拥有一个遥控器~/.bashrc,但您不能,甚至不能使用其他名称。 Bash 不支持将初始命令作为命令行参数或通过环境变量传递,它们必须位于文件中。但是,该文件不必位于文件系统上!

bash --rcfile /dev/fd/3 3< <(echo 'alias foo="echo hello"')
$ foo
hello

许多SSH服务器允许传输名称为 的环境变量LC_XXX,因为它们通常用于指示区域设置,需要在主机之间传输并且没有安全隐患。如果您允许,您可以通过环境变量传输内容.bashrc,然后将该环境变量输入到文件描述符中。

LC_BASHRC=$(cat ~/.bashrc; exec 3<&-) \
ssh -t remote.example.com \
    'exec bash --rcfile /dev/fd/3 3< <(printf %s "$LC_BASHRC")'
  • 本地的内容~/.bashrc在环境变量中传输到服务器LC_BASHRC
  • exec 3<&-附加,以在读取文件后关闭文件描述符。
  • 在远程端,登录 shellexec被新的 bash 实例替换 ( ),该实例被告知读取文件描述符 3 上的初始化文件。
  • 3< <(…)将文件描述符 3 重定向到命令替换: 的输出printf通过管道馈送到父进程。

如果服务器上的登录 shell 不是/bin/shbash 或 ksh,则无法直接在该 shell 中使用进程替换,您需要一个额外的层:

LC_BASHRC=$(cat ~/.bashrc; exec 3<&-) \
ssh -t remote.example.com \
    'exec bash -c '\''exec bash --rcfile /dev/fd/3 \
                           3< <(printf %s "$LC_BASHRC")'\'

您可以通过查找AcceptEnv directive in its [配置](http://www.openbsd.org/cgi-bin/man.cgi?query=sshd_config&sektion=5) (/etc/sshd_config or/etc/ssh/sshd_config` 来检查 SSH 服务器接受哪些环境变量。

3< <(…)您可以使用此处字符串,而不是使用命令替换。这会创建一个临时文件(在$TMPDIR或中/tmp)而不是管道,因此只有在您不介意创建临时文件时这才有效。

LC_BASHRC=$(cat ~/.bashrc; exec 3<&-) \
ssh -t remote.example.com \
    'exec bash --rcfile /dev/fd/3 3<<<"$LC_BASHRC"'

如果您不介意创建临时文件,则有一种更简单的技术:

  1. 将您的文件复制.bashrc到临时远程文件。您只需执行一次此操作,直到临时文件被删除。
  2. 启动交互式 shell 并--rcfile指向临时文件。
remote_bashrc=$(ssh remote.example.com 'bashrc=$(mktemp) && cat >>"$bashrc" && echo "$bashrc"' <~/.bashrc)
ssh -t remote.example.com "exec bash --rcfile '$remote_bashrc'"

如果 SSH 实现不是太古老,您可以使用主连接加快向同一主机启动多个 SSH 命令的速度。

如果您使用密钥登录并且可以控制公钥文件,则可以实现更多自动化。在~/.ssh/authorized_keys,一个键可以有一个command=…指令,让您运行命令而不是命令行上指定的命令。看如何为远程 rsync 进程设置环境变量?以获得更多解释。

command="if [ -n \"$SSH_ORIGINAL_COMMAND\" ]; then
           eval \"$SSH_ORIGINAL_COMMAND\";
         else exec bash -c 'bash 3<<<\"$LC_BASHRC\"'; fi" ssh-rsa …

或者

command="if [ -n \"$SSH_ORIGINAL_COMMAND\" ]; then
           eval \"$SSH_ORIGINAL_COMMAND\";
         else exec bash -c 'bash 3<<<\"alias foo=bar; …\"'; fi" ssh-rsa …

(这需要全部写在一行上,我只是为了便于阅读而换行。)

答案2

基于 Gilles 的解决方案,不需要AcceptEnv在服务器上添加LC_XXX

ssh -t remote.example.com "
  exec bash --rcfile <(
    printf '%s\n' '$(
      sed "s:':'\\\\'':g" ~/.bashrc
    )'
  )
"

~/.bashrc那就是在远程会话中使用你的本地。它利用了 bash 中单引号之间的所有内容都是按字面意思理解的事实,因此我们唯一需要担心转义的是内部单引号本身。我们可以通过用 , 包围 bash 代码''用内部 s替换'\''s 来安全地引用它。

如果您只想添加一些别名或内联内容偏僻的 ~/.bashrc:

ssh -t remote.example.com "
  exec bash --rcfile <(
    printf '%s\n' '
      . ~/.bashrc
      alias ll=\"ls -l\"
    '
  )
"

答案3

您可以SendEnv在您的.ssh/config.从 ssh_config 的手册页:

SendEnv 指定应将本地环境(7) 中的哪些变量发送到服务器。请注意,仅协议 2 支持环境传递。服务器也必须支持它,并且服务器必须配置为接受这些环境变量。请参阅 sshd_config(5) 中的 AcceptEnv 了解如何配置服务器。变量由名称指定,名称可以包含通配符。多个环境变量可以用空格分隔或分布在多个 SendEnv 指令中。默认情况下不发送任何环境变量。

相关内容