Como executar comandos da biblioteca a partir do shell?

25

Desculpe por eu parecer estúpido fazendo esta pergunta, mas estou muito curioso para saber sobre isso.

Eu queria simplesmente calcular o comprimento de uma string (que é o valor de hash). Então, abri o terminal e fiz isso:

$ apropos length

que me retornou com um monte de comandos / funções com (3) ou (3ssl) acrescentados no final deles. Agora o homem nos fornece informações sobre o que esses section numbers significam.

3   Library calls (functions within program libraries)

Por curiosidade, eu apenas tentei com todos esses comandos (na esperança de que pelo menos um funcionasse)

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

e não tem nada além do mesmo erro para cada comando

$ strnlen HelloWorld 
$ strnlen: command not found

Bem, eu sei como encontrar o tamanho da string no shell usando wc -m , expr length e outras soluções alternativas.

Mas tenho 2 perguntas aqui:

  1. Como usar qualquer library calls (3) dentro do shell?
  2. Como calcular o comprimento da string usando apenas as chamadas da biblioteca e não outros comandos?

NOTA: A questão se concentra em library calls e seu uso no shell. Isso faz com que a primeira pergunta seja mais importante para responder.

    
por C0deDaedalus 08.04.2018 / 10:15

4 respostas

38

Você provavelmente não deveria fazer isso, mas você pode. A resposta de Kusalananda é melhor para a tarefa e explica a questão. Desde que você perguntou especificamente como usar qualquer chamadas de biblioteca dentro do terminal, aqui estão algumas maneiras ...

O Compilador C Minúsculo ( tcc ) suporta uma -run bandeira que permite (na verdade) interpretar código C por escrevendo um programa pequeno, para que você possa usar qualquer chamada de biblioteca dentro do terminal através de uma única invocação disso.

Você pode executar a função strnlen assim:

$ 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

Isso usa substituição de processos de Bash, zsh e outros shells para dê tcc um arquivo para ler que parece conter os resultados de todos os echo s; existem outras opções.

Você poderia criar uma função para gerar isso para você:

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

(Eu usei strlen aqui para que seja uma função unária string- > int - você precisaria de uma função separada para cada aridade e tipo de retorno diferentes).

Outra opção é o plug-in ctypes.sh Bash de Tavis Ormandy, que agrupa dlopen e dlsym . Esta é provavelmente a aproximação mais próxima do que você estava tentando. Você pode usar, por exemplo:

$ dlcall -r uint64 strlen "hello world"

e ele chamará a função como esperado.

Esta é a maneira mais direta de fazê-lo "a partir do terminal", mas é improvável que seja algo que sua distribuição empacote, então você teria que instalá-lo manualmente (o que não é trivial). Aqui estão algumas citações informativas do próprio ctypes.sh . website para dar uma impressão geral de como as pessoas se sentem ao fazer isso:

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

Pode haver ferramentas semelhantes para outras shells, mas eu não sei sobre elas. Em teoria, não há razão para que não haja um comando autônomo que faça isso exatamente para os casos simples, mas estou um pouco surpreso por não ter conseguido encontrar um ...

... então eu fiz um! dlcall permite que você chame funções de biblioteca a partir da linha de comando :

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

Ele suporta um conjunto limitado de protótipos de função, não é muito confiável ou resiliente atualmente, mas agora existe.

Você também pode usar, por exemplo, Python e python -c 'import ctypes; import sys; print(ctypes.cdll.LoadLibrary("libc.so.6").strlen(" ".join(sys.argv[1:])))' hello world , mas Certamente não é a maneira mais fácil de fazer isso. Perl, Ruby e outros idiomas têm recursos similares que você pode usar.

Então as respostas para as suas perguntas são:

  1. Use uma das abordagens acima.
  2. Você faz precisa usar outro comando para fazer o bootstrap na biblioteca, ou um pedaço de software que se conecte ao seu shell.

Em suma, você certamente estará melhor fazendo isso de outra maneira.

    
por 08.04.2018 / 10:54
29

O comando apropos é útil de várias maneiras, mas também oferece muito "lixo". A maioria das coisas que você lista são rotinas da biblioteca C (é para isso que a seção 3 do manual serve), que você não pode usar diretamente do shell.

Para usá-los, você teria que escrever um programa em C que os chamasse. Isso está fora do intervalo de tópicos abrangidos por este site específico (ele estaria no tópico em StackOverflow ).

Essas são as respostas para suas perguntas:

  1. Você não pode, elas são rotinas da biblioteca C.
  2. Você não pode, a menos que você escreva um programa em C.

Eu sei que você sabe disso, mas por questões de integridade: no shell, se você tiver uma string em uma variável string , você pode fazer

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

Isso imprimirá Length of string "hello world" is 11 no terminal onde o 11 vem de ${#string} , que se expande para o tamanho da string em $string .

Internamente, o shell pode estar usando uma das chamadas da biblioteca que você listou para fazer seu cálculo de comprimento.

Esta é a maneira mais eficiente de obter o tamanho de uma string armazenada em uma variável shell, no shell.

Note também que ${#string} é uma expansão de parâmetro de shell POSIX , portanto é portável entre todos os shells que reivindicar qualquer grau de conformidade POSIX.

    
por 08.04.2018 / 10:22
15

Eu não faria isso apenas por strlen() , mas é um truque útil para experimentar códigos C às vezes.

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

Aqui gdb é o depurador GNU, e normalmente seria necessário um nome de programa para depurá-lo depois. Porque nós não temos nenhum, este exemplo dá a si mesmo para depurar. Então start inicia o programa, e depois disso gdb pode ser usado para executar códigos arbitrários em C.

    
por 08.04.2018 / 18:12
4

Uma ferramenta que pode ser usada para chamar interativamente funções em bibliotecas compartilhadas: a "Coleção de Compiladores de Bruxaria". link

Na página do GitHub:

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
    
por 09.04.2018 / 17:03