Can you run a script every time /bin/sh is invoked? - linux

With bash, you can set your ~/.bashrc file to run something every time a new bash shell is created. Is it possible to do the same thing with /bin/sh? (This is on Debian, by the way).
For now, I just want to echo 'I am sh' when /bin/sh is invoked. It's easy to do in bash ("echo 'I am bash'" placed at the top of the file).
Thanks!

When starting a login shell of dash, which is /bin/sh on debian-like systems, it will read ~/.profile. If you also want a configuration file read for interactive non-login shells, add the following line to your ~/.profile file:
ENV=$HOME/.shinit; export ENV
Then, with the variable ENV appearing in the environment, the file $HOME/.shinit will be sourced with every new interactive (dash) shell.
You may change the file name specified by ENV to any file name you prefer.
To assure a dash login shell has added ENV to the environment, you may need to logout and log back in, or possibly reboot, depending on your system setup.
Documentation
This is documented in man dash:
A login shell first reads
commands from the files /etc/profile and .profile if they exist. If the environment variable ENV
is set on entry to an interactive shell, or is set in the .profile of a login shell, the shell
next reads commands from the file named in ENV. Therefore, a user should place commands that are
to be executed only at login time in the .profile file, and commands that are executed for every
interactive shell inside the ENV file.
Example
Suppose that we have files set up like:
$ echo "ENV=$HOME/.shinit; export ENV" >>~/.profile
$ cat .shinit
echo FOUND ME
Since I just added the ENV line to the ~/.profile file, ENV is not yet in the environment. If we run dash:
$ dash
$
Nothing happened because this is a non-login shell and ENV is not yet in the environment.
If we start a login shell, ENV is placed in the environment and ~/.shinit is run:
$ dash -l
FOUND ME
If, as a child of that shell, we run an interactive non-login shell, then ~/.shinit will be run because the parent shell created the ENV variable:
$ dash
FOUND ME
The environment created by the login shell above only affects its children. To assure that all interactive dash shells have ENV in their environment may, as mentioned above, require logging out and back in, or a reboot.

Related

My alias name is referring to old alias not a new one [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What's the difference between .bashrc, .bash_profile, and .environment?
It seems that if I use
alias ls='ls -F'
inside of .bashrc on Mac OS X, then the newly created shell will not have that alias. I need to type bash again and that alias will be in effect.
And if I log into Linux on the hosting company, the .bashrc file has a comment line that says:
For non-login shell
and the .bash_profile file has a comment that says
for login shell
So where should aliases be written in? How come we separate the login shell and non-login shell?
Some webpage say use .bash_aliases, but it doesn't work on Mac OS X, it seems.
The reason you separate the login and non-login shell is because the .bashrc file is reloaded every time you start a new copy of Bash. The .profile file is loaded only when you either log in or use the appropriate flag to tell Bash to act as a login shell.
Personally,
I put my PATH setup into a .profile file (because I sometimes use other shells);
I put my Bash aliases and functions into my .bashrc file;
I put this
#!/bin/bash
#
# CRM .bash_profile Time-stamp: "2008-12-07 19:42"
#
# echo "Loading ${HOME}/.bash_profile"
source ~/.profile # get my PATH setup
source ~/.bashrc # get my Bash aliases
in my .bash_profile file.
Oh, and the reason you need to type bash again to get the new alias is that Bash loads your .bashrc file when it starts but it doesn't reload it unless you tell it to. You can reload the .bashrc file (and not need a second shell) by typing
source ~/.bashrc
which loads the .bashrc file as if you had typed the commands directly to Bash.
Check out http://mywiki.wooledge.org/DotFiles for an excellent resource on the topic aside from man bash.
Summary:
You only log in once, and that's when ~/.bash_profile or ~/.profile is read and executed. Since everything you run from your login shell inherits the login shell's environment, you should put all your environment variables in there. Like LESS, PATH, MANPATH, LC_*, ... For an example, see: My .profile
Once you log in, you can run several more shells. Imagine logging in, running X, and in X starting a few terminals with bash shells. That means your login shell started X, which inherited your login shell's environment variables, which started your terminals, which started your non-login bash shells. Your environment variables were passed along in the whole chain, so your non-login shells don't need to load them anymore. Non-login shells only execute ~/.bashrc, not /.profile or ~/.bash_profile, for this exact reason, so in there define everything that only applies to bash. That's functions, aliases, bash-only variables like HISTSIZE (this is not an environment variable, don't export it!), shell options with set and shopt, etc. For an example, see: My .bashrc
Now, as part of UNIX peculiarity, a login-shell does NOT execute ~/.bashrc but only ~/.profile or ~/.bash_profile, so you should source that one manually from the latter. You'll see me do that in my ~/.profile too: source ~/.bashrc.
From the bash manpage:
When bash is invoked as an
interactive login shell, or as a
non-interactive shell with the
--login option, it first reads and executes commands from the file
/etc/profile, if that file exists.
After reading that file, it looks for
~/.bash_profile, ~/.bash_login, and
~/.profile, in that order, and reads
and executes commands from the first
one that exists and is readable. The
--noprofile option may be used when the shell is started to inhibit this
behavior.
When a login shell exits, bash
reads and executes commands from the
file ~/.bash_logout, if it exists.
When an interactive shell that is not a login shell is started, bash
reads and executes commands from ~/.bashrc, if that file exists. This
may be inhibited by using the --norc option. The --rcfile file option
will force bash to read and execute commands from file instead of
~/.bashrc.
Thus, if you want to get the same behavior for both login shells and interactive non-login shells, you should put all of your commands in either .bashrc or .bash_profile, and then have the other file source the first one.
.bash_profile is loaded for a "login shell". I am not sure what that would be on OS X, but on Linux that is either X11 or a virtual terminal.
.bashrc is loaded every time you run Bash. That is where you should put stuff you want loaded whenever you open a new Terminal.app window.
I personally put everything in .bashrc so that I don't have to restart the application for changes to take effect.

