¿Cómo ejecutar comandos de biblioteca desde el shell?

27

Simplemente quería calcular la longitud de una cadena (que es el valor hash). Entonces, abrí la terminal e hice esto:

$ apropos length

eso me devolvió con un montón de comandos / funciones que tienen (3)o (3ssl)anexados al final de ellos. Ahora hombre hombre nos da información sobre lo que section numberssignifican.

3   Library calls (functions within program libraries)

Por curiosidad, acabo de probar con todos estos comandos (con la esperanza de que al menos uno funcione)

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

y obtuve nada más que el mismo error para cada comando

$ strnlen HelloWorld 
$ strnlen: command not found

Bueno, sé cómo encontrar la longitud de la cadena en la cáscara utilizando wc -m, expr lengthy otras soluciones.

Pero, tengo 2 preguntas aquí:

  1. ¿Cómo usar algunolibrary calls (3) dentro del caparazón?
  2. ¿Cómo calcular la longitud de la cadena usando solo llamadas de la biblioteca y no otros comandos?

NOTA: La pregunta se centra en general library callsy su uso en el shell. Eso hace que la primera pregunta sea más importante de responder.

C0deDaedalus
fuente
stackoverflow.com/q/49719554/841108 es casi un duplicado
Basile Starynkevitch
44
Básicamente, aunque la respuesta de Michael es un gran truco, la respuesta directa es: no. Las llamadas a la biblioteca no están relacionadas con el shell. Se utilizan para escribir programas en lenguaje C. - En términos más generales, al leer páginas de manual, a menos que esté codificando un programa, simplemente ignore las secciones 2, 3 y 9.
espectras
Fuera del tema, pero OpenVMS tiene funciones "léxicas" que son interfaces de shell para una gran cantidad de funciones de la biblioteca del sistema.
RonJohn

Respuestas:

39

Probablemente no deberías hacer esto, pero puedes. La respuesta de Kusalananda es mejor para la tarea en cuestión y explica el problema. Sin embargo, dado que usted preguntó específicamente cómo usar las llamadas a la biblioteca dentro del terminal, aquí hay algunas maneras ...


El Compilador Tiny C ( tcc) admite un -runindicador que le permite (en efecto) interpretar el código C escribiendo un pequeño programa, para que pueda usar cualquier llamada a la biblioteca dentro del terminal a través de una sola invocación de eso.

Puede ejecutar la strnlenfunción de esta manera:

$ 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

Esto utiliza la sustitución de procesos de Bash, zsh y otros shells para proporcionar tccun archivo para leer que parece contener los resultados de todos los echos; Hay otras opciones.

Podría hacer una función para generar esto para usted:

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

(Lo he usado strlenaquí para que sea una función unaria string-> int: necesitaría una función separada para cada arity y tipo de retorno diferentes).


Otra opción es el ctypes.shcomplemento Bash de Tavis Ormandy, que finaliza dlopeny dlsym. Esta es probablemente la aproximación más cercana a lo que estaba intentando. Puedes usar, por ejemplo:

$ dlcall -r uint64 strlen "hello world"

y llamará a la función como se esperaba.

Esta es la forma más directa de hacerlo "desde el terminal", pero es poco probable que sea algo que su distribución empaquete, por lo que tendría que instalarlo manualmente (lo cual no es trivial). Aquí hay algunas citasctypes.sh informativas del propio sitio web para dar una impresión general de cómo se siente la gente al hacer esto:

  • "eso es asqueroso"
  • "Esto tiene que parar"
  • "has ido demasiado lejos con esto"

Puede haber herramientas similares para otros proyectiles, pero no los conozco. En teoría, no hay razón para que no haya un comando independiente que haga esto exactamente para los casos simples, pero estoy algo sorprendido de que no haya podido encontrar uno ...


... así que hice uno! dlcallle permite llamar a funciones de biblioteca desde la línea de comandos :

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

Admite un conjunto limitado de prototipos de funciones, actualmente no es terriblemente confiable ni resistente, pero ahora existe.


También podría usar, por ejemplo, Python ypython -c 'import ctypes; import sys; print(ctypes.cdll.LoadLibrary("libc.so.6").strlen(" ".join(sys.argv[1:])))' hello world , pero ciertamente no es la forma más fácil de hacerlo. Perl, Ruby y otros idiomas tienen características similares que podrías usar.


Entonces las respuestas a sus preguntas son:

  1. Use uno de los enfoques anteriores.
  2. Usted no necesita usar otro comando para que arrancar en la biblioteca, o una pieza de software que los ganchos en su concha.

