也许这是我想要实现的一件奇怪的事情,但是:
我想通过 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<&-
附加,以在读取文件后关闭文件描述符。- 在远程端,登录 shell
exec
被新的 bash 实例替换 ( ),该实例被告知读取文件描述符 3 上的初始化文件。 3< <(…)
将文件描述符 3 重定向到命令替换: 的输出printf
通过管道馈送到父进程。
如果服务器上的登录 shell 不是/bin/sh
bash 或 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"'
如果您不介意创建临时文件,则有一种更简单的技术:
- 将您的文件复制
.bashrc
到临时远程文件。您只需执行一次此操作,直到临时文件被删除。 - 启动交互式 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 指令中。默认情况下不发送任何环境变量。