【发布时间】:2021-03-02 19:33:51
【问题描述】:
shell 是如何扩展 '*' 的
ls -l *
是否有任何系统调用在后台发生以读取目录中的所有文件?我尝试了 strace,但没有显示任何特定于 globbing 的系统调用
【问题讨论】:
标签: bash shell system-calls glob
shell 是如何扩展 '*' 的
ls -l *
是否有任何系统调用在后台发生以读取目录中的所有文件?我尝试了 strace,但没有显示任何特定于 globbing 的系统调用
【问题讨论】:
标签: bash shell system-calls glob
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'
【讨论】:
strace bash -c 'echo *' 它跟踪 bash 对吗?不是*
* 不是命令;扩展* 是shell(这里是bash)的工作。然而,仅仅使用strace bash ...,你也会得到很多系统调用来启动shell。我编辑了我的答案,只提取了相关部分。
stat32 或stat64,sed 命令可能无法正常工作——使用sed -n '/stat.*("startglob/,/stat.*("endglob/p' 应该适用于任何这些情况。
阅读glob(7)。通配符会使用几个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 应用程序)的工作原理。
当然,通配是由 shell 完成的(在 fork(2) 和 execve(2)... 之前),不是由 /bin/ls 进程完成
【讨论】:
ls
shell 如何像
ls -l *一样扩展*
当遇到像* 这样的通配模式时,shell 会读取所有目录条目并与模式一一进行比较。
快速浏览一下源代码告诉我这可能是在glob.c 中完成的,尤其是在glob_vector() 函数中。
是否有任何系统调用在后台发生以读取目录中的所有文件?
当然,没有内核的帮助,你无法读取目录的内容。
也就是说,系统调用是低级的,所以不要期望找到任何对 opendir() 和 readdir() 的调用,它们是 libc 调用。相反,您会找到对open() 和getdents()/getdents64() 的调用。
我尝试了 strace,但没有显示任何特定于 globbing 的系统调用
strace 不是正确的工具。这不是你的内核在做 globing,这是你的 shell,可能在 libc 函数的帮助下,因此不涉及系统调用(除了上面提到的读取目录内容的低级调用)。
如果您想跟踪对库函数的调用,例如 opendir()、readdir()、glob()、strcmp(),...您必须使用 ltrace:
ltrace bash -c 'ls *'
也就是说,Bash 有自己的 globing 系统,比 POSIX 标准指定的基本系统更先进,因此它不会依赖像 glob() 或 fnmatch() 这样的库函数。不要期望在ltrace 输出中看到对他们的调用。
如果您的目标是在自己的应用程序中使用 globbing 并研究它是如何完成的,您可能想看看glob(),它比 Bash 的 globbing 更简单(仅供参考,我最近发布了一个使用示例 @987654326 @)。
【讨论】: