How do I add an internal command to bash?
bash, command
Solution
This actually came up as part of an answer to a more specific question (how to get the evaluated `PS1` output to a variable) and I thought the process deserved its own question and answer, one more generalised to the process of modifying `bash` source code.
Here are the steps to add an internal command `evalps1`, which is meant to give you the same output as your primary prompt string. It's based on the version `4.2` code base.
Step 1.
First, change `support/mkversion.sh` so that you won't confuse it with a "real" `bash`, and so that the FSF can deny all knowledge for warranty purposes :-) Simply change one line (I added the `-pax` bit):
echo "#define DISTVERSION \"${float_dist}-pax\""
That script is the one which creates `version.h` and affects all the various ways of getting version information from `bash`: `--version`, the `$BASH_VERSION` variable and so forth.
Step 2:
Change `builtins/Makefile.in` to add a new source file to the mix. This entails a number of steps.
(a) Add `$(srcdir)/evalps1.def` to the end of `DEFSRC`. This `def` file contains information about the internal command as well as the implementation code.
(b) Add `evalps1.o` to the end of `OFILES`. This simply causes your code to be linked in with `bash`.
(c) Add the required dependencies:
evalps1.o: evalps1.def $(topdir)/bashtypes.h $(topdir)/config.h \
$(topdir)/bashintl.h $(topdir)/shell.h common.h
Step 3:
Add the `builtins/evalps1.def` file itself, this is the code that gets executed when you run the `evalps1` command:
This file is evalps1.def, from which is created evalps1.c.
It implements the builtin "evalps1" in Bash.
Copyright (C) 1987-2009 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
$PRODUCES evalps1.c
$BUILTIN evalps1
$FUNCTION evalps1_builtin
$SHORT_DOC evalps1
Outputs the fully interpreted PS1 prompt.
Outputs the PS1 prompt, fully evaluated, for whatever nefarious purposes
you require.
$END
The bulk of it is the GPL licence (since I modified it from `exit.def`) as shown above, with a very simple function at the end to get and decode `PS1`, using the same functions as the shell itself uses:
#include <config.h>
#include "../bashtypes.h"
#include <stdio.h>
#include "../bashintl.h"
#include "../shell.h"
#include "common.h"
int
evalps1_builtin (list)
WORD_LIST *list;
{
char *ps1 = get_string_value ("PS1");
if (ps1 != 0)
{
ps1 = decode_prompt_string (ps1);
if (ps1 != 0)
{
printf ("%s", ps1);
}
}
return 0;
}
The code itself is quite simple in this case, although it uses what I consider unnecessarily complicated indentation/brace rules and a K&R-style pre-prototype function declaration - I've kept them the same as the current code for consistency.
The code just calls the function to get the `PS1` environment variable, then calls another function which decodes prompt strings. Finally, it outputs the decoded string to standard output.
Of course, you may replace that code with your own (along with changing the command and file names from `evalps1` to something else), depending on your particular needs.
Step 4:
Just build the thing in the top level directory:
./configure
make
The `bash` executable that appears in that top-level directory can be renamed to `paxsh` (for example), though I doubt it will ever become as prevalent as its ancestor :-)
And running it, you can see it in action, starting in a real `bash` shell:
pax> mv bash paxsh
pax> ./paxsh --version
GNU bash, version 4.2-pax.0(1)-release (i686-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
pax> ./paxsh
pax> echo $BASH_VERSION
4.2-pax.0(1)-release
pax> echo "[$PS1]"
[pax> ]
pax> echo "[$(evalps1)]"
[pax> ]
pax> PS1="\h: "
paxbox01: echo "[$PS1]"
[\h: ]
paxbox01: echo "[$(evalps1)]"
[paxbox01: ]
Problem
I don't mean an alias or function. I actually want to add an entire new internal command to the `bash` source code. That way, when I compile it, it has my new command built in to the shell itself. What are the steps involved in doing this?