Unix Shell 介绍(2)

本文深入探讨了Shell编程的关键概念,包括立即文档、shell变量、test命令、控制流(如while循环和if分支)、命令组合、调试技巧以及man命令的使用。通过具体的例子,展示了如何在Shell中高效地进行文件操作、变量赋值与引用、测试文件属性、循环执行命令以及调试复杂过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2.3 立即文档
译注:here document 翻译成立即文档属于意译,参照寻址方式中立即寻址的先例。


在章节 2.1 中 shell 过程 tel 使用文件 /usr/lib/telnos 来为 grep 提供数据。一种替代方式是在这个 shell 过程中包含这些数据作为立即文档,如

 for i do grep $i <<! ... fred mh0123 bert mh0789 ... ! done
在这个例子中 shell 接收在 <<! 和 ! 之间的行作为 grep 的输入。字符串 ! 是任意的,这个文档被由紧随 << 之后的字符串构成的一行所终结。

下面的过程 edg 展示了文档在提供给 grep 之前,要替换其中的参数。

 ed $3 <<% g/$1/s//$2/g w %
调用

 edg string1 string2 file
等价于命令

 ed file <<% g/string1/s//string2/g w %
并在 file 中把所有出现的 string1 改变成 string2。使用 \ 引用特殊字符 $ 来防止替换如

 ed $3 <<+ 1,\$s/$1/$2/g w +
(这个版本的 edg 除了 ed 在没有字符串 $1 出现时打印一个 ? 之外等同于第一个版本)。通过引用终结字符串可以完全防止在立即文档内的替换,例如,

 grep $i <<\# ... #
文档被不加修改的提供给 grep。如果在立即文档中不需要参数替换,后一种形式更有效率。

2.4 shell 变量
shell 提供字符串值的变量。变量名字开始于一个字母并由字母、数字和下划线组成。可以通过下列写法给出变量的值,例如,

 user=fred box=m000 acct=mh0000
它向变量 user、box 和 acct 赋值。可以通过下列写法设置一个变量为空串,例如,

 null=
通过把 $ 前导于变量的名字来把它替换成变量的值;例如,

 echo $user
将回显 fred。


可以交互式的使用变量为经常使用的字符串提供简写。例如,

 b=/usr/fred/bin mv pgm $b
将把文件 pgm 从当前目录移动到目录 /usr/fred/bin。对于参数(或变量)替换可以有一种更一般的记号,如

 echo ${user}
它等价于

 echo $user
并在参数名字后跟随着一个字母或数字的时候有用。例如,

 tmp=/tmp/ps ps a >${tmp}a
将把 ps 的输出定向到文件 /tmp/psa,而

 ps a >$tmpa
将导致替换变量 tmpa 的值。


除了 $? 下列都是由 shell 作最初的设置。$? 在每次命令执行之后设置。

$?
最近执行的命令的退出状态(返回代码),是一个十进制数字符串。多数命令如果成功完成则返回一个零退出状态,否则返回一个非零退出状态。测试返回代码的值在以后的 if 和 while 命令中处理。
$#
位置参数的数目(十进制)。例如用在 append 命令中检查参数的数目。
$$
这个 shell 的进程编号(十进制)。因为过程编号在现存的进程中是唯一的,这个字符串经常用来生成唯一的临时文件名字。例如,
 ps a >/tmp/ps$$ ... rm /tmp/ps$$
$!
在后台运行的最后的进程的编号(十进制)。
$-
当前的 shell 标志,比如 -x 和 -v。
一些变量对 shell 有特殊意义并应该避免作一般使用。
$MAIL
在交互使用的时候,shell 在发出提示之前察看这个变量指定的文件。如果指定的文件自从上次察看之后已经被修改了,shell 在提示下一个命令之前打印消息 you have mail。 这个变量典型的在用户登录目录下的文件 .profile 中设置。例如,
 MAIL=/usr/mail/fred
$HOME
cd 命令的缺省实际参数。使用当前目录来解析不以 / 开始的文件名引用,并使用 cd 命令变更它。例如,
 cd /usr/fred/bin
使当前目录成为 /usr/fred/bin。
 cat wn
将在终端上打印在这个目录中的文件 wn。命令。没有实际参数的 cd 等价于
 cd $HOME
这个变量典型的也在这个用户的登录 .profile 中设置。
$PATH
包含命令的目录的一个列表(查找路径)。shell 通过在这个目录列表中查找可执行文件来执行每个命令。如果未设置 $PATH 则缺省的查找当前目录、/bin 和 /usr/bin。否则$PATH 由用 : 分隔的目录名字组成。例如,
 PATH=:/usr/fred/bin:/bin:/usr/bin
指定以当前目录(在第一个 : 之前的空串)、/usr/fred/bin、/bin 和 /usr/bin 的次序查找。在这种方式下单个用户可以有他们自己‘专有’命令,可在当前目录下单独访问。如果命令名字包含一个 / 则不使用这种目录查找;对执行这个命令作一次单一的尝试。
$PS1
主要的 shell 提示字符串,缺省是‘$’。
$PS2
在需要进一步输入时的 shell 提示,缺省是‘>’。
$IFS
空白解释使用的字符集合(参见章节 3.4)。
2.5 test 命令
test 命令尽管不是 shell 的一部分,但意图由 shell 程序使用。例如,

 test -f file
译注: 在当前版本的 shell 工具中,有一个与 test 等同的命令 [,它接受与 test 一样的实际参数,但要求在实际参数列表的最后附加一个 ] 作为实际参数。上面的例子也可以写成

 [ -f file ]

如果 file 存在则返回零退出状态否则返回非零退出状态。通常 test 计算一个谓词并返回这个结果作为退出状态。下面给出某些经常使用的 test 实际参数,详细的规定请参见 test (1)。

