shell glob 扩展如何像 ls -l * 一样工作?

问题描述

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 kernelfree 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).