快捷搜索:

UNIX 新手指南: 一些很好的 Shell 诀窍

当编写 Shell 法度榜样时,您平日会碰到一些特殊的环境,盼望采纳自动要领处置惩罚。本教程包括一些关于此类环境的 Bourne Shell 脚本示例。这些环境包括字符串的进制转换(十进制到十六进制、十六进制到十进制、十进制到八进制,等等)、在管道轮回中读取键盘、Subshell 履行、内联输入、为目录中的每个文件履行一次敕令,以及应用多种措施构造继续轮回。本系列文章的第 4 部分总结了一批履行有用功能的 Shell 单敕令行法度榜样。

开始之前

懂得本教程中包孕的内容以及若何最好地使用本教程。

关于本系列

本系列教程主要针对新用户撰写,简要先容 UNIX® 基础观点。本系列教程的前三篇文章站在拥有 Microsoft® Windows® 背景的新用户的角度重温了一遍 UNIX 系统,讲述了文件系统和常用敕令,先容了 vi(最常见的 UNIX 编辑器),并且经由过程应用 grep、sed 和 awk 对象简要先容了筛选器和正则表达式。

关于本教程

本教程先容了一套新用户易于掌握的诀窍和技术。阐明在特定环境下,若何应用在 Bourne Shell 中编写的小脚本自动履行操作,包括自动履行进制转换、读取键盘输入、在 Subshell 中履行敕令、为目录中的所有文件履行相同敕令,以及多种形式的轮回。本教程着末以一套实用的 Shell 单敕令行法度榜样作为停止。

目标

本教程的目标是向新用户先容若何应用和实现许多在各类级别上供给自动化操作的 Shell 措施。本教程经由过程供给针对特定环境的诀窍和技术来阐明这些措施,并且供给适用于常见义务的 Shell 单敕令行法度榜样的概要性先容。

先决前提

本教程面向相对不认识 UNIX 的用户。独一的先决前提是懂得 UNIX 文件系统的基础常识和操作敕令、敕令行本身,以及能够应用类似 vi 的编辑器编写文本文件。本系列教程的前面部分对这些观点作了周全阐明。

系统要求

您必要在带有 Bourne 兼容 Shell 情况(例如 bash)的 UNIX 系统上拥有用户级造访权限。这是本教程独一的系统要求。

Shell 敕令履行

进修 Shell 脚本的最佳措施是经由过程示例。对付您要在脚本中履行的任何敕令都可以在敕令行上急速考试测验,这也是本教程通篇供给大年夜量实践示例的缘故原由所在。例如,echo 敕令将一行文本写入到标准输出。(许多 Shell 以内置敕令形式供给其自己版本的 echo 敕令,包括 IBM AIX® 的 Bourne Shell 实现。假如这也是您的现实环境,那么当您运行 echo 时,实际上正在运行您的 Shell 版本的敕令。)

引用

考试测验在应用 echo 输出短消息时加引号:

$ echo "Hello, world"

Hello, world

Shell 引用(无论在敕令行照样在脚本中加注)是一种将字符串通报给 Shell 的措施,可以避免对字符串中可能包孕的任何特殊元字符孕育发生肴杂。当字符串包孕一个以上的单词或者段落包孕空格字符时应用引用。假如单个字符正好是 Shell 元字符,并且您想去除它的特殊含义,就可以在两边加上引号,例如,当您要通报一个美元符号 ($) 作为字面上的美元符号字符而不是作为变量名前的特殊元字符时。

在引用的文本内部发生各类扩展。例如,在双引号括起来的文本中,变量被展开为它们的值,而单引号括起来的文本内部引用的变量名则不展开。

有三种紧张的引用类型必要懂得:

经由过程在前面加反斜杠 () 引用单个字符。这样只会传替字符的字面含义,而非它可能包孕的任何特殊含义,比如空格符或 Shell 元字符。例如,应用 * 引用一个星号 (*),它是 Shell 元字符。要引用真正的反斜杠字符,可以应用 。

经由过程在文本字符串两边加双引号 (") 来通报扩展的引用。美元符号 ($) 和单引号 (') 字符会保留其自身含义。是以,和其他字符一路在引用中呈现的任何变量名都邑被它们的值所替代。新行或特定字符 ($`") 前的反斜杠被移除,然则引用的字符会被通报。

