<p>现在,您已经通过几个不同的命令(特别是<code>git add</code>和<code>echo</code>)体验到了这一点,是时候进行概括了</p>
<p>在类Unix系统(包括Linux、macOS终端windows等)上,或者在windows上使用<code>bash</code>作为命令解释器时,您使用的是一种称为<em>shell</em>的编程语言。有不同的shell编程语言(bash、dash、sh、zsh、fish等等,出于历史原因,大多数都倾向于在名称中使用<code>sh</code>),但它们都倾向于共享某些属性。其中之一是它们如何<em>扩展</em>要传递给命令的命令行参数</p>
<pre class="lang-none prettyprint-override"><code>$ mkdir t && cd t && touch .a .b c d e
$ ls -A
.a .b c d e
$ echo *
c d e
</code></pre>
<p>在这个临时目录中有五个文件<sup>1</sup>(除了标准的两个名为<code>.</code>和<code>..</code>的文件<code>ls -A</code>省略)。但是<code>echo *</code>只扩展到其中三个。这是因为<code>*</code>扩展故意<em>忽略了</em>名称<sup>2</sup><em>以<code>.</code></em>开头的任何文件</p>
<p>有些shell有一个选项可以打开或关闭此选项:例如,在bash中,可以设置<code>dotglob</code>选项。(用户可能不应该过多地摆弄这些选项,以免在使用其他人的shell或其他shell时感到意外,但最好知道它们的存在。)</p>
<p>Shell编程语言主要是围绕运行其他程序的思想而构建的。因此,当shell生成一个提示符,如<code>$</code>或<code>></code>或shell的标准提示符,并且您键入一系列空格分隔的单词时,shell会将这些单词拆分,并将其中一个用作命令名,其余部分用作命令的参数,然后运行该命令。因此,输入:</p>
<pre><code>git add *
</code></pre>
<p>让shell运行带有参数<code>add</code>和<code>*</code>的<code>git</code>命令。但是<code>*</code>本身被shell</em>特别处理<em>,扩展为文件名,不包括以点开头的文件名。因此<code>git</code>命令只能看到参数:</p>
<pre><code>add
c
d
e
</code></pre>
<p>(每行一个参数)。<code>git</code>的第一个参数是子命令,在本例中是<code>add</code>,其余参数由子命令解释。<sup>3</sup></p>
<p>由于<code>*</code>被shell扩展,Git从来没有看到过<code>*</code>,也从来没有机会用<code>*</code>做自己的事情。但是,您可以保护<code>*</code>不受shell的影响:</p>
<pre><code>*
</code></pre>
<p>在这里,shell将双引号视为一种保护。这使得shell无法解释<code>*</code>。shell<em>本身解释了双引号,因此<code>echo</code>没有看到双引号。它只看到星号<code>*</code></p>
<pre><code>$ echo '"*"'
"*"
</code></pre>
<p>这次我使用单引号作为外层。它们还告诉shell保护内容,即不要解释双引号<em>或</em>星号。所以这次<code>echo</code>看到了双引号和星号</p>
<p>shell的引用规则因shell而异,这可能会变得非常复杂。所有这些都与Git本身无关:不管运行哪个命令,shell都在执行相同的解释</p>
<p>如果您确实设法将文字星号传递给Git,<em>Git</em>将自己解释星号,并且它的解释不同于shell。因此<code>git add '*'</code>的行为不同</p>
<p>如果您使用Windows和<code>CMD.EXE</code>,请注意<code>CMD.EXE</code>本身的可编程性不如shell,并且具有更简单的命令行规则。在某些方面,这使得它的使用更容易和更可预测。这是它的优点,也是它的缺点:典型Unix shell的可编程性使使用它们编写整个系统成为可能</p>
<hr/>
<p><sup>1</sup>在Unix世界中,即使是目录(或文件夹,如果您喜欢该术语)也是一种文件。有时人们用<em>file</em>这个词来表示文件和目录,但因为我排除了<code>.</code>和<code>..</code>,这是目录文件,除了这个脚注,我不需要再提它了</p>
<p><sup>2</sup>在类似Unix系统的分层文件系统中,文件不一定只有一个名称。这里,单词<em>name</em>实际上是指在一些包含目录中找到的<em>组件名</em>。例如,Git中的文件名可能是<code>path/to/.file</code>。但是,在Unix文件系统级别,这是一个名为<code>path</code>的目录,其中包含一个名为<code>to</code>的目录,其中包含一个组件名为<code>.file</code>的文件。对于<em>shell</em>,文件名的末尾部分有一个点,因此<code>echo path/to/*</code>将不匹配它</p>
<p><sup>3</sup>这种将顶级命令与子命令结合使用的模式最近变得越来越普遍,但只有部分Unix系统是这样工作的。特别是在Python中,您可以使用<code>argparse</code>来模拟这种行为</p>