I'm trying to execute a command in shell started over SSH, but keep the shell open. I know that normally the shell closes, so this needs to be worked-around, however I'm not very satisfied with the easy way to do that, like:
ssh server -t "TheCommand ; bash -i"
First thing is that the command is then visible in the ps output, also the command is executed before the bash subshell is launched and I would prefer to launch it in the final shell - the command is supposed to setup a custom environment for the shell, and if the environment is setup before launching the final (sub)shell, the environment variables can be overwritten by the RC profile (if the variable name is the same like a variable set in the RC).
I've found that I can use heredoc, so I came with this (solution #1):
ssh server -tt 3<<SCRIPT 4<&0 <&3
TheCommand
exec 3>&- <&4
SCRIPT
Unfortunately that does not work correctly: on the line
exec 3>&- <&4
the following error is reported:
-bash: 4: Bad file descriptor
and the shell remains open, however it cannot take any input (only Ctrl+C works to exit the ssh shell). Other than that, the script is executed and if I add ps ax | grep bash
into the script, it shows:
15054 pts/15 Ss 0:00 -bash
15109 pts/15 S+ 0:00 grep bash
which would be perfect, as it is the same as with a regular ssh login.
What is it supposed to do:
- Read the heredoc script onto the custom file descriptor 3 (because if the regular input descriptor is used, the shell terminates after the script is done).
- Dup the original stdin stream (0) into custom stream 4.
- Redirect the stdin to the fd 3 to read the heredoc script.
- At the end of the heredoc script, restore the input stream back to the original one (i.e. dup the stored fd 4 back into 0).
However it seems, that the restored stream is not valid for some reason and I don't know how to fix that, I tried many things (for example restoring to /dev/tty
or $(tty)
, neither of which works - it does not report invalid stream and keeps the shell open, but I still cannot do anything there except Ctrl+C to exit).
I suspect that when I redirect the stdin to &3, the ssh or bash closes the standard stdin stream (or does not open/allocate it at all) so when I try to restore it back, it is invalid.
Is there any possibility to fix that? Because that would be my preferred solution.
I found one way to work this around like this (solution #2), by running the bash explicitly:
ssh server -t "bash -i 3<<SCRIPT 4<&0 <&3
TheCommand
exec 3>&- <&4
SCRIPT
"
That works, however it is not entirely ideal. One thing is, that it is not a login shell then (and it does not work with either bash -l
or bash -l -i
) and when running ps ax | grep bash
in such shell, it gives the following:
15234 pts/15 Ss 0:00 bash -c bash -i 3<<SCRIPT 4<&0 <&3?TheCommand?exec 3>&- <&4?SCRIPT?
15262 pts/15 S 0:00 bash -i
15282 pts/15 S+ 0:00 grep bash
which is not too good (the entire script is visible in the listing as with the simple solution at the beginning).
However, here the storing and restoring of the stdin (&0) works well. I'm wondering why it is working in the solution #2, but not in the solution #1, and if it can be fixed in the solution #1 somehow?
EDIT:
As some comments pointed out, I should better describe what I'm trying to solve.
In the principle, what exactly I'm trying to achieve is to develop a script that:
- connects to SSH remote machine
- changes the remote directory to location determined by the script
- sets some environment variables in the remote shell (note that it might need to override variables coming from the profile or bashrc; and the profile/bashrc scripts need to be run to setup e.g. the command prompt etc.)
- keep the ssh session open so that the operator can issue further commands afterwards (with the shell set up specifically)
One problem is the security issue (showing the entire command in the ps
output), another one is when setting env variables before launching the final shell, they get overridden by the profile/bashrc (which contain some defaults).
So far my currently best solution is this:
ssh server -t "cd /ThePath ; exec \$SHELL -i 3<<ENV 4<&0 <&3
export THE_VARIABLE=TheValue
exec 3>&- <&4
ENV
"
This way the variable is set after the profile is loaded, so that seems to work. Also, the ps ax
output is relatively fine:
17604 pts/15 Ss 0:00 /bin/bash -i
17654 pts/15 S+ 0:00 grep bash
so it seems that the exec
did the trick to some extent.
Is there any better way to do the above?