【问题标题】:How shell glob expansion works as in ls -l *?shell glob 扩展如何像 ls -l * 一样工作?
【发布时间】:2021-03-02 19:33:51
【问题描述】:

shell 是如何扩展 '*' 的

ls -l *

是否有任何系统调用在后台发生以读取目录中的所有文件?我尝试了 strace,但没有显示任何特定于 globbing 的系统调用

【问题讨论】:

    标签: bash shell system-calls glob


    【解决方案1】:

    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。我编辑了我的答案,只提取了相关部分。
    • @Socowi 如果底层调用是stat32stat64sed 命令可能无法正常工作——使用sed -n '/stat.*("startglob/,/stat.*("endglob/p' 应该适用于任何这些情况。
    • @GordonDavisson 不错。感谢您的评论。
    【解决方案2】:

    阅读glob(7)。通配符会使用几个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 应用程序)的工作原理。

    当然,通配是由 shell 完成的(在 fork(2)execve(2)... 之前),不是/bin/ls 进程完成

    【讨论】:

    • 感谢@Basile Starynkevitch。我试过 strace 来了解它是如何工作的,并且还通读了 bash github 。我没有看到 ls -l 和 ls -l * 之间有太大区别。没有 opendir 、 readdir 、 getdents 调用。 strace 没有捕获所有 glob 扩展调用吗?
    • 再一次,通配是由 shell 完成的。不是ls
    【解决方案3】:

    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 @)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-15
      • 2013-06-14
      • 2012-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多