你好,
我试图在运行 fab 的 run 命令时加载 .bashrc 文件(即 source /home/ubuntu/.bashrc)以添加一些环境变量并扩展路径变量:
run('source /home/ubuntu/.bashrc && echo $PATH')
这仅显示我:
[[email protected]] 出:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin: /bin:/usr/games:/usr/local/games
而不是我手动登录远程服务器时看到的更长的路径列表。
如何让 fab 在我的远程主目录中正确导入 .bashrc 文件?
谢谢!
这是一个 bash 的东西,而不是一个 fab 的东西。
bash 决定在启动时获取哪些文件可能很复杂http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html和 debian/ubuntu(和大多数发行版)在/etc/profile
有一些自定义~/.bashrc
。
fab 使用的默认 shell 是/bin/bash -l -c
,而-l
使其成为“登录”shell。 如果没有 debian/ubuntu 自定义,bash“登录”shell 有可能获取~/.bash_profile
而不是~/.bashrc
。
但是在 ubuntu 16.04 上,即使对于 login-shell,它似乎也默认使用.bashrc
来源。 但是添加在默认.bashrc
底部的行不会被处理,因为如果它检测到非交互式运行,它会在顶部附近退出。
这里我在 ubuntu-16.04 默认用户.bashrc
添加了两行
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
export VAR1=val1
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
export VAR2=val2
# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
...
这是我正在测试的 fabfile:
from fabric.api import run, env, task
<strong i="23">@task</strong>
def get_myvars():
run("echo VAR1=$VAR1 VAR2=$VAR2")
结果:
$ fab -H testpy05.ec2.st-av.net get_myvars
[testpy05.ec2.st-av.net] Executing task 'get_myvars'
[testpy05.ec2.st-av.net] run: echo VAR1=$VAR1 VAR2=$VAR2
[testpy05.ec2.st-av.net] out: VAR1=val1 VAR2=
[testpy05.ec2.st-av.net] out:
...
乍一看听起来合法,谢谢@ploxiln!
谢谢你的详细解答!
这似乎与标准 ssh 行为不同,并且很难调试。 当我手动 ssh 一切正常时,由于未在 OSX 中运行 .bash_profile,结构具有不同的 PATH 值。
@bitprophet这可能是 OSX 错误??
@code-tree 您使用的是 Fabric 1 还是 Fabric 2? 1.x 使用显式 shell 包装器,这就是它与标准 OpenSSH 客户端不同的原因; 版本 2 不做任何包装,就 sshd 代表您运行的内容而言,它的行为应该更像 OpenSSH 客户端。
版本 1 有一些 env 配置值选项用于更改该 shell 包装器,因此您可能想尝试向其中添加标志,例如-i
,其中 IIRC 是“运行交互模式并获取一些额外文件”的 bash。
这很奇怪,因为我使用的是2.2.1
。 我目前必须执行以下操作才能使我的应用程序正常工作:
c.run('bash -l -c "python3 ./configure.py"')
当我安装 Python 3 时,它通过我的.bash_profile
将自己添加到 PATH 中,它不是由 Fabric 运行的。 我也没有对标准的内置 sshd 配置进行任何修改。
那很奇怪,我必须看看我是否可以复制。 只是做了一个快速的理智跟踪来证明我的意思:Fabric 2 没有做任何“特殊”的事情:sshd 驱动的执行:
command
command
ssh myserver <command>
完全相同,大概@code-tree 也是,如果可以的话,请发布一些关于您正在运行的内容和其他环境细节(客户端和服务器操作系统/版本等)的更多详细信息。
当然,我正在运行旧的 OSX 版本作为服务器。 如果您可以确认在较新的 MacOS 上一切正常,那么好吧。 但如果不是,那么可能是 MacOS 作为服务器的一般问题?
旁注,我想知道这是否真的是误诊案例:
由于未在 OSX 中运行 .bash_profile,fabric 具有不同的 PATH 值
这可能是 #1744 的一个例子,其中 Fabric 2 与ssh
在将本地环境变量分流到管道方面不同吗? (取决于所讨论的确切 env vars 以及它们在本地和远程是否相似;@code-tree 可以通过仔细检查游戏中的确切值来轻松反驳这一理论:他们的本地和远程 bash_profile ... 😁)
但无论如何我都会直接解决这个问题,只是为了让我知道我不是疯了:我对 sshd 在 shell 和源文件方面所做的事情的理解。
另外我想知道这是否与#1816的场景/智能有关,这也是关于shell和启动文件的。 它_可能_不直接相关,因为这里关于ssh
行为一种方式而 Fabric 行为另一种方式的断言(而不是“sshd 正常调用 shell 的方式”),但无论如何都值得链接。
发现试图偶然发现一堆我记得在过去几个月里做过的深度 sshd 代码潜水,与这些方面的东西有关。 我现在找不到它,虽然这很烦人。 编辑:啊,我认为这是一个无关的 Paramiko 问题(不链接,因为没有点混淆)关于在身份验证之前执行命令。 好的。
好的,那么当我们请求命令 exec 时到底发生了什么?
shell
通道请求(加载用户定义的登录 shell 程序并且不接受任何command
)或exec
通道请求(它只是说“执行给定的命令字符串,其中可能包含一个路径”并且没有指定更多)775f8a23f2353f5869003c57a213d14b28e0736e
)好吧,首先我会闲逛一下。 针对任意 Debian 8.10 容器(运行 OpenSSH 6.7!)运行,我在执行ssh localhost whoami
时在 DEBUG3 级日志中看到了这一点:
debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec
Starting session: command for root from 172.17.0.1 port 42598
做fab -H localhost -- whoami
:
debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec
Starting session: command for root from 172.17.0.1 port 42610
好的,他们都在做exec
......这是可以预料的。 进程树有什么不同吗? 不应该,而且……没有。 两者看起来像这样:
sshd,1 -D -e
└─sshd,1535
└─pstree,1537 -alpU
那么,绝对没有外壳在起作用? 它到底在做什么? 我可以使用像&&
这样的外壳吗?
我可以! 例如whoami && id
工作正常。 考虑到我对 Unix 进程控制的理解,这有点奇怪; sshd 似乎不太可能只是在执行exec(string)
,但是如果它使用 shell 进行解释,我们不会在pstree
看到吗?
我认为是时候真正深入研究 openssh-portable 并看看是什么了。
session_input_channel_req
日志行发生的地方,在(你猜对了) session_input_channel_req ,它记录然后根据会话类型(shell、pty open、exec、subsystem 等)分派到各种函数exec
,这意味着session_exec_reqdo_exec_pty
或(在我们的例子中,因为我们没有请求 pty;你可以知道,因为日志行没有关于 tty 编号的一点信息) do_exec_no_pty另外,嗯,我 _did_ 过去做了一部分跟踪(但出于不同的原因/重点),如 2016 年 12 月: https :
我没有在pstree
看到 shell 的原因是因为使用了execve
,它替换了父进程而不是生成子进程。 很高兴知道。 (编辑:错误,这只是用外壳替换了 sshd 子进程;外壳本身也可能正在执行替换我风格的 exec 调用;请参阅下面的评论。)
无论如何,所以! 这仍然意味着 Fabric 和 OpenSSH 的客户端在 shell 执行方面做着完全相同的事情; 他们中的任何一个都不会修改 OpenSSH 代码库的这些部分。 它应该始终类似于bash -c "python3 ./configure.py"
,实际值bash
取决于@code-tree 的 macOS 级别用户及其配置的 shell。
让我想知道我关于 env var 传输作为差异化因素的预感是否准确,因为这是我唯一能想到的另一件事:两个执行环境如何不同。 FWIW 在我以 CLI 为重点的测试中, env
在两个系统上报告相同,并且不管其他与环境相关的问题,_default_ 行为(由于 SSH 安全策略)应该始终是没有本地环境“泄漏”到另一边。
是的,我很确定 sshd 总是在用户的 shell 中运行命令字符串(在 /etc/passwd 中配置)。 但是,如果它可以解析命令字符串并确定它是单个命令,则它会在命令字符串前面加上“exec”。
$ ssh testdeploy02.ec2.st-av.net pstree -a
...
|-sshd -D
| `-sshd
| `-sshd
| `-pstree -a
$ ssh testdeploy02.ec2.st-av.net 'pstree -a && sleep 1'
...
|-sshd -D
| `-sshd
| `-sshd
| `-bash -c pstree -a && sleep 1
| `-pstree -a
$ ssh testdeploy02.ec2.st-av.net 'VAR=-a sh -c "exec pstree \$VAR"'
...
|-sshd -D
| `-sshd
| `-sshd
| `-pstree -a
至于环境变量,这些主要由 ssh 客户端和 sshd 服务器配置控制:
$ grep Env /etc/ssh/*_config
/etc/ssh/ssh_config: SendEnv LANG LC_*
/etc/ssh/sshd_config:AcceptEnv LANG LC_*
因此 ssh 客户端确实明确发送了一些变量(在命令字符串本身之外),并且它们由 sshd 过滤。 但显然有一些例外:
$ ssh testdeploy02.ec2.st-av.net env | grep TERM
$ ssh -t testdeploy02.ec2.st-av.net env | grep TERM
TERM=xterm-256color
实际上,我认为“自动执行以单个命令替换 shell”行为是 bash 的一个特性:
$ dash -c "pstree -a"
...
├─sshd -D
│ └─sshd
│ └─sshd
│ └─bash
│ └─dash -c pstree -a
│ └─pstree -a
$ bash -c "pstree -a"
├─sshd -D
│ └─sshd
│ └─sshd
│ └─bash
│ └─pstree -a
编辑:为了完整性:
$ bash -c "pstree -a && sleep 1"
├─sshd -D
│ └─sshd
│ └─sshd
│ └─bash
│ └─bash -c pstree -a && sleep 1
│ └─pstree -a
啊,是的,我错了, execve
只是意味着sshd 子 proc是被替换的,所以之后会发生什么取决于 shell 本身以及它对-c xxx
。 我的测试是在 zsh 中,fwiw。
另外@ploxiln是的,带有 env vars 的东西正在引用上面链接的其他票证,尽管@code-tree 是否真的遇到了与这些票证不同的症状还有待观察。
抱歉,我认为这是误报。 我不知道通过 ssh 内联执行命令和通过 ssh 登录后执行之间有什么区别。 当我执行ssh host 'echo $PATH'
它也没有更新 PATH,因为echo $PATH
登录后工作正常。
我仍然不明白为什么 ssh 这样做(似乎我确实必须这样做ssh host 'bash -l -c "python3 ./configure.py"'
)。 无论如何,可以肯定地说这毕竟不是 Fabric 问题。 对困惑感到抱歉。
@code-tree 它与前面提到的内容有关 - shell 有几种不同的操作模式,通常称为“登录”和“交互”(通常这些模式位于基本模式之上,两者都被认为是)和哪种模式shell 正在运行,更改它加载的 rc 文件集。
这里的重点是bash -c xxx
不被视为“交互式”,因此会跳过加载某些文件 - 例如.bash_profile
- 而只是运行bash
而没有-c
通常是交互式的,并且会加载配置文件。
正如您在上面看到的 - 当您要求 sshd 运行单个命令(如 Fabric 或ssh host command
那样)时,它总是运行bash -c <command send down the pipe>
,因此,它始终处于非交互模式。 (除非你做 Fabric 1 所做的事情并使用-l
运行你自己的、嵌套的 shell ......但是你正在运行bash -c "bash -l -c \"oh god escaping is hard help\""
并且我现在要去吃一些🥃。
无论如何,并不是所有的都丢失了,我需要每隔几年刷新我对这部分事情的记忆,所以呃......现在我精神焕发了😆
这是一个 bash 的东西,而不是一个 fab 的东西。
bash 决定在启动时获取哪些文件可能很复杂http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html和 debian/ubuntu(和大多数发行版)在
/etc/profile
有一些自定义~/.bashrc
。fab 使用的默认 shell 是
/bin/bash -l -c
,而-l
使其成为“登录”shell。 如果没有 debian/ubuntu 自定义,bash“登录”shell 有可能获取~/.bash_profile
而不是~/.bashrc
。但是在 ubuntu 16.04 上,即使对于 login-shell,它似乎也默认使用
.bashrc
来源。 但是添加在默认.bashrc
底部的行不会被处理,因为如果它检测到非交互式运行,它会在顶部附近退出。这里我在 ubuntu-16.04 默认用户
.bashrc
添加了两行# ~/.bashrc: executed by bash(1) for non-login shells. # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) # for examples export VAR1=val1 # If not running interactively, don't do anything case $- in *i*) ;; *) return;; esac export VAR2=val2 # don't put duplicate lines or lines starting with space in the history. # See bash(1) for more options HISTCONTROL=ignoreboth ...
这是我正在测试的 fabfile:
from fabric.api import run, env, task <strong i="24">@task</strong> def get_myvars(): run("echo VAR1=$VAR1 VAR2=$VAR2")
结果:
$ fab -H testpy05.ec2.st-av.net get_myvars [testpy05.ec2.st-av.net] Executing task 'get_myvars' [testpy05.ec2.st-av.net] run: echo VAR1=$VAR1 VAR2=$VAR2 [testpy05.ec2.st-av.net] out: VAR1=val1 VAR2= [testpy05.ec2.st-av.net] out: ...
你救了我的命。 这是我找到的完美答案:) 非常感谢!
最有用的评论
这是一个 bash 的东西,而不是一个 fab 的东西。
bash 决定在启动时获取哪些文件可能很复杂http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html和 debian/ubuntu(和大多数发行版)在
/etc/profile
有一些自定义~/.bashrc
。fab 使用的默认 shell 是
/bin/bash -l -c
,而-l
使其成为“登录”shell。 如果没有 debian/ubuntu 自定义,bash“登录”shell 有可能获取~/.bash_profile
而不是~/.bashrc
。但是在 ubuntu 16.04 上,即使对于 login-shell,它似乎也默认使用
.bashrc
来源。 但是添加在默认.bashrc
底部的行不会被处理,因为如果它检测到非交互式运行,它会在顶部附近退出。这里我在 ubuntu-16.04 默认用户
.bashrc
添加了两行这是我正在测试的 fabfile:
结果: