exec command bin bash

10,992

How the code works and why

There's 3 main things going on here:

  • $@ is special shell variable that expands to all command-line arguments to script
  • ${cmdline:-/bin/bash} is one of the parameter expansion structures; if cmdline variable is unset or empty, the whole ${} portion gets replaced with whatever comes after - sign, in this case /bin/bash ; this is sort of a short-hand for if statement or ternary operator in other programming languages ( not exactly, but good enough for comparison)
  • exec is used to spawn a process that will overtake current process's PID, i.e. simply replace your script process with whatever was inside that ${}

Putting all that together the code simply takes command-line arguments and runs them, and if there's no command-line arguments to script - you get interactive bash shell. Note that you could also pass options to exec that are mentioned in documentation - compare ./exec_script.sh -c env and ./exec_script.sh env.

Good in theory, bad in practice

The approach itself might seem convoluted, but it is frequent to see this approach with exec in wrapper scripts - a script sets up environment and checks variables before organizing everything to run actual command. The difference here is that in wrapper scripts command is set - a wrapper script usually sets up environment and arguments for running just one particular program.

By contrast, this script aims at running whatever user puts on command-line. And this has a problem because of how shell works - given command-line arguments that contain special characters, the command you intend to run might break. Here's what I mean:

# This is how it's supposed to work
$ printf 'one%stwo' $'\t'                                                              
one     two

# This is how it works with unquoted parameter expansion
$ ./exec_script.sh  printf 'one%stwo' $'\t'                                            
onetwo

Imagine if you're trying to use that script to run my_cool_command filename$'\t'with$'\t'tabs.txt; at best - command-breaks, but if you also have filenamewithtab.txt file in your current folder, my_cool_command will operate on completely wrong file. And quoting parameter expansion doesn't help either, because then it breaks:

$ ./exec_script.sh  printf 'one%stwo' $'\t'                                            
./exec_script.sh: line 4: exec: printf one%stwo     : not found

Relevant documentation

Here's relevant portion about parameter expansion from bash (version 4.3) manual:

${parameter:-word}

Use Default Values. If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

"Special Parameters" section:

@ Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$@" is equivalent to "$1" "$2" ... If the double-quoted expansion occurs within a word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the last parameter is joined with the last part of the original word. When there are no positional parameters, "$@" and $@ expand to nothing (i.e., they are removed).

From "Shell Builtin Commands" section:

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 can‐not 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.

Conclusion and thoughts

This script itself has a good idea in theory, but poor in practice. The exec command will work well in wrapper scripts, and in fact as one of top users on Unix & Linux site, Gilles , mentioned "...exec also saves a bit of memory (and other resources such as PIDs etc.) since it there's no need to keep an extra shell around with nothing left to do". But the script in this case aims at reinventing the wheel and doing what shell already does well enough - i.e. running commands with arguments.

Share:
10,992

Related videos on Youtube

user1050619
Author by

user1050619

Updated on September 18, 2022

Comments

  • user1050619
    user1050619 over 1 year

    I was checking a shell script and noticed the below command - exec. the exec command executes the cmdline but I'm wondering what is the :-/bin/bash command do here.

    cmdline="$@"
        exec ${cmdline:-/bin/bash}