问题描述
shell 是如何扩展 '*' 的
ls -l *
是否有任何系统调用在幕后发生以读取目录中的所有文件?我试过 strace 但没有显示任何特定于通配符的系统调用
解决方法
glob 由 bash 扩展,而不是由 ls
扩展。当您运行 strace ls *
时,仅跟踪 ls
,但 glob 在 ls
甚至 strace
运行之前扩展,因此您不会在跟踪中看到它的系统调用.
要跟踪 glob 的扩展,请使用 strace bash -c 'echo *'
。这还将包括来自 shell 启动的系统调用。要仅显示为 echo *
完成的系统调用,您可以使用 ...
strace bash -c '[ -e startglob ]; echo *; [ -e endglob ]' 2>&1 |
sed -n '/"startglob"/,/"endglob"/p'
,
阅读glob(7)。 Globbing 会使用多个 syscalls(2):access(2)、stat(2) opendir(3)、readdir(3)(使用 getdents(2)...)closedir(3)。另见nftw(3)。
而 GNU bash 是(如 GNU libc 和 Linux kernel)free software,你可以下载它的源代码并研究它,编译它并改进它。>
您可以使用 strace(1) 或 gdb(1) 来了解 GNU bash(或任何其他 Linux 应用程序)的工作原理。
当然,globbing 是由 shell 完成的(在 fork(2) 和 execve(2)...之前),不是由 /bin/ls
进程
shell 如何像 *
一样扩展 ls -l *
遇到像 *
这样的全局模式时,shell 会读取所有目录条目,并将它们与模式一一比较。
快速浏览源代码告诉我这可能是在 glob.c
中完成的,尤其是在 glob_vector()
函数中。
幕后是否有系统调用读取目录中的所有文件?
当然,没有内核的帮助,你无法读取目录的内容。
也就是说,系统调用是低级的,所以不要期望找到任何对 opendir()
和 readdir()
的调用,它们是 libc 调用。相反,您会找到对 open()
和 getdents()
/getdents64()
的调用。
我尝试了 strace 但没有显示任何特定于通配符的系统调用
strace
不是正确的工具。这不是你的内核在做globing,这是你的shell,可能在libc函数的帮助下,因此不涉及系统调用(除了上面提到的读取目录内容的低级调用)。>
如果您想跟踪对 opendir()
、readdir()
、glob()
、strcmp()
等库函数的调用,您必须使用 ltrace
:
ltrace bash -c 'ls *'
也就是说,Bash 有自己的全局系统,比 POSIX 标准指定的基本系统更先进,因此它不会依赖于 glob()
或 fnmatch()
等库函数。不要期望在 ltrace
输出中看到对它们的调用。
如果您的目标是在自己的应用程序中使用 globbing 并研究它是如何完成的,您可能想看看 glob()
,它比 Bash 的 globbing 更简单(仅供参考,我最近发布了一个使用示例here).