InfinityQuest - Programming Code Tutorials and Examples with Python, C++, Java, PHP, C#, JavaScript, Swift and more

Menu
  • Home
  • Sitemap

Python Programming Language Best Tutorials and Code Examples

Learn Python Right Now!
Home
Bash
Adding New Features to bash Using Loadable Built-ins in bash
Bash

Adding New Features to bash Using Loadable Built-ins in bash

InfinityCoder June 27, 2017

The material in this recipe also appears in Learning the bash Shell by Cameron Newham (O’Reilly).

You have something that you’d like bash to do, but there’s no built-in command for it.

For efficiency reasons, you want it to be built-in to the shell rather than an external program. Or, you already have the code in C and don’t want to or can’t rewrite it.

Use the dynamically loadable built-ins introduced in bash version 2.0. The bash archive contains a number of pre-written built-ins in the directory ./examples/ loadables/, especially the canonical hello.c.

You can build them by uncommenting the lines in the file Makefile that are relevant to your system, and typing make.

We’ll take one of these built-ins, tty, and use it as a case study for built-ins in general.

The following is a list of the built-ins provided in bash version 3.2’s ./examples/ loadables/:

basename.c           id.c                           push.c                            truefalse.c
cat.c                        ln.c                           realpath.c                      tty.c
cut.c                        logname.c               rmdir.c                          uname.c
dirname.c              mkdir.c                    sleep.c                           unlink.c
finfo.c                     necho.c                    strftime.c                      whoami.c
getconf.c                pathchk.c                sync.c                             perl/bperl.c
head.c                     print.c                     tee.c                                perl/iperl.c
hello.c                     printenv.c              template.c

On systems that support dynamic loading, you can write your own built-ins in C, compile them into shared objects, and load them at any time from within the shell with the enable built-in.

We will discuss briefly how to go about writing a built-in and loading it in bash. The discussion assumes that you have experience with writing, compiling, and linking C programs.

tty will mimic the standard Unix command tty. It will print the name of the terminal that is connected to standard input. The built-in will, like the command, return true if the device is a TTY and false if it isn’t.

In addition, it will take an option, -s, which specifies that it should work silently (i.e., print nothing and just return a
result).

The C code for a built-in can be divided into three distinct sections: the code that implements the functionality of the built-in, a help text message definition, and a structure describing the built-in so that bash can access it.

The description structure is quite straightforward and takes the form:

1
2
3
4
5
6
7
8
struct builtin builtin_name_struct = {
   "builtin_name",
   function_name,
   BUILTIN_ENABLED,
   help_array,
   "usage",
   0
};

The trailing _struct is required on the first line to give the enable built-in a way to find the symbol name. builtin_name is the name of the built-in as it appears in bash.

The next field, function-name, is the name of the C function that implements the built-in. We’ll look at this in a moment. BUILTIN_ENABLED is the initial state of the built-in, whether it is enabled or not.

This field should always be set to BUILTIN_ENABLED. help_array is an array of strings that are printed when help is used on the built-in. usage is the shorter form of help: the command and its options.

The last field in the structure should be set to 0. In our example we’ll call the built-in tty, the C function tty_builtin, and the help array tty_doc. The usage string will be tty [-s].

The resulting structure looks like this:

1
2
3
4
5
6
7
8
struct builtin tty_struct = {
   "tty",
   tty_builtin,
   BUILTIN_ENABLED,
   tty_doc,
   "tty [-s]",
   0
};

The next section is the code that does the work. It looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
tty_builtin (list)
    WORD_LIST *list;
{
    int opt, sflag;
    char *t;
 
    reset_internal_getopt ( );
    sflag = 0;
    while ((opt = internal_getopt (list, "s")) != -1)
    {
      switch (opt)
      {
          case 's':
             sflag = 1;
             break;
          default:
             builtin_usage ( );
             return (EX_USAGE);
      }
     }
     list = loptend;
 
     t = ttyname (0);
     if (sflag == 0)
        puts (t ? t : "not a tty");
     return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}

Built-in functions are always given a pointer to a list of type WORD_LIST. If the built-in doesn’t actually take any options, you must call no_options(list) and check its return value before any further processing.

If the return value is nonzero, your function should immediately return with the value EX_USAGE.

You must always use internal_getopt rather than the standard C library getopt to process the built-in options. Also, you must reset the option processing first by calling reset_internal_getopt.

Option processing is performed in the standard way, except if the options are incorrect, in which case you should return EX_USAGE. Any arguments left after option processing are pointed to by loptend.

Once the function is finished, it should return the value EXECUTION_SUCCESS or EXECUTION_FAILURE.

