解析Shell的命令行参数

时间:2019-07-02 04:35:39

标签: c argv execv

我有一个要为一个夏季项目编写的shell。例如,我正在尝试解析命令行,

如果我打电话

ls -l

我需要解析

-l

部分。

因此,我可以将其传递给execv端使用的参数向量。我知道我正确解析了它,但是由于某种原因,找不到目录。我可能想念什么吗?下面是我的代码。

1 个答案:

答案 0 :(得分:5)

尽管strtok标准库函数可能很有用,但您需要了解其接口的缺点,这基本上是对粗心的陷阱。

在此程序中,您似乎偶然发现了strtok界面这两个最常见的问题。请结合此答案仔细阅读man strtok,以免将来陷入这些问题。另外,请勿使用strtok作为良好的界面设计示例。而是将其用作避免情况的模型:

隐藏的全局状态

strtok对保留在静态变量中的字符串指针进行操作。每当您使用第一个非NULL参数调用strtok时,它都会首先将此静态变量的值重置为该字符串。在每次对strtok的调用结束时,它将其静态变量设置为下一次扫描应开始的地址,该地址恰好在它刚刚找到的令牌之后。

在整个程序中只有一个静态变量实例,因此您无法交错对两个不同的字符串进行strtok扫描。更糟糕的是,您无法在对字符串进行strtok扫描时调用本身会调用strtok的函数,因为该函数内部的调用将重置strtok状态。

这意味着您在程序中进行多次strtok扫描时必须小心。在您的情况下,初始化名称错误的变量env后:

token = strtok(env, ":");

您使用strtok将输入命令分成名字不正确的变量argv中的部分:

argv = strtok(buf_copy, " ");

因此,当您以后要查找env的下一个组件时:

token = strtok(NULL, ":");

strtok的状态不再指向env;相反,它指向buf_copy(并且根据您的特定输入,指向buf_copy中找不到更多令牌的位置)。

输入参数的修改

strtok的第一个参数是char*,而不是const char*

通常,如果库函数具有字符串参数,则除非函数打算修改字符串,否则应将参数声明为const char*。或者,换句话说,const char*声明是一个承诺,即不会尝试修改该参数,如果未做出承诺,则可能是有充分的理由。

实际上,如果您阅读strtok的文档,将会看到它通过用NUL字符覆盖一些分隔符来显式修改其输入字符串。这具有将原始字符串永久划分为单独标记的作用。有时候很好,但是如果您以后想再次引用字符串的原始值,可能会给您带来很多麻烦。通常,您会发现自己在复制原始字符串,以便在其上调用strtok。 (这通常是程序设计不佳的征兆,或者是信号strtok并不是用于解析的正确工具。)

在此特定程序中,陷阱是getenv()不返回环境变量值的副本。它直接将指针返回到环境变量表中。尽管getenv的返回类型为char*,这可能使您认为修改该值是可以的,但C标准显然告诉您不要:

  

指向的字符串不得由程序修改

不幸的是,Linux的getenv手册页中没有这个禁止,但是该手册页确实指出getenv为您提供了指向环境表的指针。如果您确实修改了getenv返回的字符串,则很可能(尽管不能保证)随后对同一环境变量的getenv调用将检索修改后的值。

这就是您要做的事情:由于您在strtok返回的字符串上放开了getenv(PATH),因此对getenv(PATH)的后续调用将在第一个冒号处截断一个值。 / p>