程序是否可以在 POSIX 中获取命令行参数之间的空格数?

iBu*_*Bug 23 command-line shell programming

假设我用以下行编写了一个程序:

int main(int argc, char** argv)
Run Code Online (Sandbox Code Playgroud)

现在它通过检查argv.

程序可以检测参数之间有多少个空格吗?就像我在 bash 中输入这些一样:

ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog       aaa      bbb
Run Code Online (Sandbox Code Playgroud)

环境是现代 Linux(如 Ubuntu 16.04),但我想答案应该适用于任何符合 POSIX 的系统。

Han*_*ner 58

一般来说,没有。命令行解析是由 shell 完成的,它不会使未解析的行对被调用程序可用。实际上,您的程序可能是从另一个程序执行的,该程序不是通过解析字符串而是通过以编程方式构造参数数组来创建 argv。

  • 你可能想提到`execve(2)`。 (9认同)
  • “一般”是为了防止在可能的情况下引用特殊的复杂情况 - 例如,suid root 进程可能能够检查调用 shell 的内存并找到未解析的命令行字符串。 (9认同)
  • 你是对的,作为一个蹩脚的借口,我可以说我目前正在使用手机并且查找手册页有点乏味:-) (3认同)

Tob*_*ght 39

谈论“参数之间的空间”是没有意义的;这是一个shell概念。

shell 的工作是获取整行输入并将它们组成参数数组以启动命令。这可能涉及解析带引号的字符串、扩展变量、文件通配符和波浪号表达式等等。该命令以标准exec系统调用开始,它接受字符串向量。

存在其他方法来创建字符串向量。许多程序使用预先确定的命令调用来派生和执行他们自己的子进程——在这种情况下,从来没有“命令行”这样的东西。类似地,当用户拖动文件图标并将其放在命令小部件上时,图形(桌面)shell 可能会启动一个进程——同样,没有文本行在参数“之间”包含字符。

就调用的命令而言,shell 或其他父/前驱进程中发生的事情是私有和隐藏的——我们只能看到标准 C 指定的main()可以接受的字符串数组。


Kus*_*nda 16

不,这是不可能的,除非空格是参数的一部分

该命令访问数组中的各个参数(以一种或另一种形式取决于编程语言),并且实际的命令行可以保存到历史文件中(如果在具有历史文件的 shell 中的交互式提示符下键入),但是从未以任何形式传递给命令。

Unix 上的所有命令最终都由exec()函数系列之一执行。它们采用命令名称和参数列表或数组。它们都没有在 shell 提示符下输入命令行。该system()函数确实如此,但它的字符串参数稍后由 执行execve(),它同样采用参数数组而不是命令行字符串。

  • @Kusalananda - 好吧,不......在`hello`和`world`之间用引号括起来是_字面意思_提供三个参数中的第二个。 (5认同)
  • @LightnessRacesinOrbit 我把它放在那里以防万一对“参数之间的空格”有一些混淆。在 `hello` 和 `world` 之间用引号括起来就是两个参数之间的 _literally_ 空格。 (2认同)

Bas*_*tch 9

一般来说,这是不可能的,就像解释的其他几个答案一样。

但是,Unix shell普通程序(并且它们正在解释命令行并对其进行通配符,即在执行&之前扩展命令)。请参阅有关外壳操作的说明您可以编写自己的 shell(或者您可以修补一些现有的自由软件shell,例如GNU bash)并将其用作您的 shell(甚至您的登录 shell,请参阅passwd(5) & shells(5))。forkexecvebash

例如,你可能有自己的shell程序把完整的命令行中的一些环境变量(想象MY_COMMAND_LINE例如) -或使用其他任何形式的进程间通信的命令行外壳,从儿童步骤-传输。

我不明白您为什么要这样做,但您可能会以这种方式编写外壳程序(但我建议不要这样做)。

顺便说一句,一个程序可以由一些不是shell 的程序启动(但是它执行fork(2)然后execve(2),或者只是execve在其当前进程中启动一个程序)。在那种情况下根本没有命令行,您的程序可以在没有命令的情况下启动......

请注意,您可能有一些(专门的)Linux 系统,但没有安装任何 shell。这是奇怪和不寻常的,但可能。然后,您需要编写一个专门的init程序,根据需要启动其他程序 - 不使用任何 shell,而是通过执行fork& execve系统调用。

另请阅读操作系统:三个简单的部分,不要忘记它execve实际上总是一个系统调用(在 Linux 上,它们列在syscalls(2) 中,另见intro(2)),它重新初始化虚拟地址空间(以及其他一些事情)的过程