Как выполнить библиотечные команды из оболочки?

27

Я хотел просто вычислить длину строки (это значение хеша). Итак, я открыл терминал и сделал это:

$ apropos length

это вернуло меня с кучей команд / функций, имеющих (3)или (3ssl)добавленных в конце их. Теперь человек мужчина дает нам информацию о том, что это section numbersзначит.

3   Library calls (functions within program libraries)

Из любопытства я просто попробовал все эти команды (в надежде, что хотя бы одна из них сработает)

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

и получил только одну и ту же ошибку для каждой команды

$ strnlen HelloWorld 
$ strnlen: command not found

Ну, я знаю , как найти длину строки в оболочке , используя wc -m, expr lengthи другие обходные пути.

Но у меня есть 2 вопроса здесь:

  1. Как использовать любойlibrary calls (3) внутри оболочки?
  2. Как рассчитать длину строки, используя только библиотечные вызовы, а не другие команды?

ПРИМЕЧАНИЕ: Вопрос в основном касается library callsи их использования в оболочке. Это делает первый вопрос более важным для ответа.

C0deDaedalus
источник
stackoverflow.com/q/49719554/841108 является почти дубликатом
Василий Старынкевич
4
По сути, хотя ответ Майкла - отличный взлом, прямой ответ - нет. Библиотечные вызовы не связаны с оболочкой. Они используются для написания программ на языке Си. - В общем, при чтении man-страниц, если вы не пишете код программы, просто игнорируйте разделы 2, 3 и 9.
спектры
Не по теме, но OpenVMS имеет «лексические» функции, которые являются интерфейсами оболочки для большого количества функций системной библиотеки.
RonJohn

Ответы:

39

Вы, вероятно, не должны этого делать, но можете. Ответ Кусалананды лучше для поставленной задачи и объясняет проблему. Поскольку вы действительно спросили, как использовать любые вызовы библиотеки внутри терминала, есть несколько способов ...


Tiny C Compiler ( tcc) поддерживает -runфлаг , который позволяет вам (в действительности) истолковать C код, написав небольшую программу, так что вы можете использовать библиотечные вызовы внутри терминала через один вызов этого.

Вы можете запустить strnlenфункцию следующим образом:

$ 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

При этом используется подстановка процессов из Bash, zsh и других оболочек, чтобы дать tccфайл для чтения, который, кажется, содержит результаты всех echos; есть другие варианты.

Вы можете сделать функцию для генерации этого для вас:

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

(Я использовал strlenздесь, чтобы это была унарная функция string-> int - вам понадобится отдельная функция для каждого отдельного арности и возвращаемого типа).


Другим вариантом является плагин Bash по Tavis Орманди, который оборачивает и . Это, вероятно, самое близкое приближение к тому, что вы пытались. Вы можете использовать, например:ctypes.shdlopendlsym

$ dlcall -r uint64 strlen "hello world"

и он будет вызывать функцию, как и ожидалось.

Это самый прямой способ сделать это «из терминала», но вряд ли это что-то, что входит в ваши дистрибутивные пакеты, поэтому вам придется установить его вручную (что нетривиально). Вот некоторые информативные цитаты ctypes.shс собственного веб-сайта, чтобы дать общее представление о том, как люди относятся к этому:

  • "это отвратительно"
  • "Это должно прекратиться"
  • "ты зашел слишком далеко с этим"

Для других оболочек могут быть подобные инструменты, но я не знаю о них. В теории нет причин, по которым не может быть отдельной команды, которая бы делала это именно для простых случаев, но я несколько удивлен, что не смог найти ее ...


... так что я сделал один! dlcallпозволяет вызывать библиотечные функции из командной строки :

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

Он поддерживает ограниченный набор прототипов функций, в настоящее время он не очень надежный или отказоустойчивый, но теперь он существует.


Вы также можете использовать, например, Python иpython -c 'import ctypes; import sys; print(ctypes.cdll.LoadLibrary("libc.so.6").strlen(" ".join(sys.argv[1:])))' hello world , но это, конечно, не самый простой способ сделать это. Perl, Ruby и другие языки имеют схожие функции, которые вы можете использовать.


Итак, ответы на ваши вопросы:

  1. Используйте один из подходов выше.
  2. Вы действительно должны использовать другую команду для начальной загрузки вам в библиотеку, или часть программного обеспечения , что крючки в вашу оболочку.

В общем, вам почти наверняка будет лучше сделать это любым другим способом.