应用单引号 (') 将文本括起来以通报文本字符串的字面引用,所有的变量名、元字符等都作为翰墨字符,而不它们的含义或值来通报。

请留意在不合的 Shell 中引用切实着实切规则会有所差别。参考您所应用的特殊 Shell 的 man 页面来懂得准确规则。

分配一个变量,然后考试测验应用各类引用款式输出该变量,如清单 1 中所示。

清单 1. 应用 echo 演示 Shell 变量引用款式

$ myvar = "Hello, world"

$ echo $myvar

Hello, world

$ echo "$myvar"

Hello, world

$ echo '$myvar'

$myvar

$ echo $myvar

$myvar

$ echo '$myvar'

'Hello, world'

$ echo "'$myvar'"

'Hello, world'

$ echo '"$myvar"'

"$myvar"

$ echo "$myvar"

"Hello, world"

留意解释变量的要领取决于所应用的引用款式。

注释

在 Shell 中,以井号 (#) 开始一个注释行。井号及其后面跟随的同一行的所有内容都被轻忽。考试测验输入几行夹杂注释的文本,如清单 2 中所示:

清单 2. 在 Shell 中应用注释

$ # a comment does nothing

$ echo "Hello, world" # This text is ignored

Hello, world

$ echo # This will not output

$ echo 'But a hash (#) can be quoted'

But a hash (#) can be quoted

$ echo "# Even in double quotes"

# Even in double quotes

$

创建 Shell 脚本

正如您所看到的,您可以直接在敕令行测试这些 Shell 编程布局。然则,当您完成了单行敕令的进修并且真正开始构建更长的法度榜样时,您必要将法度榜样写入称为脚本的文件。脚本 是一个设置了可履行位的文本文件,并且包孕由 Shell 说话敕令组成的法度榜样。UNIX Shell 是一种解释性说话,这意味着它的法度榜样不颠末编译,而是由说冥器读取,说冥器本身是 Shell 可履行法度榜样,比如 /bin/sh、/bin/bsh 或 /bin/bash。

Shell 脚本的第一行平日都是相同的:

#!/bin/sh

这是 Shell 自己应用的一种特殊注释,用于确定文件的说话或目录。感叹号在 UNIX 和排版术语中经常被称为 bang,后面跟随的路径名奉告 Shell 应该应用来履行该文件的说冥器。在本例中是 /bin/sh,它在许多系统中代表 Bourne Shell 可履行法度榜样本身。举例来说,分外为 Korn Shell 编写的脚本应该以 #!/usr/bin/ksh 开始,正如 Ruby 脚本将以 #!/usr/bin/ruby 开始。安装 bash 之后,/bin/sh 平日是到 bash 二进制法度榜样的符号链接。并且斟酌到兼容性,应用 /bin/sh 比应用 /bin/bash 更可取。在一些系统中,比如 IBM AIX 5L™,Bourne Shell 可履行法度榜样的名称是 bsh,并且位于 /usr/bin/bsh。

清单 3 供给了 Shell 脚本的简短示例。

清单 3. Shell 脚本示例

#!/bin/sh

# This is a shell script

message = "Hello, world!"

echo "The message is '"$message"'"

按照本系列教程前面文章中的阐明,应用 vi 编辑器键入该脚本并保存到名为 myscript 的文件中(请拜见参考资料部分)。然后应用 chmod 设置该文件的履行权限,使该文件可以履行:

$ chmod u+x myscript

此敕令使该文件只能由您履行。假如盼望系统中的所有用户都能履行该文件,那么您还可以为所有用户设置履行权限:

$ chmod a+x myscript

现在您可以运行该脚本。给出该文件的名称和相对付当前事情目录的路径,在路径中应用一个点字符 (.) 来表示:

$ ./myscript

The message is 'Hello, world!'

$

Shell 变量 PATH 包孕一组以冒号分隔的目录。它便是您的路径,Shell 老是会“看到”这些目录中的所有文件。UNIX Path 的目的是为了便于运行二进制文件。这便是为什么您只必要键入敕令的基础文件名,比如 ls 和 echo,而不用供给它们的完备或相对路径名。假如您将脚本移动到 Path 中的目录,那么只需键入它的名字就可以运行。详细的 Path 取决于您的 UNIX 实现和本地设置,但 Path 中的目录平日包括 /bin、/usr/bin 和 /usr/local/bin。

一些用户对它们的 Shell 进行设置设置设备摆设摆设,从而使 PATH 变量包括当前的事情目录,这在 Path 中以点字符 (".") 表示。如斯一来,要在当前目录下运行脚本,只必要键入它的名称,不必要指出相对目录。,Shell 按给定的顺序搜索 Path中的目录,从而避免中木马或发生非常环境,一种极其不明智的做法是把当前事情目录放在 Path 的末端。

要查看您的 Path,可以应用 echo 显示 PATH 变量的内容,如清单 4 所示。

清单 4. 变动 PATH

$ echo $PATH

/usr/local/bin:/usr/bin:/bin:/usr/bin/X11

$ myscript

myscript: command not found

$ PATH = $PATH":."

$ echo $PATH

/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:.

$ myscript

The message is 'Hello, world!'

$

在说冥器名称的后面可以附加特殊选项或标志,比如 /usr/bin/bsh -n,这用于调试目的。连字符关闭选项,加号则打开选项。特殊的内置情况变量 -(一个连字符)包孕当前 Shell 的完备选项列表。

考试测验在您当前的交互式 Shell 中设置了哪些选项。经由过程应用 echo 显示 - 变量的内容来完成这项义务:

$ echo $-

himBH

$

参考您应用的 Shell 的 man 页面来获取当前的标志和选项列表。表 1 供给了 AIX® 上的 Bourne Shell 的常用标志列表,以及对每种标志感化的简要阐明。

表 1. AIX Bourne Shell 的常用选项

标志

描述

-a

导出所有已分配值的变量。

-c Variable

履行从变量 中读取的敕令。

-e

当敕令满意以下前提之一时急速退出:敕令退出时返回比 0 大年夜的值;敕令不是 while、until 或 if 布局的一部分;敕令不颠末 AND 或 OR 检测;或者敕令不是管道前加感叹号。

-f

禁用所有文件名调换。

-h

定义函数时,定位和记着函数内部调用的所有敕令。

-i

指定交互式 Shell。

-k

将所有关键字 都放入敕令的情况。

-n

读取敕令,然则不履行它们。

-r

调用受限定的 Shell。

-s

从标准输入读取敕令,然后将输出写入标准差错(不包括 Shell 内置敕令的输出)。

-t

读取并履行单个敕令,然退却撤退出。

-u

在脚本中,将所有不决义 变量视为差错。当考试测验变量调换时退出。

-v

当读取输入行时将其显示出来。

-x

在履行敕令之前显示其完备敕令(包括所有的参数和选项)。

Shell 运算和进制转换

Shell 供给大年夜量的基础运算操作,在脚本中异常有用。Shell 对您供给的算术表达式求值,执交运算展开式,此时应用得出的结果调换表达式。以下面的款式供给运算表达式:

$(( expression ))

您可以应用 echo 在敕令行显示运算展开式的结果,懂得其事情环境。现在考试测验清单 5 所显示的结果。

清单 5. Bourne Shell 中的运算展开式

$ echo $((10+40))

50

$ echo $((5*(3+3)))

30

您还可以将展开式分配给变量。考试测验清单 6 所显示的结果。

清单 6. 将运算展开式分配给 Shell 变量

$ myvar = 10

$ echo $myvar

10

$ echo $(($myvar-2))

8

$ myvar = $(($myvar+5))

$ echo $myvar

15

$ result = $(($myvar-10))

$ echo $result

5

$

表 2 列出了在大年夜多半 Bourne 以及与 Bourne 兼容的 Shell中可以应用的运算符。正如上面第二个示例,应用圆括号括起来的语句有更高的优先级。实际上,Shell 算术优先级平日根据 C 说话的规则来确定。

表 2. Shell 前提表达式

运算符

描述

+

-

*

/

%

求余

小于(1 代表真,0 代表假)

小于即是(1 代表真,0 代表假)

>

大年夜于(1 代表真,0 代表假)

>=

大年夜于即是(1 代表真,0 代表假)

按位向左移位:将给定的整数或第一个表达式向左移动第二个表达式表示的位数

>>

按位向右移位:将给定的整数或第一个表达式向右移动第二个表达式表示的位数

应用 Shell 运算进行进制转换

假定在您的脚本中有一些数字,您必要以别的的进制处置惩罚这些数字。应用 Shell 运算可以很轻易地自动实现这类转换。一种环境是应用 Shell 运算把一个数字从给定的进制转换位十进制。假如数字以运算展开式的形式供给,那么假定它带有十进制符号,除非 它前面带有 0(这种环境假定是八进制)或 0x(这种环境假定是十六进制)。键入以下内容以获得一些八进制和十六进制值的十进制输出:

$ echo $((013))

$ echo $((0xA4))

您还可以应用以下款式指定 2 到 64 之间的随意率性进制:

$((BASE#NUMBER))

经由过程在 Shell 提示符后键入清单 7 中所示的行,考试测验将二进制、八进制、十六进制以及其他进制的数转换为十进制。

清单 7. 在 Shell 中将随意率性进制的数以十进制输出

echo $((2#1101010))

echo $((8#377))

echo $((16#D8))

echo $((12#10))

echo $((36#ZZYY))

应用 bc 进行进制转换

在 Shell 中进行进制转换的另一个诀窍是应用 bc,它是一种随意率性精度运算说话,大年夜多半 UNIX 安装法度榜样都供给。由于它容许您指定输出进制,以是当您必要以十进制以外的进制输出时,这是一种很好的技巧。

bc 的特殊变量 ibase 和 obase 分手包孕用于输入和输出的进制的值。缺省环境下,都被设置为 10。要履行进制转换,必要改变此中的一个或两个值,然后供给一个数字。急速考试测验,如清单 8 中所示。

清单 8. 应用 bc 履行进制转换

$ bc -ql

10

10

obase=16

10

A

ibase=2

10

2

Control-D

$

要快速履行进制转换,可以联合应用 bc 和 echo形成快捷的单敕令行法度榜样,将给定的值经由过程管道传输给 bc。键入清单 9 中显示的内容。

清单 9. Shell 单敕令行 bc 法度榜样

$ echo 'obase=16; 47' | bc

2F

$ echo 'obase=10; ibase=16; A03' | bc

2563

$

警告:当您设置 bc 的输入进制今后,输入 bc 的所稀有字都应用该进制,包括您供给用于设置输出进制的数字。是以最好先设置输出进制,否则可能会产买卖想不到的结果,如清单 10 中所示。

清单 10. 设置输入和输出进制的先后顺序的紧张性

$ echo 'ibase=16; obase=10; A' | bc

A

$ echo 'ibase=16; obase=A; A' | bc

10

$

内联输入

只管 echo 经由过程管道将内容通报给交互式敕令(比如 bc)可以天生快捷的单敕令行法度榜样,然则它对付多行输入并不适用,比如可能用到实际文件中的内容。然则别的一种有用的措施可以完成这个义务。Shell 有一种对象称为 here documents 或内联输入,这是一种动态构建文件的异常好的措施,比如用于脚本内部,并且将该文件的内容重定向到一个敕令。

应用 Shell $ cat  END of input text

> ENDspace

> This is still not the END

> ENDING SOON

> THE END

> END

END of input text

END

This is still not the END

ENDING SOON

THE END

$

限制字符串(本例中是 END)可以呈现在输入的任何地方,只有当它以独有一行并且不含空格或其他字符的形式呈现时,才表示输入的停止。

脚本中的内联输入

在脚本中常常应用内联输入将应用信息输出到标准输出。这平日经由过程将 here document 发送给 cat 来完成,如清单 12 中的脚本所示。应用 vi 输入该脚本并保存到名为 baseconv 的文件中,并且将该文件设置为可履行文件(请拜见创建 Shell 脚本部分)。

清单 12. 应用 here document 供给 Shell 脚本应用信息

#!/bin/sh

cat

当履行该脚本时,here document 的内容被发送到(应用 cat)标准输出。急速考试测验,如清单 13 中所示。

清单 13. 从 here document 输出 Shell 脚本应用信息

$ baseconv

baseconv is a program to convert a number from one base to another.

Usage: baseconv [options]

Options:

-i  BASE input base

-o  BASE output base

-h  display this message

For more information, consult the baseconv man page.

$

此外,Bourne Shell 的大年夜多半实现容许呈现应用可选的连字符重定向的内联输入。可选的连字符将所有的前导 Tab 字符从所有输入行的前面去掉落,也包括包孕限制字符串的行。这对付您盼望让编写的脚本维持当前缩进时会有赞助。因为内联输入平日逐字读取,并且限制字符串必须在行的开始处给出,是以输入将打乱您确当前缩进并使脚本看起来不都雅不雅。是以,您可以重写清单 12 中的脚本,使其与清单 14 同等,而输出不会改变。

清单 14. 带前导缩进的 Shell 脚本 here document

#!/bin/sh

cat

在敕令行应用内联输入

在敕令行中,应用调用交互式法度榜样的单敕令行法度榜样进行内联输入,比如在应用 bc 进制转换部分评论争论的 bc 谋略法度榜样。在随意率性交互式敕令中,您可以应用 here document 代替实际文件,或代替随意率性行的实际输入。

考试测验应用 here document 将多行输入发送到 bc。键入清单 15 中显示的内容。

清单 15. 将内联输入发送到交互式法度榜样

$ bcibase=16

> A

> EOF

10

$

平日应用内联输入来扩展变量。考试测验清单 16 中显示的内容。

清单 16. 内联输入若何扩展变量

$ BASECON=16

$ bcibase=16

> $BASECON

> EOF

22

$

Subshell 履行

可以在一个名为 subshell 的新 Shell 中履行一个或一组敕令,当前 Shell 是 SubShell 的父 Shell。Subshell 承袭父亲的情况。I/O 重定向可以呈现在子 Shell 和父 Shell 之间,然则 Subshell 永世不能改动父情况。当您为了履行这些敕令(比如设置变量)要变动 Shell 的情况,并且不想变动脚本自身运行所在的情况时,这便是您所期望的技巧。当您想要同时在后台启动多个长光阴运行的进程时也最好应用 Subshell。一个 Shell 可以天生多个 Subshell,而 Subshell 又可以轮回天生属于它们自身的随意率性数量的 Subshell。图 1 阐清楚明了这个历程。

图 1. Subshell 若何与它的父 Shell 交互

Shell 无意偶尔自动天生自身的 Subshell,比如在管道中应用内置敕令时。在 Subshell 中,Shell $ 参数扩展到父 Shell 而不是 Subshell 的进程 ID (PID)。

在 Subshell 中运行敕令

要在 Subshell 中运行一组敕令,可以应用括号将其括起来。您可以应用重定向将输入发送到 Subshell 的标准输入,或将 Subshell 的聚拢输启程送到文件或管道。

考试测验在您的 home 目录键入清单 17 中显示的内容。该示例创建一个 example 目录和一些测试文件,条件是原本不存在 example 目录。

清单 17. 在 Subshell 中创建一组文件

$ pwd

/home/user

$ (mkdir example; cd example; touch A B C)

$ pwd

/home/user

$ cd example; ls

A B C

$ pwd

/home/user/example

$

在本例中,Shell 天生一个在后台运行的 Subshell,建立 example 目录,然后应用 touch 在该目录中天生三个虚拟文件。同时,Shell 返回 home 目录的敕令行。

当您有一组履行光阴长的敕令时,在敕令行和脚本中应用 Subshell 都很方便。为了让 Shell 维持余暇,您可以在后台运行 Subshell,或者在后台运行许多个 Subshell。

( group-of-long-running-commands ) &

( another-group-of-long-running-commands ) &

( yet-another-group-of-long-running-commands ) &

Subshell 和变量

理解变量与 Subshell 的交互要领异常紧张。由于 Subshell 情况是其父亲的副本,以是它承袭了父亲的所有变量。然则父 Shell 从不会看到 Subshell 情况发生的任何变更,同样,Subshell 天生今后,再也不会看到父亲发生的任何变更。

作为示例,应用 vi 编辑器将清单 18 中的脚本保存到 home 目录的 vartest 文件中,然后将其设置为可履行(请拜见编写 shell 脚本部分)。

清单 18. 演示 Subshell 中变量行径的 Shell 脚本

#!/bin/sh

# Demonstrates variable behavior in a subshell environment

VAR=10

echo "VAR is" $VAR

(

echo "In the subshell, VAR is still" $VAR

VAR=$(($VAR+5))

echo "The new value of VAR in the subshell is" $VAR

)

echo "Outside of the subshell, VAR is" $VAR

现在考试测验经由过程键入脚本的名称来履行它,如清单 19 中所示。

清单 19. vartest 脚本的输出

$ vartest

VAR is 10

In the subshell, VAR is still 10

The new value of VAR in the subshell is 15

Outside of the subshell, VAR is 10

$

继续轮回

现在来看轮回,它容许您履行重复义务,比如对一组文件履行一些操作或敕令。Shell 有几种构造轮回的措施。

构造 for 轮回

最常见的轮回布局是 for 轮回。首先定义一个变量作为轮回的名称,供给一组成员,可所以包括整数和文件名在内的任何单词,然后供给每次重复履行的敕令。每个敕令都以分号停止 (;),全部敕令组以位于单词 do 和 done 之间。清单 20 描述了它的布局。

清单 20. Shell 中轮回的布局

for loopname in members

do

command;

command;

...

command;

done

在轮回的第一次重复中,loopname 变量获取第一个成员的值。然后 loopname 的值被清单中下一个成员的值替代,接下来它继承重复直到遍历所有成员。

在大年夜多半 Shell 中,do 和 done 都可以被大年夜括号所替代,如清单 21 中所示。

清单 21. Shell 轮回的替代布局

for loopname in members

{

command;

command;

...

command;

}

键入清单 22 中的文原先运行包孕三个成员的简单轮回:

清单 22. 应用轮回来改变变量的值

$ for i in 1 2 3

> {

> VAR = $(($VAR+$i))

> echo $i:$VAR

> }

1:1

2:3

3:6

$

针对目录中的每个文件履行敕令

您可以应用轮回针对给定的一组文件履行一个或一组敕令。假如您供给文件的名称作为 for 轮回的成员,那么轮回按您供给名称的顺序在每个文件上履行操作。您可以两次供给同一个文件,轮回将依次对该文件履行操作。在您的 example 目录中考试测验应用清单 23 中的文本履行上述操作。

清单 23. 使用一组文件构造轮回

$ cd ~/example

$ ls

A B C

$ for file in C B B C

> {

> echo $file

> }

C

B

B

C

$

要对同一目录下的所有文件履行操作,可以应用星号 (*) 作为轮回的独一成员,如清单 24 中所示。Shell 将星号扩展为目录中的所有文件。然后,对付轮回中您要对所有文件履行的敕令,应用 loopname 变量作为相宜的参数或选项。

清单 24. 针对目录中的所有文件履行同一敕令

$ ls

A B C

$ for file in *

> {

> mv $file $((0x$file))

> }

$

假如您正在运行本教程中的所有示例,那么您的 example 目录中的内容应该已改变:

$ ls

10 11 12

$

发生的环境是轮回中的 mv 敕令将文件的名称从十六进制值(经由过程在名称的前面插入 0x 构成)变动为与它相等的十进制值。

构造 while 轮回

您可以构造一种当满意某些前提就不停运行的轮回。应用 while 前提语句来实现这一目标,其款式如清单 25 所示。

清单 25. Shell while 轮回的布局

while [ condition ]; do

command;

command;

...

command;

done

在轮回中,condition 可所以应用操作符(请拜见表 3)构建的语句,或者可以像一个变量名那样简单。只要值长短 0 的,就代表真。

表 3. 常用 Shell 操作符

操作符

描述

-eq

即是

-ne

不即是

-lt

小于

-le

小于即是

-gt

大年夜于

-ge

大年夜于即是

构造 while 轮回时,有一些留意事变必要切记在心。首先,在前提与将它括起来的括号之间必须留有空缺字符。其次,假如在前提中将变量用于数字对照,那么在 while 语句之前必须首先定义该变量。

键入清单 26 中的文本以履行一个简短的 while 轮回:

清单 26. 应用 while 轮回变动变量

$ VAR=0

$ while [ $VAR -lt 10 ]; do

>  echo $VAR;

>  VAR=$(($VAR+1));

> done

0

1

2

3

4

5

6

7

8

9

$

构造 until 轮回

until 前提语句与 while 相似并应用相同的操作符,然则它们的行径相反。它只有当前提为假时才履行轮回,并且轮回持续重复直到 给定的前提为真。它的款式在清单 27 中阐明。

清单 27. Shell until 轮回的布局

until [ condition ] ; do

command;

command;

...

command;

done

经由过程键入清单 28 中所示的内容考试测验运行一个简短的 until 轮回:

清单 28. 应用 until 轮回变动变量

$ VAR=10

$ until [ $VAR -eq 0 ]; do

>  echo $VAR;

>  VAR=$(($VAR-1));

> done

10

9

8

7

6

5

4

3

2

1

$

嵌套多重轮回

您可以嵌套轮回和组合多种类型的轮回来履行各类类型的繁杂操作。因为 for 轮回的成员不必是数字或以随意率性类型的顺序排列,是以您可以应用稍后在某个内部轮回中作为敕令履行的敕令名称作为其成员,比如 printf、echo、stop、resume,等等。

考试测验运行清单 29 中的示例。这是一个履行算术调换的 until 轮回,同时嵌套在轮回词未按数字顺序排列的 for 轮回内部。

清单 29. 应用嵌套轮回进行算术调换

$ for i in 250 100 2136 875

> {

>  VAR=10;

>  until [ $VAR -eq 0 ]; do

>   echo "$i / $VAR = $(($i/$VAR)) $i * $VAR = $(($i*$VAR))

$i + $VAR = $(($i+$VAR)) $i - $VAR = $(($i-$VAR))";

>   VAR=$(($VAR-1);

>  done;

> }

250 / 10 = 25 250 * 10 = 2500 250 + 10 = 260 250 - 10 = 240

250 / 9 = 27 250 * 9 = 2250 250 + 9 = 259 250 - 9 = 241

250 / 8 = 31 250 * 8 = 2000 250 + 8 = 258 250 - 8 = 242

250 / 7 = 35 250 * 7 = 1750 250 + 7 = 257 250 - 7 = 243

250 / 6 = 41 250 * 6 = 1500 250 + 6 = 256 250 - 6 = 244

250 / 5 = 50 250 * 5 = 1250 250 + 5 = 255 250 - 5 = 245

250 / 4 = 62 250 * 4 = 1000 250 + 4 = 254 250 - 4 = 246

250 / 3 = 83 250 * 3 = 750 250 + 3 = 253 250 - 3 = 247

250 / 2 = 125 250 * 2 = 500 250 + 2 = 252 250 - 2 = 248

250 / 1 = 250 250 * 1 = 250 250 + 1 = 251 250 - 1 = 249

100 / 10 = 10 100 * 10 = 1000 100 + 10 = 110 100 - 10 = 90

100 / 9 = 11 100 * 9 = 900 100 + 9 = 109 100 - 9 = 91

100 / 8 = 12 100 * 8 = 800 100 + 8 = 108 100 - 8 = 92

100 / 7 = 14 100 * 7 = 700 100 + 7 = 107 100 - 7 = 93

100 / 6 = 16 100 * 6 = 600 100 + 6 = 106 100 - 6 = 94

100 / 5 = 20 100 * 5 = 500 100 + 5 = 105 100 - 5 = 95

100 / 4 = 25 100 * 4 = 400 100 + 4 = 104 100 - 4 = 96

100 / 3 = 33 100 * 3 = 300 100 + 3 = 103 100 - 3 = 97

100 / 2 = 50 100 * 2 = 200 100 + 2 = 102 100 - 2 = 98

100 / 1 = 100 100 * 1 = 100 100 + 1 = 101 100 - 1 = 99

2136 / 10 = 213 2136 * 10 = 21360 2136 + 10 = 2146 2136 - 10 = 2126

2136 / 9 = 237 2136 * 9 = 19224 2136 + 9 = 2145 2136 - 9 = 2127

2136 / 8 = 267 2136 * 8 = 17088 2136 + 8 = 2144 2136 - 8 = 2128

2136 / 7 = 305 2136 * 7 = 14952 2136 + 7 = 2143 2136 - 7 = 2129

2136 / 6 = 356 2136 * 6 = 12816 2136 + 6 = 2142 2136 - 6 = 2130

2136 / 5 = 427 2136 * 5 = 10680 2136 + 5 = 2141 2136 - 5 = 2131

2136 / 4 = 534 2136 * 4 = 8544 2136 + 4 = 2140 2136 - 4 = 2132

2136 / 3 = 712 2136 * 3 = 6408 2136 + 3 = 2139 2136 - 3 = 2133

2136 / 2 = 1068 2136 * 2 = 4272 2136 + 2 = 2138 2136 - 2 = 2134

2136 / 1 = 2136 2136 * 1 = 2136 2136 + 1 = 2137 2136 - 1 = 2135

875 / 10 = 87 875 * 10 = 8750 875 + 10 = 885 875 - 10 = 865

875 / 9 = 97 875 * 9 = 7875 875 + 9 = 884 875 - 9 = 866

875 / 8 = 109 875 * 8 = 7000 875 + 8 = 883 875 - 8 = 867

875 / 7 = 125 875 * 7 = 6125 875 + 7 = 882 875 - 7 = 868

875 / 6 = 145 875 * 6 = 5250 875 + 6 = 881 875 - 6 = 869

875 / 5 = 175 875 * 5 = 4375 875 + 5 = 880 875 - 5 = 870

875 / 4 = 218 875 * 4 = 3500 875 + 4 = 879 875 - 4 = 871

875 / 3 = 291 875 * 3 = 2625 875 + 3 = 878 875 - 3 = 872

875 / 2 = 437 875 * 2 = 1750 875 + 2 = 877 875 - 2 = 873

875 / 1 = 875 875 * 1 = 875 875 + 1 = 876 875 - 1 = 874

$

读取键盘输入

您还可以在脚本中或从敕令行本身读取键盘输入。应用 read 敕令可以实现这一功能,这是一个内置函数,将随意率性数量的变量名作为参数。它从标准输入读取变量的值,读入单行输入并将各个输入词分配给各个变量。

考试测验读取一个变量,如清单 30 中所示:

清单 30. 应用 read 读取一个变量

$ read VAR

23

$ echo $VAR

23

$

应用 -p 选项为每次 read 供给提示。应用以引号括起来的字符串供给提示,如清单 31 中所示。发生变量扩展。

清单 31. 在变量读取时应用提示

$ read -p "Instead of $VAR, what number would you like? " VAR

Instead of 23, what number would you like? 17

$ echo $VAR

17

$

假如键盘输入的词比变量个数多,那么依次为变量分配输入的词,到着末一个变量时,为其分配输入行余下的部分。(假如输入的词比变量个数少,那么为变量分配值直到所有的输入都已分配,然后为所有残剩的变量分配空值。)

在轮回中读取

您可以在轮回中应用 read 作为前提表达式。现在应用清单 32 中的内容考试测验这一操作:

清单 32. 在轮回中读取一组文件名

$ while read -p "File? " file; do ls $file; done

File? 10

10

File? 12

12

File? 42

42: no such file or directory

File?

Carriage return

10 11 12

File?

Control-C

$

此技巧平日在对轮回的输入应用管道时应用。考试测验键入清单 33 中的文本,该文本应用轮回替代 ls 敕令的输出:

清单 33. 从管道读取

$ ls | while read file; do ls $file; done

10

11

12

$

您还可以跨多行操作变量,比如将一条消息发送到标准输出,然后对 loopname 变量履行 Shell 运算(请拜见 Shell 运算和进制转换部分)。考试测验清单 34 中供给的示例:

清单 34. 应用管道读取的较长轮回

$ ls | while read file; do echo "The file is " `ls -i $file`;

echo "If the number were in hex, the value would be $((16#$file))"; done

The file is 100267120 10

If the number were in hex, the value would be 16

The file is 100267121 11

If the number were in hex, the value would be 17

The file is 100267122 12

If the number were in hex, the value would be 18

$

您可以在一个管道输入的 read 中读取多个值,如清单 35 中所示。

清单 35. 从一个管道读取多个变量

$ ls -i | while read inode file; do

echo "File $file has inode $inode"; done

File 10 has inode 100267120

File 11 has inode 100267121

File 12 has inode 100267122

$

实际运用

此停止部分将您在前面学到的诀窍和技巧加以组合来实现在实际中有用的单敕令行法度榜样。它还包括一个简单的 Shell 脚本——履行随意率性进制的转换。

有用的单敕令行法度榜样

以下示例是履行有用功能的 Shell 单敕令行法度榜样样本。它们整个由本教程中描述的各类布局组成。

从当前目录中获取一组文件名正好为两个字符长的文件,并应用 .ppm 扩展名为其从新命名:

for i in ??; { mv $i $i.ppm; }

应用 tar 和 Subshell 复制全部目录树,同时维持相同的文件权限:

( cd source ; tar pcf - * ) | ( cd target ; tar pxvf - )

读取二进制数并以十进制输出值:

read BINLOC;echo $((2#$BINLOC))

在 /usr/local 目录树中找到所有带 .mp3 扩展名的文件(这些文件的名称中可能包孕空格字符),然后应用 bzip2 实用法度榜样压缩这些文件:

find /usr/local -name "*.mp3" | while read name ; do bzip2 $name; done

将给定文件中所有十进制数的值以十六进制输出:

cat file | while read number ; do echo $((0x$number)); done

将给定文件中所有十进制数转换为十六进制的值,并将值输出到带有 .hex 扩展名的新文件中:

cat file | while read number ; do echo $((0x$number)) >> file.hex; done

构造重复十次的轮回,以数字(从 0 到 90 以 10 递增)作为通报的参数运行 command:

i=0; while [ $i -ne 100 ]; do command $i; i=$(($i+10)); done

示例脚本:将数字转换为其他进制

本教程中评论争论的一些诀窍在清单 36 中被组合在一路。它是一个示例脚本——baseconv,将数字从给定的输入进制转换为输出进制。为它供给输入进制和输出进制的值作为参数,然后它从键盘输入读取数字,直到读取了数字 0。

清单 36. 转换进制的简单脚本

#!/bin/sh

# baseconv, convert numbers from one base to another.

#

NUMBER=1

while [ $NUMBER ]; do

read -p "Input base: " IN

read -p "Output base: " OUT

read -p "Number: " NUMBER

bc -ql

当您把它保存到可履行文件后(请拜见创建 Shell 脚本部分),考试测验运行该文件,如清单 37 中所示:

清单 37. baseconv 脚本的输出

$ ./baseconv

Input base: 10

Output base: 16

Number: 33

21

Input base: 2

Output base: 1100

Number: 101

5

Input base: 16

Output base: A

Number: ACA

2762

Input base: 10

Output base: 10

Number:

Carriage return

$

停止语

总结

噢!本教程确凿涵盖了许多内容,带您快速浏览了基础的 Shell 编程观点。在进修本教程的历程中,您懂得了有关 Shell 编程的许多核心观点:继续轮回、内联输入、读取键盘输入、进制转换及 Subshell 履行。您还懂得到 Shell 代码片段若何能够作为单敕令行法度榜样直接从 Shell 提示符上运行,以及若何将它们放在同一个文件中作为可履行脚本。您可以从中进修一些最紧张的脚本编程观点。您假如能综合运用在本教程以及本系列教程的前面部分学到的常识,那么您已成功地迈上 UNIX 专家之路。

您可能还会对下面的文章感兴趣: