31

I wanted to simply calculate the length of a string (that is hash value). So, I opened terminal and did this:

$ apropos length

that returned me with a bunch of commands/functions having (3) or (3ssl) appended at the end of them. Now man man gives us information about what these section numbers mean.

3   Library calls (functions within program libraries)

Out of curiosity, I just tried with all these commands (in hope at least one would work)

strcspn (3)          - get length of a prefix substring
strlen (3)           - calculate the length of a string
strnlen (3)          - determine the length of a fixed-size string
strspn (3)           - get length of a prefix substring
wcslen (3)           - determine the length of a wide-character string
wcsnlen (3)          - determine the length of a fixed-size wide-character string

and got nothing but same error for every command

$ strnlen HelloWorld 
$ strnlen: command not found

Well, I know how to find length of string in shell using wc -m , expr length and other workarounds.

But, I have 2 questions here :

  1. How to use any library calls (3) inside the shell?
  2. How to calculate string length using just library calls and not other commands?

NOTE : Question focuses in general library calls and their usage in the shell. That makes first question more important to answer.

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
  • https://stackoverflow.com/q/49719554/841108 is a near duplicate – Basile Starynkevitch Apr 08 '18 at 17:01
  • 4
    Basically, though Michael's answer is a great hack, the straightforward answer is: you don't. Library calls are not related to the shell. They are used for writing programs in C language. — More generally, when reading manpages, unless you're coding a program, just ignore sections 2, 3 and 9. – spectras Apr 08 '18 at 21:04
  • Off Topic, but OpenVMS has "lexical" functions which are shell interfaces to a great deal of system library functions. – RonJohn Apr 09 '18 at 00:06

4 Answers4

42

You probably shouldn't do this, but you can. Kusalananda's answer is better for the task at hand and explains the issue. Since you did ask specifically how to use any library calls inside the terminal, though, here's a few ways...


The Tiny C Compiler (tcc) supports a -run flag that lets you (in effect) interpret C code by writing a small program, so you can use any library calls inside the terminal through a single invocation of that.

You can run the strnlen function like this:

$ tcc -run <(echo '#include <stdio.h>'; echo '#include <string.h>'; echo 'void main(int argc, char **argv) {printf("%i\n", strnlen(argv[1], 1024));}') "Hello world"
11

This uses process substitution from Bash, zsh, and other shells to give tcc a file to read that appears to contain the results of all the echos; there are other options.

You could make a function to generate this for you:

call_library_function_s_i() {
    func=$1
    shift
    tcc -run <(echo '#include <stdio.h>'; echo '#include <string.h>'; echo 'void main(int argc, char **argv) {printf("%i\n", '$func'(argv[1]));}') "$*"
}
$ call_library_function_s_i strlen hello world

(I've used strlen here so that it's a unary function string->int - you'd need a separate function for each different arity and return type).


Another option is the ctypes.sh Bash plugin by Tavis Ormandy, which wraps up dlopen and dlsym. This is probably the closest approximation to what you were trying. You can use, for example:

$ dlcall -r uint64 strlen "hello world"

and it will call the function as expected.

This is the most direct way to do it "from the terminal", but it's unlikely to be something your distribution packages up so you'd have to install it manually (which is nontrivial). Here are some informative quotes from ctypes.sh's own website to give a general impression of how people feel about doing this:

  • "that's disgusting"
  • "this has got to stop"
  • "you've gone too far with this"

There may be similar tools for other shells, but I don't know about them. In theory there's no reason there couldn't be a standalone command that did this exactly for the simple cases, but I'm somewhat surprised I haven't been able to find one...


... so I made one! dlcall lets you call library functions from the command line:

$ dlcall strnlen "hello world" 6
$ dlcall sin 2.5
$ dlcall strchr "hello world" -c ' '

It supports a limited set of function prototypes, it's not terribly reliable or resilient currently, but it now exists.


You could also use, for example, Python and python -c 'import ctypes; import sys; print(ctypes.cdll.LoadLibrary("libc.so.6").strlen(" ".join(sys.argv[1:])))' hello world, but it's certainly not the easiest way to go about it. Perl, Ruby, and other languages have similar features you could use.


So the answers to your questions are:

  1. Use one of the approaches above.
  2. You do need to use another command to bootstrap you into the library, or a piece of software that hooks into your shell.

All in all, you'll almost certainly be better off doing this any other way.

Michael Homer
  • 76,565
  • 2
    "dlcall" reminds me of (not quite identical) Windows "rundll": https://support.microsoft.com/en-gb/help/164787/info-windows-rundll-and-rundll32-interface – pjc50 Apr 09 '18 at 22:03
  • 1
    @pjc50: Public service announcement: rundll32 is not a general purpose tool for calling arbitrary functions. It can only be used to call functions with a given signature. Also, it's not terribly future-proof. – Kevin Apr 10 '18 at 00:47
  • Those quotes are unnecessary and harmful. In fact, the disgusting thing is to not have a generalized user interface to existing functionality. It is good software design to re-use and generalize code, is right in the spirit of Unix, and Windows already has rundll. Monolithism is a toxic software design anti-pattern that came from the commercialisation of software, which stifled integration and interoperability. (For example: It is messed-up that one can’t just use the Photoshop brushes in a Word document. Not being able to just use libraries in shell scripts is like that too.) –  Oct 03 '23 at 12:02
28

The apropos command is useful in many ways, but it does give you a lot of "junk" too. Most of the things that you list are C library routines (this is what section 3 of the manual is for), which you can not use directly from the shell.

To use them, you would have to write C program that calls them. This falls outside of the range of topics covered by this particular site (it would be on topic at StackOverflow).

These, thus, are the answers to your questions:

  1. You can't, they are C library routines.
  2. You can't, unless you write a C program.

I know you know this, but for the sake of completeness: In the shell, if you have a string in a variable string, you may do

string='hello world'
printf 'Length of string "%s" is %d\n' "$string" "${#string}"

This will print Length of string "hello world" is 11 in the terminal where the 11 comes from ${#string} which expands to the length of the string in $string.

Internally, the shell may well be using one of the library calls that you listed to do its length calculation.

This is the most efficient way to get the length of a string that is stored in a shell variable, in the shell.

Note too that ${#string} is a POSIX shell parameter expansion, it is therefore portable between all shells that claim any degree of POSIX compliance.

Kusalananda
  • 333,661
  • The parts about the library functions are a good explanation, but the rest of this answer is a bit misleading. OP says "I wanted to simply calculate the length of a string" There's no need to use printf here. If you want to print it out as part of sentence then that might be the best way. But the answer to the question of "how do I get the length of a string?" is the ${#string} part, not the printf. You can just echo ${#string} if you just want to output just the length, or assign it to another variable with string_length=${#string} or whatever you want. printf is not really relevant – Adam Apr 08 '18 at 18:24
  • 1
    @Adam I have made small modifications to the answer. I think it's pretty clear how to get the length of the string and the user has already said they know how to do it. By using the length of the string with printf I add something other than just saying "use ${#string}" (which is self evident from the example). – Kusalananda Apr 08 '18 at 18:50
16

I wouldn't do this for just strlen(), but it is a useful trick for trying out C code sometimes.

user@host:~$ gdb gdb
(gdb) start
Temporary breakpoint 1, ... in main ()
(gdb) print strlen("foobar")
$1 = 6

Here gdb is the GNU debugger, and it would normally take a program name to debug after it. Because we have none, this example gives it itself to debug. Then start starts the program, and after that gdb can be used to execute arbitrary C code.

jpa
  • 1,269
  • 2
    Nice trick, to use gdb. Yet gdb is part of developer tools, and if you want to use library function, you better prepare to learn to use developer tools. – Dudi Boy Apr 08 '18 at 19:02
6

A tool which can be used to interactively call functions in shared libraries: the "Witchcraft Compiler Collection". https://github.com/endrazine/wcc

From the GitHub page:

wsh : The Witchcraft shell

The witchcraft shell accepts ELF shared libraries, ELF ET_DYN executables and Witchcraft Shell Scripts written in Punk-C as an input. It loads all the executables in its own address space and makes their API available for programming in its embedded interpreter. This provides for binaries functionalities similar to those provided via reflection on languages like Java.

Example usage of wsh The following command loads the /usr/sbin/apache2 executable within wsh, calls the ap_get_server_banner() function within apache to retrieve its banner and displays it within the wsh interpreter.

jonathan@blackbox:~$ wsh /usr/sbin/apache2
> a = ap_get_server_banner()
> print(a)
Apache/2.4.7
Alex D
  • 731