我有一个要为一个夏季项目编写的shell。例如,我正在尝试解析命令行,
如果我打电话
ls -l
我需要解析
-l
部分。
因此,我可以将其传递给用于的参数向量execv
。我知道我正确解析了它,但是由于某种原因,找不到目录。我可能想念什么吗?下面是我的代码。
尽管strtok
标准库功能可能很有用,但您需要了解其接口的缺点,这基本上是对粗心的陷阱。
在此程序中,您似乎偶然发现了strtok
界面的两个最常见问题。请man strtok
与该答案一起仔细阅读,以免将来陷入这些问题。另外,请勿将其strtok
用作良好界面设计的示例。而是将其用作避免情况的模型:
strtok
对保留在静态变量中的字符串指针进行操作。每当您strtok
使用第一个非NULL参数进行调用时,它都会首先将此静态变量的值重置为该字符串。在对的每次调用结束时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
是a char*
,而不是a const char*
。
通常,如果库函数具有字符串参数,则const char*
除非函数打算修改字符串,否则应将参数声明为。或者,换句话说,const char*
声明是一个承诺,即不会尝试修改该参数,如果没有做出承诺,则可能是有充分理由的。
而且,的确,如果您阅读strtok
的文档,您会看到它通过用NUL字符覆盖一些分隔符来显式修改其输入字符串。这具有将原始字符串永久划分为单独标记的作用。有时候很好,但是如果您以后想再次引用字符串的原始值,可能会给您带来很多麻烦。通常,您会发现自己在复制原始字符串以进行调用strtok
。(这通常是程序设计不当的征兆,或者信号strtok
并不是真正用于解析的正确工具。)
在此特定程序中,陷阱是getenv()
不返回环境变量值的副本。它直接将指针返回到环境变量表中。尽管返回类型getenv
为char*
,这可能使您认为修改值是可以的,但C标准显然告诉您不要:
指向的字符串不得由程序修改
不幸的是,Linux的manpage中没有禁止使用此功能getenv
,但是该手册确实指出getenv
了指向环境表的指针。如果您确实修改了所返回的字符串getenv
,则很有可能(尽管不能保证)getenv
对相同环境变量的后续调用将检索修改后的值。
这就是您要做的事情:由于您放开strtok
了返回的字符串getenv(PATH)
,因此后续调用getenv(PATH)
将看到一个值在第一个冒号处被截断。