Comment exécuter des commandes de bibliothèque à partir du shell?

27

Je voulais simplement calculer la longueur d'une chaîne (c'est-à-dire la valeur de hachage). J'ai donc ouvert le terminal et j'ai fait ceci:

$ apropos length

qui m'a renvoyé un tas de commandes / fonctions ayant (3)ou (3ssl)ajoutées à la fin d'entre eux. Maintenant, l' homme, l'homme nous donne des informations sur leur section numberssignification.

3   Library calls (functions within program libraries)

Par curiosité, j'ai juste essayé avec toutes ces commandes (en espérant qu'au moins une fonctionnerait)

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

et n'a obtenu que la même erreur pour chaque commande

$ strnlen HelloWorld 
$ strnlen: command not found

Eh bien, je sais comment trouver la longueur de chaîne dans shell en utilisant wc -m, expr lengthet d'autres solutions.

Mais, j'ai 2 questions ici:

  1. Comment utiliser toutelibrary calls (3) l' intérieur de la coquille?
  2. Comment calculer la longueur d'une chaîne en utilisant uniquement des appels de bibliothèque et non d'autres commandes?

REMARQUE: la question se concentre en général library callset leur utilisation dans le shell. Cela rend la première question plus importante à répondre.

C0deDaedalus
la source
stackoverflow.com/q/49719554/841108 est presque un doublon
Basile Starynkevitch
4
Fondamentalement, bien que la réponse de Michael soit un excellent hack, la réponse simple est: vous ne le faites pas. Les appels de bibliothèque ne sont pas liés au shell. Ils sont utilisés pour écrire des programmes en langage C. - Plus généralement, lorsque vous lisez des pages de manuel, à moins que vous ne codiez un programme, ignorez simplement les sections 2, 3 et 9.
spectras
Hors sujet, mais OpenVMS a des fonctions "lexicales" qui sont des interfaces shell avec de nombreuses fonctions de bibliothèque système.
RonJohn

Réponses:

39

Vous ne devriez probablement pas faire cela, mais vous le pouvez. La réponse de Kusalananda est meilleure pour la tâche à accomplir et explique le problème. Puisque vous avez demandé spécifiquement comment utiliser les appels de bibliothèque à l'intérieur du terminal, voici quelques façons ...


Le Tiny C Compiler ( tcc) prend en charge un -runindicateur qui vous permet (en effet) d'interpréter le code C en écrivant un petit programme, de sorte que vous pouvez utiliser tous les appels de bibliothèque à l'intérieur du terminal via une seule invocation de cela.

Vous pouvez exécuter la strnlenfonction comme ceci:

$ 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

Cela utilise la substitution de processus à partir de Bash, zsh et d'autres shells pour donner tccun fichier à lire qui semble contenir les résultats de tous les echos; il existe d'autres options.

Vous pouvez créer une fonction pour générer ceci pour vous:

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

(Je l'ai utilisé strlenici pour qu'il s'agisse d'une fonction unaire string-> int - vous auriez besoin d'une fonction distincte pour chaque type d'arité et de retour différent).


Une autre option est le ctypes.shplugin Bash de Tavis Ormandy, qui se termine dlopenet dlsym. C'est probablement l'approximation la plus proche de ce que vous essayiez. Vous pouvez utiliser, par exemple:

$ dlcall -r uint64 strlen "hello world"

et il appellera la fonction comme prévu.

C'est le moyen le plus direct de le faire "à partir du terminal", mais il est peu probable que vos packages de distribution soient installés, vous devrez donc l'installer manuellement (ce qui n'est pas trivial). Voici quelques citationsctypes.sh informatives du propre site Web de pour donner une impression générale de la façon dont les gens se sentent à ce sujet:

  • "c'est dégoutant"
  • "cela doit s'arrêter"
  • "tu es allé trop loin avec ça"

Il peut y avoir des outils similaires pour d'autres obus, mais je ne les connais pas. En théorie, il n'y a aucune raison qu'il ne puisse pas y avoir de commande autonome qui fasse exactement cela pour les cas simples, mais je suis quelque peu surpris de ne pas avoir pu en trouver un ...


... alors j'en ai fait un! dlcallvous permet d'appeler des fonctions de bibliothèque à partir de la ligne de commande :

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

Il prend en charge un ensemble limité de prototypes de fonctions, il n'est pas terriblement fiable ou résilient actuellement, mais il existe maintenant.


Vous pouvez également utiliser, par exemple, Python etpython -c 'import ctypes; import sys; print(ctypes.cdll.LoadLibrary("libc.so.6").strlen(" ".join(sys.argv[1:])))' hello world , mais ce n'est certainement pas la manière la plus simple de s'y prendre. Perl, Ruby et d'autres langages ont des fonctionnalités similaires que vous pourriez utiliser.


Les réponses à vos questions sont donc:

  1. Utilisez l'une des approches ci-dessus.
  2. Vous avez besoin d'utiliser une autre commande pour vous bootstrap dans la bibliothèque, ou un morceau de logiciel qui crochets dans votre coquille.

Dans l'ensemble, vous ferez certainement mieux de faire cela d'une autre manière.

Michael Homer
la source
2
"dlcall" me rappelle (pas tout à fait identique) Windows "rundll": support.microsoft.com/en-gb/help/164787/…
pjc50
1
@ pjc50: Annonce de service public: rundll32 n'est pas un outil à usage général pour appeler des fonctions arbitraires . Il ne peut être utilisé que pour appeler des fonctions avec une signature donnée. De plus, ce n'est pas vraiment à l'épreuve du temps .
Kevin
29

La aproposcommande est utile à bien des égards, mais elle vous donne aussi beaucoup de "déchets". La plupart des choses que vous listez sont des routines de bibliothèque C (c'est à cela que sert la section 3 du manuel), que vous ne pouvez pas utiliser directement à partir du shell.

Pour les utiliser, vous devez écrire un programme C qui les appelle. Cela tombe en dehors de la gamme de sujets couverts par ce site particulier (ce serait sur le sujet à StackOverflow ).

Voici donc les réponses à vos questions:

  1. Vous ne pouvez pas, ce sont des routines de bibliothèque C.
  2. Vous ne pouvez pas, sauf si vous écrivez un programme C.

Je sais que vous le savez, mais pour être complet: dans le shell, si vous avez une chaîne dans une variable string, vous pouvez faire

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

Cela s'imprimera Length of string "hello world" is 11dans le terminal d'où 11provient le ${#string}qui s'étend jusqu'à la longueur de la chaîne $string.

En interne, le shell peut très bien utiliser l'un des appels de bibliothèque que vous avez répertoriés pour effectuer son calcul de longueur.

C'est le moyen le plus efficace pour obtenir la longueur d'une chaîne stockée dans une variable shell, dans le shell.

Notez également qu'il ${#string}s'agit d' une extension des paramètres du shell POSIX , il est donc portable entre tous les shells qui revendiquent un degré de conformité POSIX.

Kusalananda
la source
Les parties sur les fonctions de bibliothèque sont une bonne explication, mais le reste de cette réponse est un peu trompeur. OP dit "Je voulais simplement calculer la longueur d'une chaîne" Il n'est pas nécessaire d'utiliser printfici. Si vous souhaitez l'imprimer dans le cadre d'une phrase, cela pourrait être le meilleur moyen. Mais la réponse à la question "comment puis-je obtenir la longueur d'une chaîne?" est la ${#string}partie, pas le printf. Vous pouvez simplement echo ${#string}si vous voulez simplement afficher uniquement la longueur, ou l'affecter à une autre variable avec string_length=${#string}ou ce que vous voulez. printf n'est pas vraiment pertinent
Adam
1
@Adam J'ai apporté de petites modifications à la réponse. Je pense qu'il est assez clair comment obtenir la longueur de la chaîne et l'utilisateur a déjà dit qu'il savait comment le faire. En utilisant la longueur de la chaîne avec, printfj'ajoute autre chose que de simplement dire "utiliser ${#string}" (ce qui est évident dans l'exemple).
Kusalananda
15

Je ne le ferais pas juste strlen(), mais c'est une astuce utile pour essayer le code C parfois.

utilisateur @ hôte: ~ $ gdb gdb
(gdb) démarrer
Point d'arrêt temporaire 1, ... en main ()
(gdb) print strlen ("foobar")
1 $ = 6

Voici gdble débogueur GNU, et il faudrait normalement un nom de programme pour déboguer après. Parce que nous n'en avons pas, cet exemple le donne lui-même au débogage. Démarre ensuite startle programme et gdbpeut ensuite être utilisé pour exécuter du code C arbitraire.

jpa
la source
2
Belle astuce, pour utiliser gdb. Pourtant, gdb fait partie des outils de développement, et si vous voulez utiliser la fonction de bibliothèque, vous feriez mieux de vous préparer à apprendre à utiliser les outils de développement.
Dudi Boy
4

Un outil qui peut être utilisé pour appeler de manière interactive des fonctions dans des bibliothèques partagées: la "Witchcraft Compiler Collection". https://github.com/endrazine/wcc

Depuis la page GitHub:

wsh: la coquille de sorcellerie

Le shell de sorcellerie accepte les bibliothèques partagées ELF, les exécutables ELF ET_DYN et les scripts de shell Witchcraft écrits en Punk-C en entrée. Il charge tous les exécutables dans son propre espace d'adressage et rend leur API disponible pour la programmation dans son interpréteur intégré. Cela fournit des fonctionnalités binaires similaires à celles fournies via la réflexion sur des langages comme Java.

Exemple d'utilisation de wsh La commande suivante charge l' /usr/sbin/apache2exécutable dans wsh, appelle la ap_get_server_banner()fonction dans apache pour récupérer sa bannière et l'affiche dans l' wshinterpréteur.

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