What init files are sourced in ash for non-interactive, non-login shells?

I have a Docker container, which uses alpine:3.7 as base image, and as a result, uses /bin/ash as its shell.
I put the container into background running mode. My intend is that I can continually use docker exec $CONTAINER_ID <command> at it. Thus, the command would be executed in non-interactive, non-login shell mode.
But, sometimes my <command> is in a non-standard path, therefore I would love to export PATH so I don't type fully qualified paths. Or, sometimes some software installation requires me to put some commands in our shell init files (e.g. eval "$(pyenv init -)").
The problem is: under /bin/ash shell, for a non-interactive, non-login shell command execution, where can I export this new PATH, or my eval "$(pyenv init -)", so that I can do my docker exec with everything loaded/sourced appropriately?
I would also consider Bash if Ash cannot do it.
TIA
The file named by the environment variable ENV is sourced when a POSIX-compliant shell (or bash in POSIX mode) is started. If you want to support bash when in non-POSIX mode as well, you'll also want to set BASH_ENV.
This means that you can tell Docker to update the environment variable ENV to point to a file which, when sourced by a shell interpreter, will run eval "$(pyenv init -)".

Is there an alternative for .bashrc for /bin/sh?

I need a script which is run on startup of /bin/sh, similar to .bashrc for /bin/bash. Is there any way to do this?
EDIT:
I tried both /etc/profile and ~/.profile, I wrote echo 'hello world' to both files. None of these are working. When I type sh into the console, nothing pops up.
I am using ArchLinux.
In Arch, /bin/sh is a symlink to /bin/bash, which has quite a few rules about startup scripts, with special cases when called sh :
If bash is invoked with the name sh, it tries to mimic the startup
behavior of historical versions of sh as closely as possible, ...
If you start it from the console, without any command, i.e. as an interactive, non-login shell, you should use the ENV variable :
export ENV=~/.profile
sh
or
ENV=~/.profile sh
When invoked as an interactive [non login] shell with the name sh, bash looks for the variable ENV, expands its value if it is defined, and uses the expanded value as the name of a file to read and execute.
Alternatively you can use the --login option to make it behave like a login shell, and read the .profile file.
sh --login
When invoked as an interactive login shell [with the name sh], or a
non-interactive shell with the --login option, it first attempts to
read and execute commands from /etc/profile and ~/.profile, in that
order
I will assume, as is true on debian-ubuntu-like systems, that your /bin/sh is dash.
For dash, set the environment variable ENV to have the path to the initialization file of your choice. This would be dash's analog to ~/.bashrc.
If you are interested in login shells, dash reads ~/.profile.
As an example, one could add to ~/.profile:
ENV=$HOME/.shinit; export ENV
This would cause dash to read ~/.shinit when an interactive shell starts.
Edit
"I tried both /etc/profile and ~/.profile, I wrote echo 'hello world'
to both files. None of these are working. When I type sh into the
console, nothing pops up."
Those files are only read for login shells. If you just run sh at the command prompt, you are starting an interactive shell.
Documentation
From man dash:
Invocation If no args are present and if the standard input of
the shell is connected to a terminal (or if the -i flag is set), and
the -c option is not present, the shell is considered an interactive
shell. An interactive shell generally prompts before each command and
handles programming and command errors
differently (as described below). When first starting, the shell inspects argument 0, and if it begins with a dash ‘-’, the shell is
also considered a
login shell. This is normally done automatically by the system when the user first logs in. A login shell first reads commands from
the files /etc/profile and .profile if they exist. If the
environment variable ENV is set on entry to an interactive shell, or
is set in the .profile of a login shell, the shell next reads
commands from the file named in ENV. Therefore, a user should place
commands that are to be executed only at login time in the .profile
file, and commands that are executed for every interactive shell
inside the ENV file. To set the ENV variable to some file, place the
following line in your .profile of your home directory
ENV=$HOME/.shinit; export ENV
substituting for “.shinit” any filename you wish.
If command line arguments besides the options have been specified,
then the shell treats the first argument as the name of a file from
which to read commands (a shell script), and the remaining arguments
are set as the positional parameters of the shell ($1, $2, etc).
Otherwise, the shell reads commands from its standard input. [Emphasis added.]
POSIX
From the POSIX standard (hat tip: chepner):
ENV This variable, when and only when an interactive shell is
invoked, shall be subjected to parameter expansion (see Parameter
Expansion) by the shell, and the resulting value shall be used as a
pathname of a file containing shell commands to execute in the current
environment. The file need not be executable. If the expanded value of
ENV is not an absolute pathname, the results are unspecified. ENV
shall be ignored if the real and effective user IDs or real and
effective group IDs of the process are different.