Майкл Гомер
источник
2
«dlcall» напоминает мне (не совсем идентичный) Windows «rundll»: support.microsoft.com/en-gb/help/164787/…
pjc50
1
@ pjc50: Объявление о государственной службе: rundll32 не является универсальным инструментом для вызова произвольных функций . Он может использоваться только для вызова функций с заданной сигнатурой. Кроме того, это не очень перспективно на будущее .
Кевин
29

Команда aproposполезна во многих отношениях, но она также дает вам много «мусора». Большинство вещей, которые вы перечисляете, - это подпрограммы библиотеки C (для этого предназначен раздел 3 руководства), которые вы не можете использовать непосредственно из оболочки.

Чтобы использовать их, вам нужно написать программу на C, которая их вызывает. Это выходит за пределы диапазона тем, охватываемых этим конкретным сайтом (это будет по теме в StackOverflow ).

Таким образом, это ответы на ваши вопросы:

  1. Вы не можете, они подпрограммы библиотеки C.
  2. Вы не можете, если вы не пишете программу на Си.

Я знаю, что вы знаете это, но для полноты: в оболочке, если у вас есть строка в переменной string, вы можете сделать

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

Это напечатает Length of string "hello world" is 11в терминале, откуда 11приходит, ${#string}который расширяется до длины строки в $string.

Внутренне оболочка вполне может использовать один из перечисленных вами библиотечных вызовов для расчета длины.

Это наиболее эффективный способ получить длину строки, которая хранится в переменной оболочки, в оболочке.

Обратите внимание также , что ${#string}является расширительным параметром POSIX оболочки , поэтому переносит между всеми оболочками , которые утверждают , что какая - либо степень соответствия POSIX.

Кусалананда
источник
Части о функциях библиотеки - хорошее объяснение, но остальная часть этого ответа немного вводит в заблуждение. OP говорит: «Я хотел просто вычислить длину строки». Здесь нет необходимости использовать printf. Если вы хотите распечатать его как часть предложения, то это может быть лучшим способом. Но ответ на вопрос "как мне получить длину строки?" это ${#string}часть, а не printf. Вы можете просто, echo ${#string}если вы просто хотите вывести только длину, или назначить ее другой переменной с помощью string_length=${#string}или что угодно. printf не очень актуален
Адам
1
@ Adam Я внес небольшие изменения в ответ. Я думаю, что довольно ясно, как получить длину строки, и пользователь уже сказал, что знает, как это сделать. При использовании длины строки с printfя добавить что - то другое , чем просто сказать «использование ${#string}» (что само видно из примера).
Кусалананда
15

Я бы не стал делать это просто strlen(), но это полезный трюк для того, чтобы иногда попробовать C-код.

user @ host: ~ $ gdb gdb
(GDB) начать
Временная точка останова 1, ... в main ()
(gdb) print strlen ("foobar")
$ 1 = 6

Вот gdbотладчик GNU, и для его отладки обычно требуется имя программы. Поскольку у нас его нет, этот пример дает самому себя отлаживать. Затем startзапускается программа, и после этого gdbее можно использовать для выполнения произвольного кода Си.

JPA
источник
2
Хороший трюк, чтобы использовать GDB. Тем не менее, GDB является частью инструментов разработчика, и если вы хотите использовать библиотечную функцию, вам лучше подготовиться к тому, чтобы научиться использовать инструменты разработчика.
Dudi Boy
4

Инструмент, который можно использовать для интерактивного вызова функций в общих библиотеках: «Коллекция компиляторов Witchcraft». https://github.com/endrazine/wcc

Со страницы GitHub:

wsh: оболочка колдовства

Оболочка колдовства принимает в качестве входных данных совместно используемые библиотеки ELF, исполняемые файлы ELF ET_DYN и сценарии оболочки Witchcraft, написанные на Punk-C. Он загружает все исполняемые файлы в собственное адресное пространство и делает их API доступными для программирования во встроенном интерпретаторе. Это обеспечивает функциональность бинарных файлов, аналогичную предоставляемой посредством отражения в таких языках, как Java.

Пример использования wsh Следующая команда загружает /usr/sbin/apache2исполняемый файл в wsh, вызывает ap_get_server_banner()функцию в apache, чтобы получить его баннер и отображает его в wshинтерпретаторе.

jonathan@blackbox:~$ wsh /usr/sbin/apache2
> a = ap_get_server_banner()
> print(a)
Apache/2.4.7
Алекс Д
источник