我有一个要为一个夏季项目编写的shell。例如,我正在尝试解析命令行,
如果我打电话
ls -l
我需要解析
-l
部分。
因此,我可以将其传递给execv
端使用的参数向量。我知道我正确解析了它,但是由于某种原因,找不到目录。我可能想念什么吗?下面是我的代码。
答案 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>