emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How to get shell source blocks to read my profile?


From: Tim Cross
Subject: Re: How to get shell source blocks to read my profile?
Date: Wed, 17 Mar 2021 07:32:52 +1100
User-agent: mu4e 1.5.10; emacs 27.1.91

George Mauer <gmauer@gmail.com> writes:

> I do still wonder what would be the disadvantage of just configuring it to do 
> --login by default and doing all configuration in profile scripts. It would be
> unconventional yes, but it would also make dynamic scoping of environment 
> variable effectively opt-in via --noprofile rather than opt-out (which imo is
> how it should be). I would assume that uses extra resources or risks 
> improperly handling crashed processes, but I can't find anything to that 
> effect in the
> docs

The following is long, but may provide some of the context you are
looking for to help understand all the moving parts involved here. Some
of ti is not 100% accurate, but trying to be 100% accurate would make it
longer and would not necessarily help with the mental model needed to
understand how this all fits together. Hopefully, this has a reasonable
balance between accuracy and simplicity!

While there is certainly aspects of shell architecture which are less
relevant in a modern computing environment, the way it all works is
actually pretty good. It can sometimes help to remember what early Unix
environments were like and what the resource constraints were to
understand some of the design decisions. A time when memory and disk
storage was extremely expensive and in short supply, where CPUs were
slower and less capable than those commonly found in a modern washing
machine. This was a time where every CPU cycle, every memory bit and
every byte of storage had to be justified and used efficiently.

The 'login' shell actually has a special purpose and a few subtleties
associated with it that may not be obvious at first. For example, the
login shell usually records a user's login in a few system files like
wtmp and utmp. The idea is that the login shell will be the 'root' of a
user's process tree. One of its key roles is to setup the environment
which will be inherited by all the other (child) processes which the
user initiates. This includes some things which you only want run once
and processes which must run as parents to all other processes. A good
example is setting up an ssh agent and adding ssh keys to that agent.
You only want this to occur once, so you might add this to your .profile
(or whatever the shell equivalent is, such as .bash_profile or
.zsh_profile). More critical is the requirement that the ssh agent must
be run inside a parent process for child processes to be able to use it.
This means it needs to be near the 'root' login process to ensure all
children are able to take advantage of the service it provides.
(Keep in mind that some platforms, like the mac, have created a whole
new architecture for managing keys etc and even on Linux, it is rarely
necessary to run ssh-agent like we did in the 'old days' as that
functionality has been subsumed into desktop environments like gnome)

It is also here that you will define static environment variables i.e.
those which will not change during the lifetime of the session and which
are exported, meaning they are made available in the environments of
child processes. Examples are setting up things like PATH, EMAIL etc.
Some common environment variables, like USER, HOME etc are done
automatically as part of the system wide profile in /etc/profile or the
system wide path defined in /etc/paths and /etc/paths.d etc.

In addition to these static environment variables are more dynamic ones
which change and therefore need to be updated with each new shell
process. This can include variables like PWD and CWD which track the
parent and current working directory or the PS1 and PS2 variables which
contain details about your shell prompt that may include dynamic
information such as the current working directory, current date/time or
status of the git repository associated with the current working
directory. This is essentially the role fulfilled by the 'rc' files,
like .bashrc. As these dynamic enironment variables tend to only be
relevant for interactive shells, the rc file is only 'sourced' for those
shells.

When you execute a command, it creates a new process which has an
execution environment associated with it (there are some exceptions,
such as built-in shell commands that run inside the current process and
command 'modifiers' like 'exec' which replace the current process with
the command being executed). Some of these processes will be very short
lived, executing a single command or possibly run for hours, days, weeks
(such as Emacs). This process is a child process of the process which
ran/executed the command. You can use the -f (forest) switch to ps to
see the relationship between processes. Those processes in turn may
execute more child processes (such as when Emacs creates a shell process
to perform some task, like run a compilation task, spawn a shell or
terminal, etc. The environment each of these processes inherits consists
of the exported variables defined in the parent process. This is
typically all the exported variables from the login process (as it is
the parent of all) and any variables exported by parent processes in the
process hierarchy (such as any exported as part of an interactive shell
which sourced .bashrc for example) or is defined and exported in a
parent (such as part of a shell script which then executes the command
which creates the child process.

So, in general, you don't want to run all or many of your processes as a
login shell. At the very least it is wasting resources and at the worst
it could actually cause confusion or break things because it is causing
things to be run multiple times which should only be run once.

I suspect some of the confusion here is being cause because of the
different model approach used by the Mac OS. On the mac, you actually
have at least two process trees (I'm not a mac expert, so it is possible
there are more than two - in fact I'm pretty sure there are, but for
explanation purposes....). On the mac you have your login process tree
and your 'dock' (launcher) process tree. The dock process tree is not
rooted as the child of a login shell, so there is no sourcing of
.profile and it is not an interactive shell, so no .bashrc either. The
execution environment associated with processes it spawns (such as when
you run a command) only consist of the global system environment setup
by the system prfile in /etc/profile, /etc/paths and /etc/paths.d and
any environment variables created and exported by the process itself
(i.e. using the setenv command in your emacs init.el). Whether emacs is
run in terminal mode or in GUI mode has nothing to do with what
environment context it has. The context is determined by the environment
context of the process parent.

So a common way to ensure your .profile environment variables are
available in Emacs when it is run from the dock is to use a shell
wrapper script which first sources .profile to create and export the
variables in .pofile and then run emacs. Often you won't just make it a
login shell, but simply source .profile. It is this script which is
added to the dock and is executed to start Emacs, ensuring it has your
.profile variables set and exported to the child process, Emacs. In
turn, any child processes created by Emacs will also include this
environment context (such as wehn you open an emacs terminal, run M-x
shell or execute org source blocks). 

The other approach is to just add the variables you need using 'setenv'
i your emacs init.el file. Often, there are only a few additional
variables you want inside of Emacs and child processes it creates, so
adding them to your init.el file is not hard. The one which is most
often needed is updates to the PATH variable to add additional
directories to search for executable files. You can use setenv to do
this or you can use the exec-path package or you can do what I do, which
is add these additional paths to /etc/paths.d. I don't bother with the
wrapper script on the Mac. There are only a couple of additional
variables I need defined, so I set them in my init.el using setenv. 

-- 
Tim Cross



reply via email to

[Prev in Thread] Current Thread [Next in Thread]