Why does remote command by ssh read bashrc file? - bash

According to a document, it says:
When an interactive shell that is not a login shell is started, Bash reads and executes commands from ~/.bashrc, if that file exists.
I did a quick test:
At my server,
[USER#MYSERVER ~]$ cat .bashrc
...
echo 'I am in a bashrc file of my server'
...
At a remote server,
# unquoted
[USER#REMOTESERVER ~]$ ssh MYSERVER echo $-
I am in a bashrc file of my server
himBH
#quoted
[USER#REMOTESERVER ~]$ ssh MYSERVER 'echo $-'
I am in a bashrc file of my server
hBc
When command is unquoted, it seems to be run in an interactive shell, and when quoted, it seems to be run in a non-interactive shell.
Why is this so?
And both read the bashrc file of MYSERVER, which doesn't follow the rule in the document.
Any link or comment appreciated.
EDITED:
And it seems to be a non-login shell.
[USER#REMOTESERVER ~]$ ssh MYSERVER 'shopt -q login_shell && echo 1 || echo 2'
2

In the bash document, there says:
Invoked by remote shell daemon
Bash attempts to determine when it is being run with its standard input connected to a network connection, as when executed by the remote shell daemon, usually rshd, or the secure shell daemon sshd. If Bash determines it is being run in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable.
I missed this part...
Therefore, calling from ssh should read .bashrc file.
And ssh remote command is a non-interactive shell, as comments to the question explain.

The remote bash is indeed not started as an interactive shell (as we can see from the output from $-), so somewhat else must be sourcing your .bashrc. For sure, it is run as a login shell. Could it be that you have a ~/.bash_profile or ~.bash_login or ~/.profile, which explicitly sources .bashrc?

Related

Shell Script that does chroot and execute commands in chroot

If in shell script I write
chroot /home/mayank/chroot/codebase
cd SBC
when I run this shell script It does go in the chroot but does not execute the command cd SBC,
when I exit chroot then it executes cd SBC.
How can I achieve something that does chroot and execute commands in chroot through shell script ??
When you run chroot without telling it what to do, it will try to start chrooted interactive shell session. So your script would "pause" at that point and when you are done with that interactive shell session, it continues out of chroot again.
One of the quick and dirt options would be to abuse here-document, like this:
chroot /home/mayank/chroot/codebase /bin/bash <<"EOT"
cd /tmp/so
ls -l
echo $$
EOT
Which takes all lines up to EOT and feeds them into bash started through chroot. Those double quotes around "EOT" should ensure bash passes the content not trying to expand variables and such. Hence that echo $$ should be PID of the inner chrooted bash.
somewhat I found a solution,
chroot /work3/tmp_GU/$build_env/sbcbuild/chroot ./test.sh
after chroot giving a script there is working fine for me.
test.sh present in the chroot folder.
All commands in test.sh will be executed in chroot folder.
So basically giving a command after chroot
man chroot
chroot [OPTION] NEWROOT [COMMAND [ARG]...]

Reading "bash_profile" is doing two things that are contradicting each other

I'm very confused about how my shell is reading bash_profile.
In root, my ~/.bash_profile looks like so
# .bash_profile
# Get the aliases and functions
if [-f ~/.bashrc ]; then
.~/.bashrc
fi
PATH=$PATH:$HOME/bin:$HOME/sbin:$HOME/usr/sbin:$HOME/usr/bin:/usr/sbin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
export PATH=$PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH
unset USERNAME
There is no ~/.profile file.
In a user called maruhan, my ~/.bash_profile looks like so
# .bash_profile
# Get the aliases and functions
if [-f ~/.bashrc ]; then
.~/.bashrc
fi
PATH=$PATH:$HOME/bin:$HOME/sbin:$HOME/usr/sbin:$HOME/usr/bin:/usr/sbin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/maruhan/Desktop/issac:/usr/local/lib
ASDF=$ASDF:/home
export PATH=$PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH
export ASDF=$ASDF
unset USERNAME
And my ~/.profile looks like so
LD_LIBRARY_PATH=/home/maruhan/Desktop/issac:/usr/local/lib:$LD_LIBRARY_PATH
ASDF=/home:$ASDF
export ASDF=$ASDF
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH
You can clearly see that ASDF is not defined in root's bash_profile.
However when I call export, I get this in root.
declare -x ASDF=":/home"
but nothing about LD_LIBRARY_PATH.
Strangely in maruhan, running export shows both ASDF and LD_LIBRARY_PATH.
Also, nothing about ASDF or LD_LIBRARY_PATH exist in /etc/environment. I also don't have a /etc/bash_profile file.
Running echo $0 gives me bash for both root and maruhan.
How come LD_LIBRARY_PATH disappeared in root while ASDF is there?
The rules are a bit complicated. According to bash's man page:
INVOCATION
A login shell is one whose first character of argument zero is a -, or one
started with the --login option.
An interactive shell is one started without non-option arguments (unless -s is
specified) and without the -c option whose standard input and error are both
connected to terminals (as determined by isatty(3)), or one started with the -i
option. PS1 is set and $- includes i if bash is interactive, allowing a shell
script or a startup file to test this state.
... ...
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 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.
... ...
Note that on some systems bash may be customized so that it would also execute a system wide rc file (e.g. /etc/bash.bashrc) before sourcing ~/.bashrc for an interactive shell that's not a login shell.
Shells started by a login mechanism (usually with a username/password prompt, like console login, telnet, ssh, ...) are usually login shells. For a login shell, $0 is usually -bash.
[local] % ssh user#host <-- The user is trying to login
Password: P#ssw0rd
[remote] % echo $0
-bash <-- This is a login shell
[remote] % bash <-- This is not a login (no username/password)
[remote] % echo $0
bash <-- Not a login shell
[remote] %
To make life easier I would put all rc things in ~/.bashrc and source ~/.bashrc in ~/.bash_profile. For example:
% cat ~/.bash_profile
[[ -f ~/.bashrc ]] && source ~/.bashrc
% cat ~/.bashrc
# return immediately if not in an interactive shell
[[ $- != *i* ]] && return 0
export FOO=bar
PATH=$PATH:/my/path
%

Sending Bash Aliases to detached screen sessions

I'm on a Linux machine using screen, and I'm attempting to write a (fairly portable) function which runs a bash function in a new, detached screen session which automatically closes upon completion. I've had some success, but I noticed the following behavior:
If I include the definition of mail_submit() in my ~/.bashrc file, I can run
mail_submit foo
in the terminal, and also I can access the alias in a new screen session:
screen -S test
mail_submit foo
However, the following command does not work:
screen -d -m -S test sh -c 'mail_submit foo'
presumably because sh -c starts a fresh shell that has no knowledge of my ~/.bashrc profile. So, I can use the following fix:
screen -d -m -S test sh -c 'source ~/.bashrc; mail_submit foo'
which does work.
But if I want to wrap this functionality up into a bash alias (which is my ultimate goal here), this will cause a weird self-referential situation.
Question: What is an easy way to either have sh -c know the location of my ~/.bashrc profile, or use a variant of sourcing the file and creating an alias?
EDIT: I could save the shell script in my home directory, and create an alias which runs
screen -d -m -S test bash -c '~/mail_submit.sh $1'
but I'd still be curious to hear other possible fixes.
A default ~/.bashrc contains this ([[ "$-" != *i* ]] && return) little piece of code on top of it (or somewhere else in the upper part). This line will prevent the ~/.bashrc from beeing sourced if the bash shell doesn't run in interactive mode.
You could:
Remove this line
Create a new file which will only contain the alias you need and source that
Create a little bash script instead of an alias and run that
Do you mean screen -d -m -S test bash -c 'mail_submit foo'?
It looks like you're trying to run the command with the shell (sh), and not the bourne again shell (bash), which is the shell interpreter which actually reads the ~/.bashrc profile.
Edit: The .bashrc file is not being sourced by default because screen does not create the bash process as a login shell, which is when the .bashrc file is read. Creating a .screenrc file with the line defshell -bash will create the bash process as a login shell instead, which will then call the .bashrc file.

ssh and chroot followed by cd in shell

How to excute cd command after chroot to a remote node in shell script?
For ex:
I need this.
ssh remote-node "chroot-path cd command here; extra commands"
Without chroot it works fine, If I put the command list in another shell script and execute the shell script after chroot it seems to run okay.
But chroot seems to break cd?
Use printf %q to have your local shell (which must be bash) give you correct quoting that works, and bash -c to explicitly invoke a remote shell compatible with that quoting (as %q can generate bash-only quoting with input strings that contain special characters) under your chroot.
cmd_str='cd /to/place; extra commands'
remote_command=( bash -c "$cmd_str" )
printf -v remote_command_str '%q ' "${remote_command[#]}"
ssh remote-node "chroot /path/here $remote_command_str"
The bash -c is necessary because cd is a shell construct, and chroot directly exec's its arguments (with no shell) by default.
The printf %q and correct (single-quote) quoting for cmd_str ensures that the command string is executed by the final shell (the bash -c invoked under the chroot), not your local shell, and not by the remote pre-chroot shell.
Assuming by chroot-path you mean chroot /some/root/path.
chroot only takes a single command and cd isn't a command it is a shell built-in so that won't work.
Additionally only cd command here is being run (or attempted to) under the chroot setup. Everything after the ; is running in the main shell.
A script is the easiest way to do what you want.

How to force ssh to execute bash instead of the user default on the remote machine?

I want to execute a bash script with ssh but when I try this it's using ksh which is the user's default shell.
I can't change that default.
So, how can I trick ssh to execute my script with bash instead of the default shell?
Make this the first line of your script:
#!/usr/bin/env bash
Edit: As per this, the utility of /usr/bin/env is dubious. So, you probably want:
#!/bin/bash
Replace /bin/bash with the actual path of bash executable.
You can call your script explicitly with bash:
ssh <ssh-opts> bash <scriptname>
This way there will be a ksh executed at login, but inside ksh you start a bash executing your script.

Resources