test s
如果实际参数 s 不是空串则为真
test -f file
如果 file 存在则为真
test -r file
如果 file 可读则为真
test -w file
如果 file 可写则为真
test -d file
如果 file 是目录则为真
2.6 控制流 - while
for 循环的动作和 case 分支由 shell 可获得的数据决定。还提供 while 或 until 循环和 if then else 分支,它们的动作由命令返回的退出状态决定。while 循环有一般形式

 while command-list1 do command-list2 done
while 命令测试的值是紧随 while 之后的最后的简单命令的退出状态。每轮循环都执行 command-list1;如果返回一个零退出状态则执行 command-list2;否则中止循环。例如,

 while test $1 do ... shift done
等价于

 for i do ... done
shift 是重命名位置参数 $2, $3, ...为 $1, $2, ... 并丢弃 $1 的一个命令。

另一种 while/until 循环的用法是等待直到某个外部的事件发生并接着运行某个命令。在 until 循环中中止条件是反过来的。例如,

 until test -f file do sleep 300; done commands
将循环直到 file 存在。每轮循环都在再次尝试之前等待 5 分钟。(推测另一个进程最终会建立这个文件。)

2.7 控制流 - if
还可以获得下面形式的一般的条件分支,

 if command-list then command-list else command-list fi
它测试紧随 if 之后的最后一个简单命令的返回值。


if 命令可以与 test 命令联合使用来测试文件的存在如

 if test -f file then process file else do something else fi

在2.10 节给出使用 if、case 和 for 构造的一个例子。

形如下面的多重测试 if 命令

 if ... then ... else if ... then ... else if ... ... fi fi fi
可以使用 if 记号的一种扩展而写成

 if ... then ... elif ... then ... elif ... ... fi
下列例子是改变一组文件的‘最近修改时间’的 touch 命令。这个命令可以与 make (1)联合使用来重新编译一组文件。

 flag= for i do case $i in -c) flag=N ;; *) if test -f $i then ln $i junk$$; rm junk$$ elif test $flag then echo file \'$i\' does not exist else >$i fi esac done
在这命令中使用 -c 来强制后面的文件如果不存在则建立之。否则,如果文件不存在,则打印一个错误消息。如果遇到 -c 参数则把 shell 变量 flag 设置为非空字符串。命令

 ln ...; rm ...
制作到这个文件的一个连接接着删除它,这导致更新最后的修改日期。


序列

 if command1 then command2 fi
可以写成

 command1 && command2
反过来,

 command1 || command2
只在 command1 失败时执行 command2。在这些情况下返回值是最后的简单命令的返回值。

2.8 命令组合
可以用两种方式组合命令,

 { command-list ; }

 ( command-list )
第一个命令列表被简单的执行。第二种形式把命令列表作为一个单独的进程执行。例如,

 (cd x; rm junk )
在目录 x 中执行 rm junk 而不改变调用 shell 的当前目录。


命令

 cd x; rm junk
有相同的效果但把调用 shell 留在目录 x 中。

2.9 调试 shell 过程
shell 提供两种跟踪机制来帮助调试 shell 过程。第一种在过程中调用为

 set -v
(v 是 verbose冗余)并导致打印过程的行,同读到的一样。这对分离语法错误有用。可以用下列写法调用它而不用修改过程

 sh -v proc ...
这里的 proc 是 shell 过程的名字。这个标志可以与 -n 标志联合使用,它防止随后的命令执行。(注意在终端上的 set -n 将放弃(render)终端不用直到键入一个文件结束符。)


命令

 set -x
将产生执行跟踪。紧随参数替换之后按实际上执行的那样打印每个命令。(在终端上尝试一下这种效果)。通过如下表述来关闭这些标志

 set -
而 shell 标志的当前设置可以获得为 $-。

2.10 man 命令
下面是用来打印 UNIX 手册章节的 man 命令。调用它的例子如下

 $ man sh $ man -t ed $ man 2 fork
打印手册的第一章的 sh。因为没有指定章节,使用第 1 章。第二个例子将用打印机打印(-t 选项)手册章节 ed。 最后一个例子打印第二章的 fork 手册页。

 cd /usr/man : '冒号是注释命令' : '缺省是 nroff ($N), 章节 1 ($s)' N=n s=1 for i do case $i in [1-9]*) s=$i ;; -t) N=t ;; -n) N=n ;; -*) echo unknown flag \'$i\' ;; *) if test -f man$s/$i.$s then ${N}roff man0/${N}aa man$s/$i.$s else : 'look through all manual sections' found=no for j in 1 2 3 4 5 6 7 8 9 do if test -f man$j/$i.$j then man $j $i found=yes fi done case $found in no) echo \'$i: manual page not found\' esac fi esac done
图 1.  man 命令的一个版本


译注:在当前版本的 shell 中把以‘#’开始的一行作为注释


--------------------------------------------------------------------------------

 

3.0 关键字参数
可以通过赋值或在调用 shell 过程的时候给出 shell 变量的值。在命令名字之前的、给 shell 过程的形如 name=value 的实际参数,导致在过程开始之前把 value 赋给 name。这不影响调用 shell 中的 name 的值。例如,

 user=fred command
执行 command 并把 user 设置为 fred。-k 标志导致在实际参数列表中所有地方的形如 name=value 的实际参数都按这种方式来解释。这种名字有时叫做关键字参数。如果有任何实际参数剩下则它们可以获得为位置参数 $1, $2, ....

还可以使用 set 命令在过程内设置位置参数。例如,

 set - *
将设置 $1 为在当前目录中的第一个文件名字,$2 为下一个,以此类推。注意第一个实际参数 -,确保在第一个文件名字以 - 开始时作出正确的处置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值