How create shell commands from C functions

痴心易碎 提交于 2019-12-05 18:13:35

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.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!