Commands available in bash -i can't access in bash -l - linux

I don't know how to describe it. It just happens when i use vim and set shell=bash -l. Then i found that a command called mm which can execute in terminal can't execute in vim .
And i also found that when i write this command in run.sh and execute this script. It still report command not found. I think there must be something wrong with my $HOME/.bash* files and $HOME/.profile. And i am sure that .profile are almost the same with .bashrc.

From $ man bash:
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.
and
When an interactive shell that is not a login shell is started, bash
reads and executes commands from ~/.bashrc, if that file exists.
So:
shell | files loaded
--------+-----------------
bash -l | /etc/profile
| ~/.bash_profile
| ~/.bash_login
| ~/.profile
--------+-----------------
bash -i | ~/.bashrc

Related

shopt -s extdebug in .bashrc not working in script files

I am writing a a bash script (echoo.sh) with the intention of echoing the command before it is executed. I source this script (echoo.sh) inside .bashrc. But it does not execute for commands run in script file(tmp.sh) with the bash shebang. Below is the code I have so far
echoo.sh
#!/usr/bin/env bash
shopt -s extdebug; get_hacked () {
[ -n "$COMP_LINE" ] && return # not needed for completion
[ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return # not needed for prompt
local this_command=$BASH_COMMAND;
echo $this_command;
};
trap 'get_hacked' DEBUG
When I open a shell and run any command - It works. But for stuff in a script file it doesn't work.
SOME FURTHER TRIES:
I tried sourcing the .bashrc file within the script file (tmp.sh) - didn't work.
I sourced echoo.sh inside tmp.sh and it worked.
SO, I am trying to understand
Why doesn't it work if I just source my script in .bashrc for stuff that runs in scripts?
Why doesn't further try #1 work when #2 does.
AND finally what can I do such that I don't have to source echoo.sh in all script files for this to work. Can source my script in one place and change some setting such that it works in all scenarios.
I source this script (echoo.sh) inside .bashrc. But it does not execute for commands run in script file(tmp.sh) with the bash shebang
Yes it won't because you are invoking the shell non-interactively!
The shell can be spawned either interactively or non-interactively. When bash is invoked as an interactive login shell 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.
When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists.
When you run a shell script with an interpreter set, it opens a new sub-shell that is non-interactive and does not have the option -i set in the shell options.
Looking into ~/.bashrc closely you will find a line saying
# If not running interactively, don't do anything
[[ "$-" != *i* ]] && return
which means in the script you are calling, e.g. consider the case below which am spawning a non-interactive shell explicitly using the -c option and -x is just to enable debug mode
bash -cx 'source ~/.bashrc'
+ source /home/foobaruser/.bashrc
++ [[ hxBc != *i* ]]
++ return
which means the rest of the the ~/.bashrc was not executed because of this guard. But there is one such option to use here to read a start-up file for such non-interactive cases, as defined by BASH_ENV environment variable. The behavior is as if this line is executed
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
You can define a file and pass its value to the local environment variable
echo 'var=10' > non_interactive_startup_file
BASH_ENV=non_interactive_startup_file bash -x script.sh
Or altogether run your shell script as if an interactive non login shell is spawned. Run the script with an -i flag. Re-using the above example, with the -i flag passed now the ~/.bashrc file will be sourced.
bash -icx 'source ~/.bashrc'
You could also set this option when setting your interpreter she-bang in bash to #!/bin/bash -i
So to answer your questions from the above inferences,
Why doesn't it work if I just source my script in .bashrc for stuff that runs in scripts?
It won't because ~/.bashrc cannot be sourced from a shell that is launched non-interactively. By-pass it by passing -i to the script i.e. bash -i <script>
Why doesn't further try #1 work when #2 does.
Because you are depending on reading up the ~/.bashrc at all here. When you did source the echoo.sh inside tmp.sh, all its shell configurations are reflected in the shell launched by tmp.sh

Can make shell run interactively along with --command option

I'm using GNU bash that is installed as git bash. On startup I need to change directory, so I'm doing it like this:
"C:\Program Files\Git\bin\sh.exe" --rcfile "./cd.sh"
Where cd.sh just contains cd /d/ command. Everything works fine here. Now I'm trying to get rid of cd.sh file and pass command to the shell as a parameter yet I want it to remain interactive, so I'm doing like this:
"C:\Program Files\Git\bin\sh.exe" -ic "cd /d"
It executes the command (tested with echo command) but then exits. Why doesn't it stay interactive?
From man bash:
An interactive shell is one started without non-option arguments and without the -c option ...
From man dash:
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.

How to make a command string executing bash ("bash -c") to read the .bashrc

When I call a bash shell from another program to execute an command string I call
bash -c <shell commands>
This works pretty well, but the shell which gets invoked does not read the startup file .bashrc. I need this for environment variables and aliases set in the start-up file.
I tried
bash -i -c <shell commands>
and
bash -l -c <shell commands>
to read .bashrc or .bash_profile but didn't have any luck. How can I enforce reading of a start-up file?
Note: I tried this only on Cygwin bash 4.1.0, but I assume this is a machine-independent issue.
As the comments mentioned,
bash -i -c
does indeed source the start-up file.
I did something wrong in Emacs, my calling app; that's why it didn't work for me.
Apologies for the unnecessary question.

Strange behavior of .bashrc

I changed .bashrc file on my web server a little bit, to color links on ls -la and so on. But when I log in using ssh: ssh user#server and type ls -al nothing is coloring, seems like my .bashrc file has not been applied on login. When if I just type bash and then again ls -la - all works fine. In short, all my rules in .bashrc only apllied when I type bash just after authorization, a little boring.
When you log in via ssh, you invoke a login shell. When you type bash in an existing shell, you invoke an interactive shell.
.bash_profile is read when a login shell is invoked, and .bashrc is read when an interactive shell is invoked.
Try adding this to your .bash_profile:
if [ -f ~/.bashrc ]; then
source ~/.bashrc
fi
See bash(1) for more details.
~/.bashrc is only read if the shell is interactive and not a login shell:
When an interactive shell that is not a login shell is started, bash reads and executes commands from
/etc/bash.bashrc and ~/.bashrc, if these files exist.
Furthermore:
Bash attempts to determine when it is being run with [...] sshd. If bash determines it is being run in this fashion, it reads and executes commands from ~/.bashrc and ~/.bashrc, if
these files exist and are readable. It will not do this if invoked as sh.
So:
your remote shell must be bash, not sh,
it must not be a login shell, and
it must be an interactive shell.

How to start a shell without any user configuration?

I need to use a "clean" shell (e.g. bash) under Linux/OSX terminal without any user configuration, but it reads config info from some files (e.g ~/.bashrc) every time it starts. I can modify the file every time I need a "clean" shell, and revert it back when I finished, but is there any easier ways to do this, for example a command?
Running bash --noprofile --norc still inherited from parent process. Based on a similar question I found that the way I interpreted this question env -i bash --norc --noprofile was what I would want.
You can pass the --noprofile and --norc command-line options:
$ bash --noprofile --norc
You will find documentation about these options in the man page.
Use --noprofile --norc:
--noprofile
Do not read either the system-wide startup file /etc/profile or any of the personal initializa‐
tion files ~/.bash_profile, ~/.bash_login, or ~/.profile. By default, bash reads these files
when it is invoked as a login shell (see INVOCATION below).
--norc Do not read and execute the system wide initialization file /etc/bash.bashrc and the personal
initialization file ~/.bashrc if the shell is interactive. This option is on by default if the
shell is invoked as sh.
(from the manpage).
It is often desirable to launch an entirely blank bash:
no environment variables carried from the parent shell;
an empty home dir without any package-specific configuration files (e.g. .gitconfig and .local/...);
no shell configuration files.
This works for me both on MacOS and Linux:
env -i HOME=$(mktemp -d) bash --noprofile --norc
cd
In that bash shell, the HOME dir is that test dir just created (change the name if needed), and there are no particular settings. The only environment variables that are set are PWD, HOME, and SHLVL.
Upon starting bash, the PWD is where we were before, so we need to do that initial cd.
Example (Linux):
$ env -i HOME=$(mktemp -d) bash --noprofile --norc
bash-5.0$ cd
bash-5.0$ pwd
/tmp/tmp.mwgHRQE1aJ
bash-5.0$ printenv
PWD=/tmp/tmp.mwgHRQE1aJ
HOME=/tmp/tmp.mwgHRQE1aJ
SHLVL=1
OLDPWD=/home/xxxxxxxxxxxxxxxxx
_=/usr/bin/printenv
bash-5.0$

Resources