Why do exported variables but not aliases from .bash_profile work in non-login shells?

I'm using the Fedora 20 graphical desktop. I found the alias put in the .bash_profile didn't have effect. Then I find the graphical terminal is not a login shell, so the bash_profile is not read at all.
Now it's weird to me that the export command does have effect in .bash_profile.
My .bash_profile is as below:
#bash_profile
export mytest=bash_profileIsRead
alias kk=ls
Test result:
$ shopt login_shell
login_shell off
$ echo $mytest
bash_profileIsRead
$ kk
bash: kk: command not found...
There's nothing unusual or surprising about this.
Your .bash_profile is run once per session, by your login shell. It is not run by other shells run later in your session.
.bashrc, by contrast, is run by every interactive shell instance, so things like aliases and shell functions placed there will be honored throughout the session.
Environment variables only need to be set once, because they're inherited by subprocesses (every subprocess, not just shells!). Aliases are not inherited, so they need to be set in every shell.
See the DotFiles page on the wooledge.org wiki (maintained by irc.freenode.org's #bash channel) for more.
Aliases are not inherited like environment variables. They should not be placed in profile, but instead in the .bashrc file.
Basically, .profile (or .bash_profile) is for things that are inherited (e.g. env variables) and the rc file is for things that must be re-initialized in non-login shells, such as aliases.

Setting Enviroment Variables Dynamically on Linux

I am currently looking for a way to set enviroment variables in Linux via a simple shell script. Within the script I am currently using the 'export' command, however this only has scope within the script where system-wide scope is needed.
Is there anyway I can do this via a shell script, or will another method need to be used?
When you run a shell script, it executes in a sub-shell. What you need is to execute it in the context of the current shell, by sourcing it with:
source myshell.sh
or:
. myshell.sh
The latter is my preferred approach since I'm inherently lazy.
If you're talking about system-wide scope inasmuch as you want to affect everybody, you'll need to put your commands in a place where they're sourced at login time (or shell creation time), /etc/profile for example. Where you put your commands depends on the shell being used.
You can find out what scripts get executed by examining the man page for your shell:
man bash
The bash shell, when invoked as a login shell (including as a non-login shell but with the --login parameter), will use /etc/profile and the first of ~/.bash_profile, ~/.bash_login or ~/.profile.
Non-login bash shells will use. unless invoked with --norc or --rcfile <filename>, the files /etc/bash.bashrc and ~/.bashrc.
I'm pretty certain it's even more convoluted than that depending on how the shell is run, but that's as far as my memory stretches. The man page should detail it all.
You could have your script check for the existence of something like /var/myprog/env-vars-to-load and 'source' it then unlink it if it exists, perhaps using trap and a signal. Its hard to say, I'm not familiar with your program.
There is no way to 'inject' environmental variables into another process' address space, so you'll have to find some method of IPC which will can instruct the process on what to set.
A fundamental aspect of environment variables is that you cannot affect the environment for any process but your own and child processes that you spawn. You can't create a script that sets "system wide" environment variables that somehow become usable by other processes.
On the shell prompt:
$ source script.sh
And set the env vars in script.sh
test.sh
#!/bin/bash
echo "export MY_VAR=STACK_OVERFLOW" >> $HOME/.bashrc
. $HOME/.bashrc
sh task.sh
task.sh
#!/bin/sh
echo $MY_VAR
Add executable rights:
chmod +x test.sh task.sh
And lauch test.sh
./test.sh
Result:
STACK_OVERFLOW

Resources