假设我用下面的代码写了一个程序:
int main(int argc, char** argv)
现在,通过检查的内容,知道将哪些命令行参数传递给它argv。
程序可以检测参数之间有多少空格吗?就像我在bash中键入以下内容一样:
ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog aaa bbb
环境是现代Linux(例如Ubuntu 16.04),但是我想答案应该适用于任何POSIX兼容系统。
假设我用下面的代码写了一个程序:
int main(int argc, char** argv)
现在,通过检查的内容,知道将哪些命令行参数传递给它argv。
程序可以检测参数之间有多少空格吗?就像我在bash中键入以下内容一样:
ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog aaa bbb
环境是现代Linux(例如Ubuntu 16.04),但是我想答案应该适用于任何POSIX兼容系统。
Answers:
谈论“参数之间的空间”没有意义。那是壳的概念。
Shell的工作是获取整行输入并将它们形成为参数数组以开始命令。这可能涉及解析带引号的字符串,扩展变量,文件通配符和代字号表达式等。该命令以标准exec系统调用开始,该系统调用接受字符串向量。
存在其他方法来创建字符串向量。许多程序使用预定的命令调用分叉并执行它们自己的子流程-在这种情况下,永远不会出现“命令行”之类的东西。类似地,当用户将文件图标拖放到命令小部件上时,图形(桌面)shell可能会启动一个过程-再次,没有文本行在参数之间添加字符。
就所调用的命令而言,shell或其他父/前体进程中发生的事情是私有且隐藏的-我们仅看到标准C指定main()可以接受的字符串数组。
tar cf texts.tar *.txt那么tar程序将获得两个参数,并且必须自己扩展第二个(*.txt)。在开始编写自己的处理参数的脚本/程序之前,许多人都没有意识到它的真正作用。
一般来说,没有。命令行解析由外壳完成,该外壳不会使未解析的行可用于调用的程序。实际上,您的程序可能是从另一个创建argv的程序执行的,而不是通过解析字符串而是通过以编程方式构造参数数组来创建argv的。
execve(2)。
不可以,除非空格是参数的一部分,否则这是不可能的。
该命令从数组(以一种形式或另一种形式,取决于编程语言)访问数组中的各个参数,并且实际的命令行可能会保存到历史记录文件(如果在具有历史记录文件的shell中以交互式提示键入)。绝不以任何形式传递给命令。
Unix上的所有命令最终都由exec()一系列功能之一执行。它们采用命令名称和参数列表或数组。它们都不采用在shell提示符下键入的命令行。该system()函数可以,但是其字符串参数稍后由执行execve(),该参数再次使用参数数组而不是命令行字符串。
hello和之间用引号引起world来的空格实际上是两个参数之间的空格。
hello,并world在字面上提供的三个参数的第二位。
通常,不可能像其他几个答案一样解释。
但是,Unix shell是普通程序(它们正在解释命令行并对其进行遍历,即在执行&为此之前扩展命令)。请参阅有关shell操作的说明。您可以编写自己的外壳程序(或可以修补一些现有的免费软件外壳程序,例如GNU bash)并将其用作外壳程序(甚至您的登录外壳程序,请参阅passwd(5)和shells(5))。forkexecvebash
例如,你可能有你自己的shell程序把完整的命令行中的一些环境变量(想象MY_COMMAND_LINE例如) -或使用其他任何形式的进程间通信的命令行外壳,从儿童步骤-传输。
我不明白您为什么要这样做,但是您可能会以这种方式对行为进行编码(但我建议不要这样做)。
顺便说一句,程序可以由不是外壳程序(但先执行fork(2)然后执行execve(2)或只是execve在当前进程中启动程序)的某个程序启动。在这种情况下,根本就没有命令行,并且您的程序可以在没有命令的情况下启动...
请注意,您可能具有未安装任何外壳的某些(专用)Linux系统。这很奇怪和不同寻常,但是可能。然后,您需要编写一个专门的init程序,根据需要启动其他程序-无需使用任何Shell,而是通过fork& execve系统调用。
另请阅读操作系统:三个简单的部分,不要忘记,execve它实际上总是一个系统调用(在Linux上,它们在syscalls(2)中列出,另请参阅intro(2)),它们会重新初始化虚拟地址空间(以及其他一些初始化)事情)的过程。
argv[0] 程序名称和参数的其余元素是POSIX规范,不能更改。argv[-1]我假设运行时环境可以为命令行指定...
execve文档。您不能使用argv[-1],使用它是未定义的行为。
execvepluscmd使用额外的参数(或argv约定)调用特殊的非POSIX 函数,syscall为main构造一个参数矢量,该矢量在指向程序名称的指针之前包含指向命令行的指针,然后传递地址argv调用程序的名称时指向程序名称的指针main...
sh。所以不是新的。
您总是可以告诉您的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,最接近zshs 的功能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情况下一样。在大多数(如果不是全部)语言中,定界符中使用的空间量没有什么区别,因此,编译器/解释器在这里使用它们会产生一些自由也就不足为奇了。
BASH_COMMAND不包含用于分隔参数的原始空格,因此这不适用于OP的文字要求。这个答案是否包含针对该特定用例的任何演示?
我将添加其他答案中缺少的内容。
查看其他答案
在程序中无法执行任何操作,但是在运行程序时外壳中可以执行某些操作。
您需要使用引号。所以代替
./myprog aaa bbb
你需要做其中之一
./myprog " aaa bbb"
./myprog ' aaa bbb'
这会将带有所有空格的单个参数传递给程序。两者之间是有区别的,第二个是文字的,与显示的字符串完全相同(除非'必须键入\')。第一个将解释一些字符,但分为多个参数。有关更多信息,请参见shell引用。因此,无需重写外壳,外壳设计人员已经想到了这一点。但是,因为它现在是一个参数,所以您将不得不在程序中进行更多的传递。
通过标准输入传递数据。这是将大量数据放入命令的正常方法。例如
./myprog << EOF
aaa bbb
EOF
要么
./myprog
Tell me what you want to tell me:
aaaa bbb
ctrl-d
(斜体是程序的输出)
./myprog␣"␣␣␣␣␣aaa␣␣␣␣␣␣bbb"通常在子进程中)执行存储在其中的文件,./myprog并向其传递两个参数:./myprog和␣␣␣␣␣aaa␣␣␣␣␣␣bbb(argv[0]和argc[1],argc为2),和在OP中一样,分隔这两个参数的空间不会以任何方式传递到myprog。