Wondering how to take a set of C functions and turn them into shell/bash commands.
So say I have a simple set of C functions
int
fn1() {
// some C code for function 1.
}
int
fn2() {
// some C code for function 2.
}
int
fn3() {
// some C code for function 3.
}
I would like to then somehow create CLI commands so I can use them from the terminal.
$ fn1 <param> <param> ...
$ fn2 ...
$ fn3 ...
Not sure what the process is of doing this. If I need to somehow rewrite all the function interfaces in shell script, and then call of to a C function in some way, sort of like this (bash script):
fn1() {
callc mylib/fn1 $1 $2
}
fn2() {
...
}
...
Or if I can just somehow convert each C function into a shell script automatically by separating them into individual files fn1.c
, fn2.c
, etc. and somehow loading them into the shell with source ~/.bash_profile
type of thing.
Any help would be appreciated, thank you.
Or go old-school, perhaps: write C code to examine how it was invoked (0th arg from original command-line arguments) and invoke the right C function based on that name. Requires compiling such a C program to a single executable, then creating symbolic links to the base application where the symlinks are the names of the functions of interest. No shell code needed beyond installing the artifacts here - executable and symlinks - into a directory in your $PATH.
Example. If the following code is name toybox.c, and ~/bin exists and is in the user's $PATH, use something like:
$ cc -o ~/bin/toybox toybox.c
$ ln -s toybox ~/bin/fn1
$ ln -s toybox ~/bin/fn2
$ ln -s toybox ~/bin/fn3
Simple tests - only shows that the scaffolding is in place.
$ fn1
fn1 invoked - no arguments.
$ fn3 1 2 'a b c'
fn3 invoked - arguments:
1 - '1'
2 - '2'
3 - 'a b c'
The source for toybox.c might look like:
#include <string.h>
#include <libgen.h>
#include <stdio.h>
struct name2func {
const char *name;
int (*func)(int ac, char *const av[]);
};
void
fn_debug(const char *fn, int ac, char *const av[])
{
int n;
printf("%s invoked - ", fn);
if (ac <= 0) {
printf("no arguments.\n");
} else {
printf("arguments:\n");
for (n = 0; n < ac; n++) {
printf(" %d - '%s'\n", n + 1, av[n]);
}
}
}
int
fn1(int ac, char *const av[])
{
fn_debug("fn1", ac, av);
/* some C code for function 1. */
return 0;
}
int
fn2(int ac, char *const av[])
{
fn_debug("fn2", ac, av);
/* some C code for function 2. */
return 0;
}
int
fn3(int ac, char *const av[])
{
fn_debug("fn3", ac, av);
/* some C code for function 3. */
return 0;
}
/*
* Establish a crude symbol table after function definitions: size of
* the name2func array (i.e., its number of elements) is available via the
* sizeof builtin.
*/
struct name2func n2f[] = {
{ "fn1", fn1 },
{ "fn2", fn2 },
{ "fn3", fn3 }
};
int
dispatch(const char *func_name, int ac, char *const av[])
{
size_t n;
/* linear search ok for small # of funcs */
for (n = 0; n < sizeof n2f / sizeof n2f[0]; n++) {
if (strcmp(func_name, n2f[n].name) == 0) {
return (*n2f[n].func)(ac, av);
}
}
fprintf(stderr, "%s: unsupported\n", func_name);
return 1;
}
int
main(int argc, char *const argv[])
{
/*
* using POSIX basename(3) to create, say, "fn1" from
* a full-path invocation like "/my/odd/dir/fn1".
*/
char *fnbase = basename(argv[0]);
if (fnbase == 0) {
perror("basename");
return 1;
}
return dispatch(fnbase, argc - 1, argv + 1);
}
Write a single C executable with all the functions in it ... then make aliases for them or create wrapper shell scripts .
Note aliases won't take options but you can use functions for that : https://askubuntu.com/questions/626458/can-i-pass-arguments-to-an-alias-command
Aliases:
alias function-x="set_of_c_functions.exe --run function_x"
alias function-y="set_of_c_functions.exe --run function_y"
scripts:
/usr/bin/function-x.sh
#!/bin/bash
/path/to/set_of_c_functions --run function_x -options ${@}
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf("I am a shell command with these arguments:\n");
for (int i = 0; i < argc; ++i)
printf("\t%s\n", argv[i]);
return EXIT_SUCCESS;
}
Compile the above, then execute the command “FullPathToDirectory/NameOfExecutableFile Argument1 Argument2”.
Once that is working, either move the executable file to one of the directories listed in your PATH environment variable or modify your PATH directory to include the directory that contains the executable file.
Some shells require you to execute the rehash
command after putting a new executable in the PATH.
Once that is working, modify the program as you desire.
You can create separate programs for each command. If you want all the source code in one program, a common method to do that is to create multiple file system links to the executable file (as with the Unix ln
command) and use the argv[0]
contents to choose which function to perform. (argv[0]
usually contains the executable path, with the file name as the last component.)