在C语言中实现Unix shell:检查文件是否可执行。

4

我正在用C语言实现一个Unix shell,目前遇到的问题是相对路径。特别是在输入命令时。现在每次都必须输入可执行文件的完整路径,而我更希望只需输入"ls"或"cat"。

我已经成功获取了$PATH环境变量。我的想法是在":"字符处将变量分割,然后将每个新字符串附加到命令名称上,并检查文件是否存在且可执行。

例如,如果我的PATH是"/bin:/usr/bin",我输入"ls",我希望程序首先检查"/bin/ls"是否存在并可执行,如果不存在则转到"/usr/bin/"。

两个问题:

1)这是一个好方法吗?(不一定要是最好的,只想确保它能够工作。)

2)更重要的是,我该如何在C语言中检查文件是否存在且可执行?

希望我表述清楚了,谢谢 :)

6个回答

10

不要这样做。进行这种检查是错误的;它本质上容易受到竞态条件的影响。相反,尝试使用适当的exec-family调用来执行它。如果它无法执行,你会收到一个错误。

还要注意,您不需要自己搜索PATH execvp 可以为您完成此操作。


4
没错。也许把这个检查作为一种预验证的形式会有用,但现实情况是除了尝试运行它,没有其他方法能告诉你它是否可运行。 - Steven Sudit
问题在于,如果您正在尝试检测其中一个程序,每个程序可能需要30秒才能启动。 - don bright

9

stat?嗯,绰绰有余。

看看access()系统调用。

if (access(filename, F_OK|X_OK) == 0)
{
    /* You can execute this file. */
}

请注意,任何文件访问或文件存在的检查都具有固有的竞态条件。您无法保证在调用execve时,自您进行检查以来,没有人删除了可执行位、更改了文件所有权、删除了文件等等。在编写代码并决定如何处理错误条件时,请牢记这一点。

+1 提醒了竞态条件。这样的解决方案不能假设在检查和操作外部(不在程序内部)资源之间什么都没有改变。 - Edwin Buck
2
不,X_OK是不够的。您仍然需要调用stat(2)来查看它是否是一个文件,然后尝试执行它以查看它是否真的可以。从精细的手册中得知:“X_OK表示执行/搜索权限”,甚至“Access()是一个潜在的安全漏洞,永远不应该使用”。 - mu is too short
@mu 太短了 - 看起来我错过了文件存在的 F_OK。但如果不检查位,我对 X_OK 的作用感到困惑。有趣。至于“潜在的安全漏洞”部分,这非常模糊。他们可能指的是竞态条件部分。 - asveikau
F_OK 仍然不能检查它是目录、文件还是其他类型;F_OK 只是一个简单的“是否存在”检查,而 X_OK 只是一个简单的“是否具有执行位设置”的检查。对于 /bin/bin/lsF_OK|X_OK 将返回相同的结果。模糊的安全警告可能是在谈论竞态条件。 - mu is too short
请注意,这对目录也是适用的。 - mic_e

4
使用stat函数: http://linux.die.net/man/2/stat 例如:
struct stat sb;
if(!stat(path, &sb))
{
  if(IS_REG(sb.st_mode) && sb.st_mode & 0111)
    printf("%s is executable\n", path);
}

4
调用stat函数并传入完整路径名,如果返回值为0(成功),则目标文件存在。在这种情况下,您可以使用返回的结构体中的st_mode字段来测试目标是目录、设备、命名管道还是普通文件。如果它是一个文件,st_mode也将包含权限位。(如果它是一个目录,一些“可执行”位可能已经设置,但这意味着“可搜索”而不是“可执行”。)
编辑:正如其他人所指出的,这存在竞态条件,并且有更好的方法来完成您在特定情况下想要做的事情。

0
你是如何创建进程的?
使用execlp或execvp。

execvp已经为我做了这件事。与execlp有什么不同? - rahmu
2
区别在于文档!(如果您有谷歌,您就有文档。)如果您正在做这种事情,您真的想把查看文档作为一件正常的事情。您甚至可能会学到一些意外的东西。 - davep

0

access()的答案是唯一明智的选择。 如果你使用stat,你必须解析/etc/group来找出你所在的所有组,除了getgid(),并将它们与组可执行位一起测试。


请检查@asveikau的“access”答案中的注释,它不是一个正确的检查。 - tuxillo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接