I want to run a bash subshell, (1) run a few commands, (2) and then remain in that subshell to do as I please. I can do each of these individually:
Run command using
-c
flag:$> bash -c "ls; pwd; <other commands...>"
however, it immediately returns to the "super" shell after the commands are executed. I can also just run an interactive subshell:
Start new
bash
process:$> bash
and it won't exit the subshell until I say so explicitly... but I can't run any initial commands. The closest solution I've found is:
$> bash -c "ls; pwd; <other commands>; exec bash"
which works, but not the way I wanted to, as it runs the given commands in one subshell, and then opens a separate one for interaction.
I want to do this on a single line. Once I exit the subshell, I should return back to the regular "super"shell without incident. There must be a way~~
NB: What I am not asking...
- not asking where to get a hold of the bash man page
- not asking how to read initializing commands from a file... I know how to do this, it's not the solution I'm looking for
- not interested in using tmux or gnu screen
- not interested in giving context to this. I.e., the question is meant to be general, and not for any specific purpose
- if possible, I want to avoid using workarounds that sort of accomplish what I want, but in a "dirty" way. I just want to do this on a single line. In particular, I don't want to do something like
xterm -e 'ls'
This can be easily done with temporary named pipes:
Credit for this answer goes to the comment from Lie Ryan. I found this really useful, and it's less noticeable in the comments, so I thought it should be its own answer.
Try this instead:
$SHELL
It makes the shell open in interactive mode, waiting for a close withexit
.You can do this in a roundabout way with a temp file, although it will take two lines:
Why not use native subshells?
Enclosing commands with parentheses makes bash spawn a subprocess to run these commands, so you can, for example, alter the environment without affecting parent shell. This is basically more readable equivalent to the
bash -c "ls; pwd; exec $BASH"
.If that still looks verbose, there are two options. One is to have this snippet as a function:
Another is to make
exec $BASH
shorter:I personally like
R
approach more, as there is no need to play with escaping strings.What you need is to execute a startup script, then proceed with the interactive session.
The default startup script is
~/.bashrc
, another script could be given with the--init-file
option. If you simply pass an--init-file
option, your script would replace the default, rather than augment it.The solution is to pass, using the
<(...)
syntax, a temporary script that sources the default~/.bashrc
followed by any other commands:The "Expect solution" I was referring to is programming a bash shell with the Expect programming language:
You'd run that like:
./subshell.exp "ls; pwd"
If
sudo -E bash
does not work, I use the following, which has met my expectations so far:I set HOME=$HOME because I want my new session to have HOME set to my user's HOME, rather than root's HOME, which happens by default on some systems.
less elegant than
--init-file
, but perhaps more instrumentable:I accomplish basically the same thing by just using a script, typically for the purpose of setting environment variables for use in a specific project directory;
This drops you into an interactive
bash
shell after all the environment adjustments are made; you can update this script with the relevant other commands you want to run.In case you can't use process substitution:
With
sh
(dash
,busybox
):Or: