Wie führe ich Bibliotheksbefehle aus der Shell aus?

27

Ich wollte einfach die Länge eines Strings berechnen (das ist ein Hash-Wert). Also öffnete ich das Terminal und tat dies:

$ apropos length

das zurückgegeben mich mit einem Bündel von Befehlen / Funktionen haben , (3)oder (3ssl)am Ende von ihnen angehängt. Nun gibt uns der Mensch Auskunft darüber, was diese section numbersbedeuten.

3   Library calls (functions within program libraries)

Aus Neugier habe ich es nur mit all diesen Befehlen versucht (in der Hoffnung, dass mindestens einer funktionieren würde)

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

und bekam nichts als den gleichen Fehler für jeden Befehl

$ strnlen HelloWorld 
$ strnlen: command not found

Nun, ich weiß , wie die Länge der Zeichenfolge in der Schale finden verwenden wc -m, expr lengthund anderen Abhilfen.

Aber ich habe hier 2 Fragen:

  1. Wie benutzt man jedelibrary calls (3) im Inneren der Schale?
  2. Wie berechnet man die Länge eines Strings nur mit Bibliotheksaufrufen und nicht mit anderen Befehlen?

HINWEIS: Fragenschwerpunkte im Allgemeinen library callsund deren Verwendung in der Shell. Das macht die Beantwortung der ersten Frage wichtiger.

C0deDaedalus
quelle
stackoverflow.com/q/49719554/841108 ist ein fast doppeltes
Basile Starynkevitch
4
Obwohl Michaels Antwort ein großartiger Hack ist, lautet die direkte Antwort: Sie tun es nicht. Bibliotheksaufrufe beziehen sich nicht auf die Shell. Sie werden zum Schreiben von Programmen in C-Sprache verwendet. - Im Allgemeinen ignorieren Sie beim Lesen von Manpages die Abschnitte 2, 3 und 9, es sei denn, Sie codieren ein Programm.
spectras
Off Topic, aber OpenVMS hat "lexikalische" Funktionen, die Shell-Schnittstellen zu vielen Funktionen der Systembibliothek sind.
RonJohn

Antworten:

39

Sie sollten das wahrscheinlich nicht tun, aber Sie können. Kusalanandas Antwort ist besser für die anstehende Aufgabe und erklärt das Problem. Da Sie genau haben fragen , wie zu verwenden , alle innerhalb des Terminals Bibliothek Anrufe, aber hier ein paar Möglichkeiten , ...


Der Tiny C Compiler ( tcc) unterstützt eine -runFlagge , die Sie (in der Tat) interpretieren C - Code kann durch ein kleines Programm zu schreiben, so dass Sie kann keine Bibliothek Anrufe innerhalb des Terminals durch einen einzigen Aufruf verwendet diese .

Sie können die strnlenFunktion folgendermaßen ausführen :

$ 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

Hierbei wird die Prozessersetzung aus Bash, zsh und anderen Shells verwendet, um tcceine zu lesende Datei zu erstellen, die die Ergebnisse aller echos zu enthalten scheint . es gibt andere Möglichkeiten.

Sie könnten eine Funktion erstellen, um dies für Sie zu generieren:

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

(Ich habe strlenhier verwendet , damit es eine unäre Funktionszeichenfolge ist-> int - Sie benötigen eine separate Funktion für jeden unterschiedlichen arity- und return-Typ.)


Eine weitere Option ist die ctypes.shBash - Plugin von Tavis Ormandy, die einpackt dlopenund dlsym. Dies ist wahrscheinlich die beste Annäherung an das, was Sie versucht haben. Sie können zum Beispiel verwenden:

$ dlcall -r uint64 strlen "hello world"

und es wird die Funktion wie erwartet aufrufen.

Dies ist der direkteste Weg, dies "vom Terminal aus" zu tun, aber es ist unwahrscheinlich, dass Ihre Distributionspakete installiert werden, sodass Sie es manuell installieren müssen (was nicht trivial ist). Hier sind einige informative Zitate von ctypes.shder eigenen Website , um einen allgemeinen Eindruck davon zu vermitteln, wie sich die Leute dabei fühlen:

  • "das ist wiederlich"
  • "das muss aufhören"
  • "du bist damit zu weit gegangen"

Es mag ähnliche Werkzeuge für andere Shells geben, aber ich weiß nichts darüber. Theoretisch gibt es keinen Grund, warum es keinen eigenständigen Befehl geben könnte, der dies genau für die einfachen Fälle ausführt, aber ich bin etwas überrascht, dass ich keinen finden konnte ...


... also habe ich eins gemacht! dlcallErmöglicht den Aufruf von Bibliotheksfunktionen über die Befehlszeile :

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

Es unterstützt eine begrenzte Anzahl von Funktionsprototypen, ist derzeit nicht besonders zuverlässig und belastbar, existiert aber jetzt.


Sie könnten zum Beispiel auch Python und verwendenpython -c 'import ctypes; import sys; print(ctypes.cdll.LoadLibrary("libc.so.6").strlen(" ".join(sys.argv[1:])))' hello world , aber es ist sicherlich nicht der einfachste Weg, dies zu tun. Perl, Ruby und andere Sprachen verfügen über ähnliche Funktionen, die Sie verwenden können.


Die Antworten auf Ihre Fragen lauten also:

  1. Verwenden Sie einen der oben genannten Ansätze.
  2. Sie müssen einen anderen Befehl verwenden, um in die Bibliothek zu booten, oder eine Software, die sich in Ihre Shell einfügt.

