如何从外壳执行库命令?


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 -mexpr length以及其他解决方法。

但是,我在这里有两个问题:

  1. 如何在外壳内使用任何library calls (3)东西?
  2. 如何仅使用库调用而不是其他命令来计算字符串长度?

注意:问题主要library calls针对外壳及其在外壳中的用法。这使得第一个问题更重要。



4
基本上,尽管迈克尔的答案很不错,但简单的答案是:您不会。库调用相关的外壳。它们用于用C语言编写程序。-更一般地,阅读手册页时,除非你正在编写一个程序,就忽略第2,第3和9
spectras

不在主题之列,但是OpenVMS具有“词汇”功能,这些功能是许多系统库功能的外壳接口。
罗恩·约翰

Answers:


39

您可能不应该这样做,但是可以。Kusalananda的答案更好地解决了当前的任务,并解释了该问题。但是,由于您确实询问过如何在终端内使用任何库调用,因此有以下几种方法...


微小的C编译器(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和其他shell的进程替换来提供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-对于每种不同的arity和return类型,您都需要一个单独的函数)。


另一个选择是Tavis Ormandy ctypes.shBash插件,它包装了dlopendlsym。这可能是您尝试的最接近的近似值。您可以使用,例如:

$ 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. 确实需要使用另一个命令将您引导到库中,或者使用一个挂接到您的Shell中的软件。

总而言之,以任何其他方式执行此操作几乎都会更好。


2
“ dlcall”使我想起(不太完全相同)Windows“ rundll”:support.microsoft.com/en-gb/help/164787/…–
pjc50

1
@ pjc50:公共服务声明:rundll32 不是用于调用任意函数的通用工具。它只能用于调用具有给定签名的函数。而且,它并不是非常适合未来的
凯文(Kevin)

29

apropos命令在许多方面都很有用,但是它确实也给您带来很多“垃圾”。您列出的大多数内容都是C库例程(这是手册的第3节所针对的例程),您不能直接在shell中使用它们。

要使用它们,您必须编写调用它们的C程序。这不在此特定站点涵盖的主题范围之内(将在StackOverflow上作为主题)。

因此,这些是您的问题的答案:

  1. 您不能,它们是C库例程。
  2. 除非编写C程序,否则您将无法这样做。

我知道您知道这一点,但是为了完整起见:在shell中,如果变量中有字符串string,则可以

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

这将Length of string "hello world" is 1111来源来自的终端中打印${#string}扩展到中的字符串长度$string

在内部,shell可能会使用您列出的库调用之一来进行长度计算。

这是获取存储在shell变量中的字符串长度的最有效方法。

还要注意,这${#string}POSIX shell参数扩展,因此可以在要求任何POSIX兼容程度的所有shell之间移植。


有关库函数的各部分是一个很好的解释,但是此答案的其余部分有点误导。OP说“我只想简单地计算一个字符串的长度”,printf这里无需使用。如果您想将其作为句子的一部分打印出来,那可能是最好的方法。但是,“我如何获得一个字符串的长度”这个问题的答案呢?是${#string}部分,而不是printf。您可以只echo ${#string}输出长度,或者将其分配给具有string_length=${#string}或所需的其他变量。printf并没有真正的意义
亚当

1
@Adam我对答案做了一些小的修改。我认为很清楚如何获取字符串的长度,并且用户已经说过他们知道如何做到这一点。通过使用字符串的长度,printf我添加了一些内容,而不仅仅是说“ use ${#string}”(这在示例中很明显)。
Kusalananda

15

我不会只是这样做 strlen(),但这是有时尝试C代码的有用技巧。

用户@主机:〜$ gdb gdb
(gdb)开始
临时断点1,...在main()中
(gdb)打印strlen(“ foobar”)
$ 1 = 6

gdb是GNU调试器,通常需要一个程序名来调试。因为我们没有,所以本示例将其本身进行调试。然后start启动程序,然后gdb可以用来执行任意C代码。


2
不错的技巧,可以使用gdb。但是gdb是开发人员工具的一部分,如果您想使用库函数,则最好准备学习使用开发人员工具。
Dudi Boy '18

4

可用于交互式调用共享库中的函数的工具:“ Witchcraft编译器集合”。https://github.com/endrazine/wcc

在GitHub页面上:

wsh:巫术外壳

该巫术外壳接受以Punk-C编写的ELF共享库,ELF ET_DYN可执行文件和Witchcraft Shell脚本作为输入。它将所有可执行文件加载到其自己的地址空间中,并使它们的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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.