Linux shell的展开模式【实操演示】


在这篇文章,我们将研究几个复杂而有趣的shell特性,我们只需要一个命令:

  • echo -显示一行文本

🏞️1. 字符展开

每当你输入一个命令并按下 enter 键,bash 会在执行你的命令之前对输入的字符完成几个步骤的处理。我们已经见过几个例子:例如一个简单的字符序列 “*”, 对 shell 来说有着多么丰富的涵义。这背后的的过程叫做(字符)展开。通过展开,你输入的字符,在 shell 对它起作用之前,会展开成为别的字符。为了说明这一点,让我们看一看 echo 命令。echo 是一个 shell 内建命令,可以完成非常简单的任务。它将它的文本参数打印到标准输出中。

image-20220827215121411

这个命令的作用相当简单明了。传递到 echo 命令的任一个参数都会在(屏幕上)显示出来。让我们试试另一个例子:

image-20220827215514388

那么刚才发生了什么事情呢?为什么 echo 不打印 ”*“呢?如果你回忆起我们所学过的关于通配符的内容,这个 "\*"字符意味着匹配文件名中的任意字符,但在原先的讨论中我们并不知道 shell 是怎样实现这个功能的。简单的答案就是 shellecho 命令被执行前把 “*” 展开成另外的东西(在这里,就是在当前工作目录下的文件名字)。当回车键被按下时,shell 在命令被执行前在命令行上自动展开任何符合条件的字符,所以 echo 命令的实际参数并不是 "\*",而是它展开后的结果。知道了这个以后,我们就能明白 echo 的行为符合预期

🌁2. 路径名展开

通配符所依赖的工作机制叫做路径名展开.如果我们试一下在之前的章节中使用的技巧,我们会看到它们实际上是展开。给定一个家目录,它看起来像这样:

image-20220827220301473

我们能够执行以下的展开:

image-20220827220328803

隐藏文件路径名展开:
正如我们知道的,以圆点字符开头的文件名是隐藏文件。路径名展开也尊重这种行为。像这样的展开:echo *不会显示隐藏文件
直觉告诉我们,如果展开模式以一个圆点开头,我们就能够在展开中包含隐藏文件,就像这样:echo .*
它几乎要起作用了。然而,如果我们仔细检查一下输出结果,我们会看到名字“.” 和 “…” 也出现在结果中。由于它们是指当前工作目录和父目录,使用这种模式可能会产生不正确的结果。我们可以通过这个命令来验证:
ls -d .* | less
为了在这种情况下正确地完成路径名展开,我们应该使用一个更精确的模式。这个模式会正确地工作:
ls -d .[!.]?*
这种模式展开成所有以圆点开头,第二个字符不包含圆点,再包含至少一个字符,并且这个字符之后紧接着任意多个字符的文件名。这个命令将正确列出大多数的隐藏文件(但仍不能包含以多个圆点开头的文件名)。带有 -A 选项(“几乎所有”)的 ls 命令能够提供一份正确的隐藏文件清单:ls -A

🏖️3. 波浪线展开

可能你从我们对 cd 命令的介绍中回想起来,波浪线字符 (“∼”) 有特殊的含义。当它用在一个单词的开头时,它会展开成指定用户的家目录名,如果没有指定用户名,则展开成当前用户的家目录:

image-20220827220920293

如果有用户 “temp” 这个帐号,那么:

[me@linuxbox ~]$ echo ~foo
/home/foo

🏜️4. 算术表达式展开

shell 在展开中执行算数表达式。这允许我们把 shell 提示当作计算器来使用:

image-20220827221149261

算术表达式展开用这种格式:

$((expression))

括号中的表达式指算术表达式.

算术表达式只支持整数(全部是数字,不带小数点),但是能执行很多不同的操作。这里是一些它支持的操作符:

操作符说明
+
-
*
/除(因为展开只支持整数除法,所以结果是整数)
%取余
**取幂

在算术表达式中空格并不重要,并且表达式可以嵌套。例如,4 的平方乘以 3:

image-20220827221724801

一对括号可以用来把多个子表达式括起来。通过这个技术,我们可以重写上面的例子,同时用一个展开代替两个,来得到一样的结果:

image-20220827221827231

🌿5. 花括号展开

可能最奇怪的展开是花括号展开。通过它,你可以从一个包含花括号的模式中创建多个文本字符串。这是一个例子:

image-20220827222009958

花括号展开模式可能包含一个开头部分叫做报头,一个结尾部分叫做附言。**花括号表达式本身可能包含一个由逗号分开的字符串列表,或者一个整数区间,或者单个的字符的区间。**这种模式不能嵌入空白字符。这个例子中使用了一个整数区间:

image-20220827222049106

花括号展开可以嵌套:

image-20220827222301497

