我并不特别要求使用 shell 脚本来实现此目的,但我的要求如下:
- 我可以双击桌面上的图标。(这是为了选择一个可选的,替代的启动终端窗口的方式;所以我不能只需编辑一个
.bashrc
,.profile
等等) - 这将打开一个终端窗口并运行一些命令,包括另一个脚本。(根据文档,这一个必须来自
source
Bash。) - 命令运行后,窗口保持打开状态无限期,可用于输入更多 shell 命令. (这意味着任何基于
read
等的东西xterm -keep
都是不是一个办法。) - 已运行的命令影响 shell 的状态(这就是我在这里要问问题的原因。)
具体用例是,我想要打开一个窗口,从一个特定的目录启动(在我的情况下,是桌面上的一个文件夹,其中包含我正在处理的 Python 项目的子目录),并激活一个 Python 虚拟环境(在我的情况下,是SANDBOX
位于该目录中的一个名为“scratch”的常见 venv)。
经过大量研究,我最终得出了以下结论:
#!/bin/bash
cd ~/Desktop/dev
source SANDBOX/bin/activate
exec $SHELL
这是几乎对。如果真的需要的话,我可以忍受它,这已经足够好了,但我真的被它的缺点困扰了。在我的测试中,窗口保持打开状态并且可用;密码设置正确;并且脚本正确修改了VIRTUALENV
和PATH
环境变量。但是,PS1
没有设置,因此提示符没有被修改,并且命令deactivate
没有像通常那样可用。
我怎样才能让它完全透明?
答案1
信用
Kamil 的答案对我来说很有效,但事实证明我还有一些额外的小要求,直到我看到答案我才意识到。凭借这种新的理解,我设法以完全不同的方式解决了这个问题。
讨论
就我而言,我真的不想修改自定义脚本,因为它是由 Python 生成的。(在更一般的情况下,我还希望能够在不重新加载 .bashrc 的情况下运行脚本。)我也不想编写单独的自定义 rcfile,因为它感觉不可扩展;现在我正在制作二每次我需要这种包装器时,我都会创建一个特殊的文件(并且我还希望它能用于其他一些事情)。
这些问题似乎是由于启动新 shell 造成的,即使使用exec
而不是命令。虽然它确实正确地管理了窗口的生命周期,
.bashrc 再次运行,覆盖设置
PS1
;函数定义不会被导出,所以新的 shell 根本没有它。
第一个问题实际上不能通过使用 来解决exec bash --norc
,因为我希望从两个地方修改PS1
—— 只是顺序不同。第二个问题根本没有通过这种方式解决。
使原始方法奏效的诀窍是activate
脚本必须在最后的shell 创建(无论有多少个)。该 shell 需要通过某种方法运行 .bashrc,然后运行原始脚本内容。但是...如果我们不打算修改原始脚本(首先获取 .bashrc;不打算制作单独的脚本包装器(按顺序运行两者);不打算按顺序运行bash -c
两者(这行不通;我们需要-c
按顺序安排,但这样我们就得不到持久终端)——我们该怎么办?
我的解决方案
我想到的是:反转逻辑,并.bashrc
(有条件地)获取新脚本。由于我们无法向其传递命令行参数(bash
如果没有 ,它们将转到调用-c
,而有了 ,-c
我们又得不到持久终端),我们通过设置环境变量来传递必要的信息。为此,我们需要一个外部的包装子 shell。
我在末尾添加了以下内容.bashrc
:
if [ -f "$MORE_STARTUP" ]; then
. $MORE_STARTUP
elif [ -n "$MORE_STARTUP" ]; then
echo "Warning! Could not run ${MORE_STARTUP}"
fi
现在MORE_STARTUP
,如果设置并找到环境变量,则该变量指定一个脚本,其中包含要采取的其他设置操作。它能通过普通脚本进行设置:
#!/bin/bash
cd /path/to/start/dir
export MORE_STARTUP=path/to/script
exec bash
但是,这仍然保留了 Cinnamon 关于执行文本文件的警告,并且“在终端中运行”选项对于使其工作是必需的。这仍然有点尴尬。相反,按照原来的建议,我制作了一个形状如下的启动器:
[Desktop Entry]
Exec=/bin/bash -c "export MORE_STARTUP=\"path/to/script\" && /bin/bash"
Icon=some-icon
Name=custom terminal
Path=/path/to/start/dir
Terminal=true
Type=Application
我发现 shell 扩展在选项中不起作用Path
。将 a 添加cd
到命令序列中也应该有效,但使用Path
似乎更简洁。这里不需要转义引号,但如果路径中有空格等,则需要。
现在每个原始脚本只有一个包装器,双击即可完成我想要的所有操作。我甚至开始编写一个脚本来生成此类启动器(添加一个函数被.bashrc
证明很笨重,所以我将改用 Python)。
答案2
解决方案
创建快捷方式(a.desktop
文件或其他任何可以在终端仿真器中运行命令的程序。命令应该是:
/bin/bash --rcfile /path/to/rcfile
rcfile
自定义文件在哪里来源:bash
代替~/.bashrc
*. 在文件中执行您想要的源,调用cd
或任何其他操作。您可能无论如何都想要执行源~/.bashrc
,因此请在自定义文件中明确执行。不要再生出另一个bash
。
*有些发行版之前会发布bash
源代码/etc/bash.bashrc
~/.bashrc
,然后--rcfile
将它们替换为单个自定义文件。 如果需要,调整答案的其余部分。
概念验证
这是我的例子customshell.desktop
:
[Desktop Entry]
Comment=
Exec=/bin/bash --rcfile ~/.myspecialbashrc
Icon=utilities-terminal
Name=My custom shell
Path=
StartupNotify=true
Terminal=true
TerminalOptions=
Type=Application
并且~/.myspecialbashrc
是:
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
cd ~/Desktop/dev
. SANDBOX/bin/activate
我使用一个设置 shell 变量和一些 shell 选项的简单activate
文件进行了测试。它成功了,我得到的交互式 shell 的配置与计划完全一致。
简化
最终设置源~/Desktop/dev/SANDBOX/bin/activate
。如果您不介意在activate
文件中添加其他内容,那么您不需要任何额外的 rcfile(如上所示~/.myspecialbashrc
)。桌面文件将如下所示:
[Desktop Entry]
Comment=
Exec=/bin/bash --rcfile SANDBOX/bin/activate
Icon=utilities-terminal
Name=My custom shell
Path=~/Desktop/dev
StartupNotify=true
Terminal=true
TerminalOptions=
Type=Application
NotePath=~/Desktop/dev
负责在bash
启动之前输入正确的目录,因此我们稍后不需要cd
(即在activate
文件中)。不过,如果您想要源代码,~/.bashrc
则需要从activate
文件内部获取源代码。
如果您不能或不想更改文件,activate
请坚持使用第一个解决方案(使用~/.myspecialbashrc
; notePath=~/Desktop/dev
而不是仍然是一个选项)。如果您可以通过在命令行中提供多个文件( )来按顺序cd
生成源文件,那就太好了;但在我的测试中,这不起作用,只有最后一个文件有效。bash
--rcfile
bash --rcfile … --rcfile …
--rcfile