Como executar comandos de biblioteca a partir do shell?

27

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

$ apropos length

que me devolveu um monte de comandos / funções tendo (3)ou (3ssl)acrescentado no final deles. Agora, homem, homem, nos fornece informações sobre o que isso section numberssignifica.

3   Library calls (functions within program libraries)

Por curiosidade, 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 obteve nada além do mesmo erro para cada comando

$ strnlen HelloWorld 
$ strnlen: command not found

Bem, eu sei como encontrar o comprimento de corda em shell usando wc -m, expr lengthe outras soluções alternativas.

Mas eu tenho 2 perguntas aqui:

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

NOTA: A pergunta se concentra em geral library callse seu uso no shell. Isso torna a primeira pergunta mais importante para responder.

C0deDaedalus
fonte
stackoverflow.com/q/49719554/841108 é uma duplicata próxima
Basile Starynkevitch
4
Basicamente, embora a resposta de Michael seja um grande truque, a resposta direta é: você não precisa. As chamadas de biblioteca não estão relacionadas ao shell. Eles são usados ​​para escrever programas em linguagem C. - De modo mais geral, ao ler as páginas de manual, a menos que você está programando um programa, simplesmente ignorar seções 2, 3 e 9.
espectros
Fora do tópico, mas o OpenVMS possui funções "lexicais", que são interfaces de shell para muitas funções da biblioteca do sistema.
RonJohn

Respostas:

39

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


O Tiny C Compiler ( tcc) suporta um -runsinalizador que permite (com efeito) interpretar o código C escrevendo um pequeno programa, para que você possa usar qualquer chamada de biblioteca dentro do terminal através de uma única chamada.

Você pode executar a strnlenfunção 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 a substituição de processo do Bash, zsh e outros shells para fornecer tccum arquivo para ler que parece conter os resultados de todos os echos; existem outras opções.

Você pode 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 strlenaqui para que seja uma função unária string-> int - você precisaria de uma função separada para cada tipo de retorno e aridade diferente).


Outra opção é o ctypes.shplugin Bash de Tavis Ormandy, que encerra dlopene 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 chamará a função conforme o esperado.

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

  • "isso é nojento"
  • "Isto tem que parar"
  • "você foi longe demais com isso"

Pode haver ferramentas semelhantes para outras conchas, mas eu não as conheço. Em teoria, não há razão para que não possa haver um comando independente que faça isso exatamente nos casos simples, mas estou um pouco surpreso por não ter conseguido encontrar um ...


... então eu fiz um! dlcallpermite chamar funções de biblioteca na 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, atualmente não é muito confiável ou resistente, mas agora existe.


Você também pode usar, por exemplo, Python epython -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 semelhantes que você pode usar.


Portanto, as respostas para suas perguntas são:

  1. Use uma das abordagens acima.
  2. Você não precisa usar um outro comando para inicializar-lo para a biblioteca, ou um pedaço de software que ganchos em seu shell.

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

Michael Homer
fonte
2
"dlcall" me lembra (não completamente idênticos) Windows "rundll": support.microsoft.com/en-gb/help/164787/...
pjc50
11
@ pjc50: Anúncio de serviço público: rundll32 não é uma ferramenta de uso geral para chamar funções arbitrárias . Só pode ser usado para chamar funções com uma determinada assinatura. Além disso, não é terrivelmente à prova de futuro .
Kevin
29

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

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

Estas são, portanto, 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 escreva um programa em C.

Eu sei que você sabe disso, mas por uma questão de integridade: No shell, se você tiver uma string em uma variável string, poderá fazer

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

Isso será impresso Length of string "hello world" is 11no terminal de onde 11vem a ${#string}qual se expande para o comprimento da string $string.

Internamente, o shell pode muito bem estar usando uma das chamadas de biblioteca listadas para fazer o cálculo do comprimento.

Essa é a maneira mais eficiente de obter o comprimento de uma string armazenada em uma variável do shell, no shell.

Observe também que ${#string}é uma expansão de parâmetro do shell POSIX , portanto é portátil entre todos os shells que reivindicam qualquer grau de conformidade com o POSIX.

Kusalananda
fonte
As partes sobre as funções da biblioteca são uma boa explicação, mas o restante desta resposta é um pouco enganador. O OP diz "Eu queria simplesmente calcular o comprimento de uma string" Não há necessidade de usar printfaqui. Se você quiser imprimi-lo como parte da frase, essa pode ser a melhor maneira. Mas a resposta para a pergunta "como obtenho o comprimento de uma string?" é a ${#string}parte, não a printf. Você pode fazer isso apenas echo ${#string}se quiser apenas produzir o comprimento ou atribuí-lo a outra variável com string_length=${#string}ou o que quiser. printf não é realmente relevante
Adam
11
@ Adam Fiz pequenas modificações na resposta. Eu acho que é bem claro como obter o comprimento da string e o usuário já disse que sabe como fazê-lo. Ao usar o comprimento da corda com printfque eu adicionar algo diferente do que simplesmente dizer "use ${#string}" (que é evidente a partir do exemplo).
Kusalananda
15

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

usuário @ host: ~ $ gdb gdb
(gdb) start
Ponto de interrupção temporário 1, ... no principal ()
(gdb) print strlen ("foobar")
$ 1 = 6

Aqui gdbestá o depurador GNU, e normalmente seria necessário um nome de programa para depurar depois dele. Como não temos nenhum, este exemplo se dedica a depurar. Em seguida, startinicia o programa e, depois disso, gdbpode ser usado para executar código C arbitrário.

jpa
fonte
2
Bom truque, para usar o gdb. No entanto, o gdb faz parte das ferramentas do desenvolvedor e, se você deseja usar a função de biblioteca, é melhor se preparar para aprender a usar as ferramentas do desenvolvedor.
Dudi Boy
4

Uma ferramenta que pode ser usada para chamar interativamente funções em bibliotecas compartilhadas: a "Coleção Witcher Compiler". https://github.com/endrazine/wcc

Na página do GitHub:

wsh: A concha de bruxaria

O shell de bruxaria aceita bibliotecas compartilhadas ELF, executáveis ​​ELF ET_DYN e scripts de shell de bruxaria escritos em Punk-C como entrada. Carrega todos os executáveis ​​em seu próprio espaço de endereço e disponibiliza sua API para programação em seu intérprete incorporado. Isso fornece funcionalidades binárias semelhantes às fornecidas por meio de reflexão em linguagens como Java.

Exemplo de uso do wsh O comando a seguir carrega o /usr/sbin/apache2executável no wsh, chama a ap_get_server_banner()função no apache para recuperar seu banner e o exibe no wshintérprete.

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