为什么 ls -d 也列出文件,它在哪里记录?

erc*_*rch 51 ls shell bash options wildcards

  • 指定ls --directory a*时应仅列出以开头的目录a*
  • 但它列出了以开头的文件和目录 a

问题

  • 我在哪里可以找到一些这方面的资料,比其他maninfo在那里我觉得我完全看?
  • 这仅在 BASH 中有效吗?

Kei*_*son 92

a**a*语法是由shell实现的,而不是由ls命令。

当你输入

ls a*
Run Code Online (Sandbox Code Playgroud)

在您的 shell 提示符下,shell 扩展a*为当前目录中名称以a. 例如,它可能会扩展a*到序列a1 a2 a3,并将它们作为参数传递给ls。该ls命令本身不会看到*角色; 它只能看到三个参数a1a2a3

出于通配符扩展的目的,“文件”是指当前目录中的所有实体。例如,a1可能是一个普通文件,a2可能是一个目录,也a3可能是一个符号链接。它们都有目录条目,shell 的通配符扩展并不关心这些条目所指的实体类型。

实际上,您可能遇到的所有 shell(bash、sh、ksh、zsh、csh、tcsh 等)都实现了通配符。细节可能会有所不同,但*匹配零个或多个字符和?匹配任何单个字符的基本语法是相当一致的。

特别是对于 bash,这在 bash 手册的“文件名扩展”部分有记录;运行info bash并搜索“文件名扩展”,或查看此处

这是由 shell 完成的,而不是由单个命令完成的,这一事实会产生一些有趣(有时令人惊讶)的结果。最好的一点是通配符处理对于(几乎)所有命令都是一致的;如果 shell 不这样做,不可避免地有些命令不会打扰,而其他命令会以作者认为“更好”的微妙不同的方式执行。(我认为 Windows 命令外壳有这个问题,但我对它不够熟悉,无法进一步评论。)

另一方面,很难编写一个命令来重命名多个文件。如果你写:

mv *.log *.log.bak
Run Code Online (Sandbox Code Playgroud)

它可能会失败,因为它*.log.bak是根据当前目录中已经存在的文件展开的。有一些命令可以做这种事情,但它们必须使用自己的语法来指定如何重命名文件。一些命令(如find)可以做自己的通配符扩展;你必须引用参数来抑制 shell 的扩展:

find . -name '*.txt' -print
Run Code Online (Sandbox Code Playgroud)

shell 的通配符扩展完全基于命令行参数的语法和现有文件集。它不能用命令的含义而受到影响。例如,如果要将所有.log文件向上移动到父目录,则可以键入:

mv *.log ..
Run Code Online (Sandbox Code Playgroud)

如果您忘记了..

mv *.log
Run Code Online (Sandbox Code Playgroud)

并且.log当前目录中恰好有两个文件,它将扩展为:

mv one.log two.log
Run Code Online (Sandbox Code Playgroud)

这将重命名one.log和破坏two.log

编辑:经过 52 次投票、接受和 Guru 徽章后,也许我应该真正回答标题中的问题。

-d--directory选项ls没有告诉它只是列出目录。它告诉它像它们本身一样列出目录,而不是它们的内容。如果您将目录名称作为参数提供给ls,默认情况下它将列出目录的内容,因为这通常是您感兴趣的内容。该-d选项告诉它只列出目录本身。这在与通配符结合使用时特别有用。如果您键入:

ls -l a*
Run Code Online (Sandbox Code Playgroud)

ls会给你的每一个长列表文件的名字开始与a和的,在内容的每个名字开始与目录a。如果您只想要文件和目录的列表,每行一行,您可以使用:

ls -ld a*
Run Code Online (Sandbox Code Playgroud)

这相当于:

ls -l -d a*
Run Code Online (Sandbox Code Playgroud)

再次记住,该ls命令永远不会看到该*字符。

至于记录在哪里,man ls将向您展示该ls命令在几乎任何类 Unix 系统上的文档。在大多数基于 Linux 的系统上,该ls命令是 GNU coreutils 包的一部分;如果你有info命令,要么info lsinfo coreutils ls应该给你更明确和全面的文档。其他系统,如MacOS,可能会使用不同版本的ls命令,也可能没有info命令;对于这些系统,请使用man ls. 如果您使用的是 GNU coreutils 实现,并且ls --help会显示一个相对较短的使用消息(在我的系统上为 117 行)。

是的,即使是专家也需要不时查阅文档。另见这个经典笑话

  • @Thomas:`a*` 扩展为当前目录中*名称以 `a` 开头的所有文件的列表。 (8认同)
  • @Thomas:或者在您指定的任何目录中:`ls subdir/a*` (2认同)
  • @sendmoreinfo:这取决于外壳和设置。在 csh 和 tcsh 中,失败的 glob 扩展是一个错误。在 bash 中,`shopt -s failglob` 会导致相同的行为。例如,设置 `nullglob` 而不是 `failglob` 会导致 `*nosuchfile*` 扩展为空字符串。 (2认同)

chi*_*rlu 27

见基思汤普森的回答;但要解释为什么ls --directory a*显示文件目录:该--directory选项不抑制非目录文件。相反,它会按原样列出目录,否则会列出它们的内容。例子:

$ mkdir foo
$ touch foo/bar
$ ls foo
bar
$ ls --directory foo
foo
Run Code Online (Sandbox Code Playgroud)

  • 该示例使用 `-F` 选项更引人注目 (3认同)

msw*_*msw 6

非常明确,它记录在ls(1) 手册页中

-d, --directory 列出目录条目而不是内容,并且不要取消引用符号链接

公平地说,“条目而不是内容”可能更具解释性:

如果 FILE 是目录,则显示目录条目本身而不是列出该目录的内容。如果 FILE 是符号链接,则显示链接条目本身而不是链接指向的文件。