Shell 技巧 - 更多特殊变量

作者:Dave Taylor

我知道这可能会给Linux Journal的编辑工作带来一些麻烦,但在花了两个月的时间研究如何分析英语中字母的使用频率,以便在Hangman游戏中给你优势之后(是的,我简直不敢相信我竟然写了这些东西),现在是时候回到我们基本的 shell 变量引用功能之旅了。

在之前的专栏中,我们讨论了 ${var:-alt value}、${var:=alt value}、${var:?no value} 甚至 ${var:start:length},作为从变量中提取特定字符范围的方法。

本月,我想看看一些可能更晦涩难懂的变量引用——如果你深入脚本编写,这些调用绝对会有所帮助。 我认为你不会在那些五行快速脚本中需要它们,但是当你的小项目扩展到十几个屏幕,并且你有七个函数和十几个数组时,嗯,这些对你来说将非常有价值。

扩展和匹配

在之前的专栏中,我展示了如何使用 ${var:start:length} 形式的 shell 变量进行子字符串扩展,但是了解变量值的长度也很有用。 这可以使用 ${#var} 来完成,就像这样

$ test="the rain in Spain"
$ echo ${#test}
17

我在脚本中遇到的一个情况是需要以 value1、value2、value3 等形式设置任意数量的变量。 稍后,我需要确定我设置的变量的名称。 我的懒惰解决方案通常是另一个变量 valuecount,它计算我设置的变量的数量,但是,当然,这并没有直接给我名称。 一种更聪明的方法是使用 ${!pattern*} 表示法,如下所示

$ echo ${!t*}
test
$ thimble="full"
$ tart="pop"
$ echo ${!t*}
tart test thimble

正如你所看到的,它允许你获取与指定模式匹配的已定义变量列表。 我在示例中使用了 t*,但它也可以很容易地是 value* 以匹配前面概述的情况。

模式替换

这是一个你可以使用 bash shell 完成的很酷的事情,我敢打赌你没有意识到:模式替换。 当我遇到需要这种情况时,我几乎总是使用笨拙且 CPU 成本很高的形式:

var=$(echo $var | sed 's/old/new')

实际上,可以使用 shell 本身通过使用 ${var/old/new} 形式来简洁地完成。 我没有骗你!看看这个例子

$ test="The Rain in Spain"
$ echo ${test/ain/ixn}
The Rixn in Spain

如果你像我一样,你的手指会渴望在替换中添加 /g 后缀。 事实证明,在 shell 变量中,这是以有点不同的方式完成的:你需要让模式以 / 开头,这看起来有点奇怪,但它确实有效

$ echo ${test//ain/ixn}
The Rixn in Spixn

这里的一般情况是 ${var//pat/global subst}。 你也可以使用这种表示法做更多的事情——特别是,使用你可能在 sed 正则表达式中使用的 ^ 和 $ 特殊字符的等效项,将模式锚定到变量值的开头或结尾

$ echo ${test/#ain/ixn}
The Rain in Spain
$ echo ${test/%ain/ixn}
The Rain in Spixn

在第一种情况下,模式与变量值的前几个字母不匹配(模式需要是“The”而不是“ain”),因此没有任何改变。 但是,在第二种情况下,它确实与最后几个字符匹配,因此发生了替换。

公平地说,使用 sed 确实给你带来了更多的功能和能力,但是如果你只是做一些简单的事情,例如删除扩展名并将 PID 附加到变量以创建一个快速临时文件,你确实可以直接使用 shell 模式替换

$ test="The Rain in Spain.txt"
$ echo ${test/%.*/}.$$
The Rain in Spain.10126

就我个人而言,我认为这非常酷!

命令替换

除了深入研究数组之外,我们已经探讨了几乎所有你可以用变量做的事情,我们将在下个月进行数组的讲解,所以我认为我想花一点空间向你展示一些巧妙的命令替换技巧。 首先,我们这些老派人士习惯于使用反引号将命令嵌入到另一个命令中,如下所示

echo the date is `date`

这很常用,但实际上,更好且肯定更易读的表示法约定是使用 $() 代替,正如我之前展示的那样。 这在功能上是相同的

echo the date is $(date)

使用这种表示法给你带来了一些有趣的功能。 例如,代替$(cat file),你可以简单地使用$(< file)使文件的内容出现。

与 shell 的情况一样,字段的解析时间和地点也很重要。 看看下面这个

$ echo the date is $(date)
the date is Wed Feb 4 08:08:35 MST 2009
$ echo the date is "$(date)"
the date is Wed Feb  4 08:08:43 MST 2009

通过在 $(date) 的第二次调用周围添加双引号,你可以看到返回的值没有被 shell 解析和规范化:请注意第二个输出中 Feb 和 4 之间的两个空格,而第一个输出中只有一个空格。

我希望我不需要告诉你如果使用单引号而不是双引号会发生什么——哦,管他的

$ echo the date is '$(date)'
the date is $(date)

毫不奇怪——单引号禁用 shell 扩展,就像在这种情况下一样

$ echo The '$HOSTNAME' is $HOSTNAME
The $HOSTNAME is soyvah33

这就引出了一个经典问题:如果你真的想让这些引号成为输出的一部分怎么办? 这有点复杂,但这有效

$ echo The '$HOSTNAME' is \'$HOSTNAME\'
The $HOSTNAME is 'soyvah33'

让我们在这里结束,下个月,我们将深入研究经常令人困惑的 shell 脚本数组世界。

Dave Taylor 自 1980 年首次登录 ARPAnet 以来就一直参与 UNIX。 这意味着,是的,他即将迎来 30 周年纪念。 你几乎可以在网上任何地方找到他,但从这里开始:www.DaveTaylorOnline.com

加载 Disqus 评论