![程序是否可以获取 POSIX 中命令行参数之间的空格数?](https://linux22.com/image/129521/%E7%A8%8B%E5%BA%8F%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E8%8E%B7%E5%8F%96%20POSIX%20%E4%B8%AD%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E4%B9%8B%E9%97%B4%E7%9A%84%E7%A9%BA%E6%A0%BC%E6%95%B0%EF%BC%9F.png)
假设我用以下行编写了一个程序:
int main(int argc, char** argv)
现在它通过检查 .txt 的内容知道传递给它的命令行参数是什么argv
。
程序可以检测参数之间有多少个空格吗?就像我在 bash 中输入这些内容时一样:
ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog aaa bbb
环境是现代 Linux(如 Ubuntu 16.04),但我认为答案应该适用于任何兼容 POSIX 的系统。
答案1
一般来说,没有。命令行解析是由 shell 完成的,它不会使未解析的行可供被调用的程序使用。事实上,您的程序可能是从另一个程序执行的,该程序不是通过解析字符串而是通过以编程方式构造参数数组来创建 argv。
答案2
谈论“论证之间的空间”是没有意义的;这是一个 shell 概念。
shell 的工作是获取整行输入并将它们形成参数数组以启动命令。这可能涉及解析带引号的字符串、扩展变量、文件通配符和波形符表达式等等。该命令以标准系统调用启动exec
,该系统调用接受字符串向量。
还存在其他方法来创建字符串向量。许多程序通过预定的命令调用来分叉和执行它们自己的子进程 - 在这种情况下,永远不会有“命令行”这样的东西。类似地,当用户拖动文件图标并将其放到命令小部件上时,图形(桌面)shell 可能会启动一个进程 - 同样,参数“之间”不存在包含字符的文本行。
就调用的命令而言,shell 或其他父/前体进程中发生的事情是私有且隐藏的 - 我们只能看到标准 C 指定的main()
可以接受的字符串数组。
答案3
不,这是不可能的,除非空格部分一个论点。
该命令访问数组中的各个参数(以一种形式或另一种形式,具体取决于编程语言),并且实际的命令行可能会保存到历史文件中(如果在具有历史文件的 shell 中的交互式提示符下键入),但从未以任何形式传递给命令。
Unix 上的所有命令最终都由该exec()
函数系列之一执行。它们采用命令名称和参数列表或数组。它们都不采用在 shell 提示符下键入的命令行。该system()
函数确实如此,但其字符串参数稍后由 执行execve()
,它再次采用参数数组而不是命令行字符串。
答案4
您始终可以让您的 shell 告诉应用程序哪些 shell 代码会导致其执行。例如,使用,通过使用钩子在环境变量zsh
中传递该信息(作为示例,您将在程序中使用):$SHELL_CODE
preexec()
printenv
getenv("SHELL_CODE")
$ preexec() export SHELL_CODE=$1
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv SHELL_CODE
printenv CODE
$ $(echo printenv SHELL_CODE)
$(echo printenv SHELL_CODE)
$ for i in SHELL_CODE; do printenv "$i"; done
for i in SHELL_CODE; do printenv "$i"; done
$ printenv SHELL_CODE; : other command
printenv SHELL_CODE; : other command
$ f() printenv SHELL_CODE
$ f
f
所有这些都将执行printenv
为:
execve("/usr/bin/printenv", ["printenv", "SHELL_CODE"],
["PATH=...", ..., "SHELL_CODE=..."]);
允许printenv
检索导致printenv
使用这些参数执行的 zsh 代码。我不清楚你想用这些信息做什么。
对于bash
,最接近zsh
s 的功能将在陷阱中preexec()
使用它,但请注意,其中会进行一定程度的重写(特别是重构一些用作分隔符的空格),并且这会应用于每个(好吧,一些)命令运行,而不是在提示符下输入的整个命令行(另请参见该选项)。$BASH_COMMAND
DEBUG
bash
functrace
$ trap 'export SHELL_CODE="$BASH_COMMAND"' DEBUG
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv $(echo 'SHELL_CODE')
printenv $(echo 'SHELL_CODE')
$ for i in SHELL_CODE; do printenv "$i"; done; : other command
printenv "$i"
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printf '%s\n' "$(printenv "SHELL_CODE")"
$ set -o functrace
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printenv "SHELL_CODE"
$ print${-+env } $(echo 'SHELL_CODE')
print${-+env } $(echo 'SHELL_CODE')
了解 shell 语言语法中作为分隔符的一些空格如何被压缩为 1,以及如何不总是将完整的命令行传递给命令。所以对你的情况可能没有用。
请注意,我不建议这样做,因为您可能会将敏感信息泄露给每个命令,如下所示:
echo very_secret | wc -c | untrustedcmd
会将这个秘密泄露给wc
和untrustedcmd
。
当然,您可以对 shell 以外的其他语言执行此类操作。例如,在 C 中,您可以使用一些宏将执行命令的 C 代码导出到环境:
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define WRAP(x) (setenv("C_CODE", #x, 1), x)
int main(int argc, char *argv[])
{
if (!fork()) WRAP(execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (!fork()) WRAP(0 + execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (argc > 1 && !fork()) WRAP(execvp(argv[1], &argv[1]));
wait(NULL);
return 0;
}
例子:
$ ./a.out printenv C_CODE
execlp("printenv", "printenv", "C_CODE", NULL)
0 + execlp("printenv", "printenv", "C_CODE", NULL)
execvp(argv[1], &argv[1])
看看 C 预处理器如何压缩某些空格,就像在 bash 情况下一样。在大多数(如果不是全部)语言中,分隔符中使用的空间量没有区别,因此编译器/解释器在这里对它们采取一些自由也就不足为奇了。