Con todo, seguramente será mejor que hagas esto de otra manera.

Michael Homer
fuente
2
"dlcall" me recuerda a (no del todo idéntico) Windows "rundll": support.microsoft.com/en-gb/help/164787/…
pjc50
1
@ pjc50: Anuncio de servicio público: rundll32 no es una herramienta de propósito general para llamar a funciones arbitrarias . Solo se puede usar para llamar a funciones con una firma determinada. Además, no es terriblemente a prueba de futuro .
Kevin
29

El aproposcomando es útil de muchas maneras, pero también le da mucha "basura". La mayoría de las cosas que enumera son rutinas de biblioteca C (para esto es para la sección 3 del manual), que no puede usar directamente desde el shell.

Para usarlos, tendría que escribir un programa en C que los llame. Esto queda fuera del rango de temas cubiertos por este sitio en particular (sería sobre el tema en StackOverflow ).

Estas, por lo tanto, son las respuestas a sus preguntas:

  1. No puedes, son rutinas de biblioteca C.
  2. No puede, a menos que escriba un programa en C.

Sé que sabes esto, pero por razones de integridad: en el shell, si tienes una cadena en una variable string, puedes hacer

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

Esto se imprimirá Length of string "hello world" is 11en el terminal de donde 11viene el ${#string}que se expande a la longitud de la cadena $string.

Internamente, el shell puede estar usando una de las llamadas de la biblioteca que usted enumeró para hacer su cálculo de longitud.

Esta es la forma más eficiente de obtener la longitud de una cadena que se almacena en una variable de shell, en el shell.

Tenga en cuenta también que ${#string}es una expansión de parámetros de shell POSIX , por lo tanto, es portátil entre todos los shells que afirman cualquier grado de cumplimiento POSIX.

Kusalananda
fuente
Las partes sobre las funciones de la biblioteca son una buena explicación, pero el resto de esta respuesta es un poco engañosa. OP dice "Simplemente quería calcular la longitud de una cadena" No hay necesidad de usar printfaquí. Si desea imprimirlo como parte de la oración, entonces esa podría ser la mejor manera. Pero la respuesta a la pregunta "¿cómo obtengo la longitud de una cuerda?" es la ${#string}parte, no la printf. Puede echo ${#string}hacerlo solo si desea generar solo la longitud, o asignarlo a otra variable con string_length=${#string}o lo que desee. printf no es realmente relevante
Adam
1
@ Adam, he hecho pequeñas modificaciones a la respuesta. Creo que está bastante claro cómo obtener la longitud de la cadena y el usuario ya ha dicho que sabe cómo hacerlo. Al usar la longitud de la cadena con printf, agrego algo más que solo decir "usar ${#string}" (que es evidente por el ejemplo).
Kusalananda
15

No haría esto por solo strlen(), pero a veces es un truco útil para probar el código C.

usuario @ host: ~ $ gdb gdb
(gdb) inicio
Punto de interrupción temporal 1, ... en main ()
(gdb) print strlen ("foobar")
$ 1 = 6

Aquí gdbestá el depurador GNU, y normalmente se necesitaría un nombre de programa para depurarlo. Como no tenemos ninguno, este ejemplo lo da a sí mismo para depurar. Luego startinicia el programa, y ​​luego gdbpuede usarse para ejecutar código C arbitrario.

jpa
fuente
2
Buen truco, usar gdb. Sin embargo, gdb es parte de las herramientas de desarrollo, y si desea usar la función de biblioteca, es mejor que se prepare para aprender a usar herramientas de desarrollador.
Dudi Boy
4

Una herramienta que se puede usar para llamar interactivamente funciones en bibliotecas compartidas: la "Colección del compilador de brujería". https://github.com/endrazine/wcc

Desde la página de GitHub:

wsh: el caparazón de brujería

El shell de brujería acepta bibliotecas compartidas ELF, ejecutables ELF ET_DYN y secuencias de comandos Witchcraft Shell escritas en Punk-C como entrada. Carga todos los ejecutables en su propio espacio de direcciones y hace que su API esté disponible para la programación en su intérprete incorporado. Esto proporciona funcionalidades binarias similares a las proporcionadas a través de la reflexión en lenguajes como Java.

Ejemplo de uso de wsh El siguiente comando carga el /usr/sbin/apache2ejecutable dentro de wsh, llama a la ap_get_server_banner()función dentro de apache para recuperar su banner y lo muestra dentro del wshintérprete.

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