Alles in allem sind Sie mit ziemlicher Sicherheit besser dran, wenn Sie dies auf andere Weise tun.

Michael Homer
quelle
2
"dlcall" erinnert mich an (nicht ganz identisches) Windows "rundll": support.microsoft.com/en-gb/help/164787/…
pjc50
1
@ pjc50: Öffentlicher Dienst Ankündigung: rundll32 ist kein Allzweckwerkzeug zum Aufrufen beliebiger Funktionen . Es kann nur zum Aufrufen von Funktionen mit einer bestimmten Signatur verwendet werden. Außerdem ist es nicht besonders zukunftssicher .
Kevin
29

Der aproposBefehl ist in vielerlei Hinsicht nützlich, aber es gibt Ihnen auch viel "Müll". Die meisten Dinge, die Sie auflisten, sind C-Bibliotheksroutinen (dazu dient Abschnitt 3 des Handbuchs), die Sie nicht direkt über die Shell verwenden können.

Um sie zu verwenden, müssten Sie ein C-Programm schreiben, das sie aufruft. Dies liegt außerhalb des Themenspektrums, das von dieser speziellen Site abgedeckt wird (es würde sich um ein Thema bei StackOverflow handeln ).

Dies sind also die Antworten auf Ihre Fragen:

  1. Sie können nicht, es sind C-Bibliotheksroutinen.
  2. Sie können nicht, es sei denn, Sie schreiben ein C-Programm.

Ich weiß, dass Sie das wissen, aber der Vollständigkeit halber: Wenn Sie in der Shell eine Zeichenfolge in einer Variablen haben string, können Sie dies tun

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

Dies wird Length of string "hello world" is 11in dem Terminal gedruckt, in dem das 11kommt, von ${#string}dem die Länge der Zeichenfolge in erweitert wird $string.

Intern verwendet die Shell möglicherweise einen der von Ihnen aufgelisteten Bibliotheksaufrufe, um ihre Längenberechnung durchzuführen.

Dies ist die effizienteste Methode, um die Länge einer Zeichenfolge abzurufen, die in einer Shell-Variablen in der Shell gespeichert ist.

Beachten Sie auch, dass ${#string}es sich um eine POSIX-Shell-Parametererweiterung handelt. Sie kann daher zwischen allen Shells übertragen werden, die einen beliebigen Grad an POSIX-Konformität beanspruchen.

Kusalananda
quelle
Die Teile über die Bibliotheksfunktionen sind eine gute Erklärung, aber der Rest dieser Antwort ist etwas irreführend. OP sagt: "Ich wollte einfach die Länge einer Zeichenkette berechnen." Hier muss nichts verwendet printfwerden. Wenn Sie es als Teil eines Satzes ausdrucken möchten, ist dies möglicherweise der beste Weg. Aber die Antwort auf die Frage "Wie bekomme ich die Länge einer Zeichenfolge?" ist das ${#string}Teil, nicht das printf. Sie können nur, echo ${#string}wenn Sie nur die Länge ausgeben möchten, oder es mit einer anderen Variablen zuweisen string_length=${#string}oder was auch immer Sie möchten. printf ist nicht wirklich relevant
Adam
1
@Adam Ich habe kleine Änderungen an der Antwort vorgenommen. Ich denke, es ist ziemlich klar, wie man die Länge der Zeichenkette erhält, und der Benutzer hat bereits gesagt, dass er weiß, wie man es macht. Indem ich die Länge der Zeichenkette mit benutze,printf füge ich etwas anderes hinzu, als nur "use ${#string}" zu sagen (was aus dem Beispiel klar hervorgeht).
Kusalananda
15

Ich würde das nicht einfach so machen strlen(), aber es ist manchmal ein nützlicher Trick, um C-Code auszuprobieren.

user @ host: ~ $ gdb gdb
(gdb) starten
Temporärer Haltepunkt 1, ... in main ()
(gdb) print strlen ("foobar")
$ 1 = 6

Hier gdbist der GNU-Debugger, nach dem normalerweise ein Programmname zum Debuggen benötigt wird. Da wir keine haben, gibt sich dieses Beispiel zum Debuggen. Dann startwird das Programm gestartet und danach gdbverwendet werden kann beliebigen C - Code auszuführen.

jpa
quelle
2
Netter Trick, um gdb zu benutzen. Gdb ist jedoch Teil von Entwicklertools. Wenn Sie die Bibliotheksfunktion verwenden möchten, sollten Sie sich besser auf den Umgang mit Entwicklertools vorbereiten.
Dudi Boy
4

Ein Tool, mit dem Funktionen in gemeinsam genutzten Bibliotheken interaktiv aufgerufen werden können: die "Witchcraft Compiler Collection". https://github.com/endrazine/wcc

Von der GitHub Seite:

wsh: Die Witchcraft-Shell

Die Hexen-Shell akzeptiert gemeinsam genutzte ELF-Bibliotheken, ausführbare ELF-ET_DYN-Dateien und in Punk-C geschriebene Hexen-Shell-Skripte als Eingabe. Es lädt alle ausführbaren Dateien in seinen eigenen Adressraum und stellt ihre API für die Programmierung in seinem eingebetteten Interpreter zur Verfügung. Dies bietet Binärfunktionen, die denen ähneln, die durch Reflektion in Sprachen wie Java bereitgestellt werden.

Beispiel für die Verwendung von wsh Der folgende Befehl lädt die /usr/sbin/apache2ausführbare Datei in wsh, ruft die ap_get_server_banner()Funktion in apache auf, um das Banner abzurufen, und zeigt es im wshInterpreter an.

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