那么这对什么有好处呢?最常见的应用是,创建一系列的文件或目录列表。例如,如果我们是摄影师,有大量的相片。我们想把这些相片按年月先后组织起来。首先,我们要创建一系列以数值 “年-月” 形式命名的目录。通过这种方式,可以使目录名按照年代顺序排列。我们可以手动键入整个目录列表,但是工作量太大了,并且易于出错。反之,我们可以这样做:

image-20220827222505498

🍁6. 参数展开

参数展开这个特性在 shell 脚本中比直接在命令行中更有用。它的许多功能和系统存储小块数据,并给每块数据命名的能力有关系。许多像这样的小块数据,更恰当的称呼应该是变量,可供你方便地检查它们。例如,叫做 “USER” 的变量包含你的用户名。可以这样做来调用参数,并查看 USER 中的内容:

image-20220827222633377

要查看有效的变量列表,可以试试这个:

printenv | less

你可能注意到在其它展开类型中,如果你误输入一个模式,展开就不会发生。这时 echo 命令只简单地显示误键入的模式。但在参数展开中,如果你拼写错了一个变量名,展开仍然会进行,只是展开的结果是一个空字符串

image-20220827222827470

🍃7. 命令替换

命令替换允许我们把一个命令的输出作为一个展开模式来使用:

image-20220827223000082

🌴8. 引用

我们已经知道 shell 有许多方式可以完成展开,现在是时候学习怎样来控制展开了。以下面例子来说明:

image-20220827223259245

在第一个例子中,shell 利用单词分割删除掉 echo 命令的参数列表中多余的空格。在第二个例子中,参数展开把 $5 的值替换为一个空字符串,因为 5 是没有定义的变量。shell 提供了一种叫做引用的机制,来有选择地禁止不需要的展开。

📖8.1 双引号

我们将要看一下**引用的第一种类型,双引号。如果你把文本放在双引号中,shell 使用的特殊字符,都失去它们的特殊含义,被当作普通字符来看待。有几个例外:$\ (反斜杠),和 (倒引号)。**这意味着单词分割、路径名展开、波浪线展开和花括号展开都将失效,然而参数展开、算术展开和命令替换仍然执行。使用双引号,我们可以处理包含空格的文件名。比方说我们是不幸的名为 two words.txt 文件的受害者。如果我们试图在命令行中使用这个文件,单词分割机制会导致这个文件名被看作两个独自的参数,而不是所期望的单个参数:

[me@linuxbox ~]$ ls -l two words.txt
ls: cannot access two: No such file or directory
ls: cannot access words.txt: No such file or directory

使用双引号,我们可以阻止单词分割,得到期望的结果;进一步,我们甚至可以修复破损的文件名:

image-20220827225641778

记住,在双引号中,参数展开、算术表达式展开和命令替换仍然有效.

我们来看一下双引号在命令替换中的效果。首先仔细研究一下单词分割是怎样工作的。在之前的范例中,我们已经看到单词分割机制是怎样来删除文本中额外空格的:

image-20220827225823777

在默认情况下,**单词分割机制会在单词中寻找空格,制表符,和换行符,并把它们看作单词之间的界定符。这意味着无引用的空格,制表符和换行符都不是文本的一部分,它们只作为分隔符使用。**由于它们把单词分为不同的参数,所以在上面的例子中,命令行包含一个带有四个不同参数的命令。如果我们加上双引号:

image-20220827225956365

单词分割被禁止,内嵌的空格也不会被当作界定符,它们成为参数的一部分。一旦加上双引号,我们的命令行就包含一个带有一个参数的命令。

考虑下面的例子:

image-20220827230135456

事实上,单词分割机制把换行符看作界定符,对命令替换产生了一个虽然微妙但有趣的影响,在第一个实例中,没有引用的命令替换导致命令行包含 38 个参数。在第二个例子中,命令行只有一个参数,参数中包括嵌入的空格和换行符。

📖8.2 单引号

如果需要禁止所有的展开,我们要使用单引号。以下例子是无引用,双引号,和单引号的比较结果:

[me@linuxbox ~]$ echo text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
text /home/me/ls-output.txt a b foo 4 me
[me@linuxbox ~]$ echo "text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER"
text ~/*.txt {a,b} foo 4 me [me@linuxbox ~]$ echo 'text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER' text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER

正如我们所看到的,随着引用程度加强,越来越多的展开被禁止.

🍀9. 转义字符

有时候我们只想引用单个字符。我们可以在字符之前加上一个反斜杠,在这里叫做转义字符。经常在双引号中使用转义字符,来有选择地阻止展开:

image-20220827230638778

使用转义字符来消除文件名中一个字符的特殊含义,是很普遍的。例如,在文件名中可能使用一些对于 shell 来说有特殊含义的字符。这些字符包括 “$”, ”¡‘, ” ” 等字符。在文件名中包含特殊字符,你可以这样做:

image-20220827231101771

为了允许'&'字符出现,输入 “\” 来转义。注意在单引号中,反斜杠失去它的特殊含义,它被看作普通字符。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉默.@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值