In the case of our tty built-in, we then just call the standard C library routine ttyname, and if the -s option wasn’t given, print out the name of the TTY (or “not a tty” if the device wasn’t).

The function then returns success or failure, depending upon the result from the call to ttyname. The last major section is the help definition. This is simply an array of strings, the
last element of the array being NULL.

Each string is printed to standard output when help is run on the built-in. You should, therefore, keep the strings to 76 characters or less (an 80-character standard display minus a 4-character margin). In the case of tty, our help text looks like this:

1
2
3
4
5
6
7
char *tty_doc[] = {
  "tty writes the name of the terminal that is opened for standard",
  "input to standard output. If the `-s' option is supplied, nothing",
  "is written; the exit status determines whether or not the standard",
  "input is connected to a tty.",
  (char *)NULL
}

The last things to add to our code are the necessary C header files. These are stdio.h and the bash header files config.h, builtins.h, shell.h, and bashgetopt.h. Here is the C program in its entirety:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# cookbook filename: builtin_tty.c
 
#include "config.h"
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
 
 
extern char *ttyname ( );
 
tty_builtin (list)
   WORD_LIST *list;
{
   int opt, sflag;
   char *t;
 
   reset_internal_getopt ( );
   sflag = 0;
   while ((opt = internal_getopt (list, "s")) != -1)
{
   switch (opt)
   {
     case 's':
        sflag = 1;
        break;
     default:
        builtin_usage ( );
        return (EX_USAGE);
    }
}
list = loptend;
t = ttyname (0);
if (sflag == 0)
   puts (t ? t : "not a tty");
return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}
 
char *tty_doc[] = {
  "tty writes the name of the terminal that is opened for standard",
  "input to standard output. If the `-s' option is supplied, nothing",
  "is written; the exit status determines whether or not the standard",
  "input is connected to a tty.",
  (char *)NULL
};
 
struct builtin tty_struct = {
  "tty",
  tty_builtin,
  BUILTIN_ENABLED,
  tty_doc,
  "tty [-s]",
  0
};

We now need to compile and link this as a dynamic shared object. Unfortunately, different systems have different ways to specify how to compile dynamic shared objects.

After you have compiled and linked the program, you should have a shared object called tty. To load this into bash, just type enable -f tty tty. You can remove a loaded built-in at any time with the -d option, e.g., enable -d tty.

You can put as many built-ins as you like into one shared object as long as the three main sections for each built-in are in the same C file. It is best, however, to keep the number of built-ins per shared object small.

You will also probably find it best to keep similar built-ins, or built-ins that work together (e.g., pushd, popd, dirs), in the same shared object.

bash loads a shared object as a whole, so if you ask it to load one built-in from a shared object that has 20 built-ins, it will load all 20 (but only one will be enabled).

For this reason, keep the number of built-ins small to save loading memory with unnecessary things, and group similar built-ins so that if the user enables one of them, all of them will be loaded and ready in memory for enabling.

BASH Linux Job Control Pausing & Resuming Apps Processes & Downloads, etc in Terminal TTY UNIX *BSD

Share
Tweet
Email
Prev Article

Related Articles

Keeping Your Data with Your Script in bash
You need input to your script, but don’t want a …

Keeping Your Data with Your Script in bash

Creating Simple Menus in bash
You have a simple SQL script that you would like …

Creating Simple Menus in bash

About The Author

InfinityCoder
InfinityCoder

Leave a Reply

Cancel reply

Recent Tutorials InfinityQuest

  • Adding New Features to bash Using Loadable Built-ins in bash
    Adding New Features to bash Using Loadable …
    June 27, 2017 0
  • Getting to the Bottom of Things in bash
    Getting to the Bottom of Things in …
    June 27, 2017 0

Recent Comments

  • fer on Turning a Dictionary into XML in Python
  • mahesh on Turning a Dictionary into XML in Python

Categories

  • Bash
  • PHP
  • Python
  • Uncategorized

InfinityQuest - Programming Code Tutorials and Examples with Python, C++, Java, PHP, C#, JavaScript, Swift and more

About Us

Start learning your desired programming language with InfinityQuest.com.

On our website you can access any tutorial that you want with video and code examples.

We are very happy and honored that InfinityQuest.com has been listed as a recommended learning website for students.

Popular Tags

binary data python CIDR convert string into datetime python create xml from dict python dictionary into xml python how to create xml with dict in Python how to write binary data in Python IP Address read binary data python tutorial string as date object python string to datetime python

Archives

  • June 2017
  • April 2017
  • February 2017
  • January 2017
  • December 2016
  • November 2016
Copyright © 2021 InfinityQuest - Programming Code Tutorials and Examples with Python, C++, Java, PHP, C#, JavaScript, Swift and more
Programming Tutorials | Sitemap