What Does This Command Do? "Exec Bash -L"

What does this command do? exec bash -l

exec executes a specified command, replacing the current process rather than starting a new subprocess.

If you type

bash -l

at a shell prompt, it will invoke a new shell process (the -l makes it a login shell). If you exit that shell process, you'll be back to your original shell process.

Typing

exec bash -l

means that the new shell process replaces your current shell process. It's probably slightly less resource intensive.

The reason for doing it is probably so that the new shell sets up its environment (by reading your .bashrc, .bash_profile, etc.).

See the bash documentation for more information:

  • Bash Startup Files for how a login shell differs from a non-login shell
  • Bourne Shell Builtins for documentation on the exec command.

(You should be able to read the manual on your own system by typing info bash.)

Option -l of exec shell command

The -l option of exec adds a - at the beginning of the name of your command. For example:

exec -l diff | head

-diff: missing operand after '-diff'
-diff: Try '-diff --help' for more information.

Note the - everywhere before diff.

The point of all this? If you have a - before a command to start a shell it will act as a login shell. From man bash:

A login shell is one whose first character of argument zero is a -, or one started with the --login option.

Now, man exec states that:

If the -l option is supplied, the shell places a dash at the beginning of the zeroth argument passed to command. This is
what login(1) does.

So exec -l bash will run bash as a login shell. To test this, we can use the fact that a login bash executes the file ~/.bash_profile, so:

$ cat ~/.bash_profile 
#!/bin/sh

printf "I am a login shell!\n"

If I start a login bash, the command printf "I am a login shell!\n" will be executed. Now to test with exec:

$ exec bash
$

Nothing is displayed, we are on a non-login shell.

$ exec -l bash
I am a login shell!
$

Here we have a login shell.

What is the purpose of using exec command

The exec is a builtin command of the Bash shell which allows you to execute a command that completely replaces the current process, i.e., the current shell process is destroyed, and entirely replaced by the command you specify. It is useful when you want to run a command, but you don't want a bash shell to be the parent process. When you exec a command, it replaces bash entirely - no new process is forked, no new PID is created, and all memory controlled by bash is destroyed and overwritten. This can be useful if, for instance, you want to give a user restricted access to a certain command. If the command exits because of an error, the user will not be returned to the privileged shell that executed it. exec may also be used without any command, to redirect all output of the current shell to a file. Here is the definition from man bash:

exec [-cl] [-a name] [command [arguments]]
If command is specified, it replaces the shell. No new process
is created. The arguments become the arguments to command. If
the -l option is supplied, the shell places a dash at the
beginning of the zeroth argument passed to command. This is
what login(1) does. The -c option causes command to be executed
with an empty environment. If -a is supplied, the shell passes
name as the zeroth argument to the executed command. If command
cannot be executed for some reason, a non-interactive shell
exits, unless the exec fail shell option is enabled. In that
case, it returns failure. An interactive shell returns failure
if the file cannot be executed. If command is not specified,
any redirections take effect in the current shell, and the
return status is 0. If there is a redirection error, the return
status is 1.

I don't understand bash exec

Yes, it sends any further output to the file named logfile. In other words, it redirects standard output (also known as stdout) to the file logfile.

Example

Let's start with this script:

$ cat >script.sh
#!/bin/bash
echo First
exec >>logfile
echo Second

If we run the script, we see output from the first but not the second echo statements:

$ bash script.sh
First

The output from the second echo statement went to the file logfile:

$ cat logfile
Second
$

If we had used exec >logfile, then the logfile would be overwritten each time the script was run. Because we used >> instead of >, however, the output will be appended to logfile. For example, if we run it once again:

$ bash script.sh
First
$ cat logfile
Second
Second

Documentation

This is documented in man bash:

exec [-cl] [-a name] [command [arguments]]
If command
is specified, it replaces the shell. No new process is created. The
arguments become the arguments to command. If the -l option is
supplied, the shell places a dash at the beginning of the zeroth
argument passed to command. This is what login(1) does. The -c
option causes command to be executed with an empty environment. If
-a is supplied, the shell passes name as the zeroth argument to the executed command. If command cannot be executed for some reason, a
non-interactive shell exits, unless the execfail shell option is
enabled. In that case, it returns failure. An interactive shell
returns failure if the file cannot be executed. If command is not
specified, any redirections take effect in the current shell, and the
return status is 0.
If there is a redirection error, the return
status is 1. [Emphasis added.]

In your case, no command argument is specified. So, the exec command performs redirections which, in this case, means any further stdout is sent to file logfile.

find command and -exec

The find command has a -exec option. For example:

find / -type f -exec grep -l "bash" {} \;

Other than the similarity in name, the -exec here has absolutely nothing to do with the shell command exec.

The construct -exec grep -l "bash" {} \; tells find to execute the command grep -l "bash" on any files that it finds. This is unrelated to the shell command exec >>logfile which executes nothing but has the effect of redirecting output.

`shell: bash -l {0}` in GitHub Actions

That could be linked to issue 128 which states:

I got stuck for a while because my run commands were not using a login bash shell.

So the conda environment was not active.

Would be helpful to warn about that and recommend something like the following in the yaml:

defaults:
run:
shell: bash -l {0}

With:

  • -l to insure a login bash, where the environment is correctly set;
  • {0}, a template placeholder, replaced at pipeline execution time by the actual script command to execute.

Why do I have to use bash -l -c inside my container?

From bash(1):

  • -l Make bash act as if it had been invoked as a login shell
  • -c If the -c option is present, then commands are read from string.

You're running the command passed to the -c argument. -l makes it a login shell so bash first reads /etc/profile, which probably has the path to rvm which is what makes it work.

FWIW, here's what I do to install rvm in a docker container.

# Install some dependencies
RUN apt-get -y -q install curl rubygems

# Install rvm
RUN curl -L https://get.rvm.io | bash -s stable

# Install package dependencies
RUN /usr/local/rvm/bin/rvm requirements

# Install ruby
RUN /usr/local/rvm/bin/rvm install ruby-2.0.0

# create first wrapper scripts
RUN /usr/local/rvm/bin/rvm wrapper ruby-2.0.0 myapp rake rails gem

do I add 'exec bash -l' to .cshrc to have bash default shell for ssh session? (or what)

Use chsh command to change your default login shell.

Print the list of shells available:

chsh -l

Change default shell:

chsh -s /bin/bash <username>

Get the default login shell of the current user:

getent passwd <username> | cut -d: -f7

Bash shebang option -l

The -l option (according to the man page) makes "bash act as if it had been invoked as a login shell". Login shells read certain initialization files from your home directory, such as .bash_profile. Since you set the value of TEST in your .bash_profile, the value you set on the command line gets overridden when bash launches.

Execute a command in bash script

There are two ways you can do this. One way would be to set your shell script as

# File: command_execute.sh
$1

and run it like this: sh command_execute.sh 'ls -l' (notice the single quotes around ls -l). What this will do is the full string ls -l will be passed to your script, and since it is the first argument to the script, $1 in the script will be effectively replaced with ls -l, and then that command will be executed.

The other way would be to use this as your script:

# File: command_execute.sh
"$@"

In this case, "$@" is all the arguments that were passed to the script. If you run the script like sh command_execute.sh ls -l (note in this case the ls -l is not quoted), then ls is passed to the script as argument 1, and -l is passed to the script as argument 2. Then "$@" is effectively replaced with ls -l, and then the command is executed.

Which of these is best depends on your requirements.



Related Topics



Leave a reply



Submit