《Linux命令行与shell脚本编程》学习笔记

第一章、 初识Linux shell

1. Linux介绍

《Linux命令行与shell脚本编程》学习笔记_第1张图片
Linux系统的核心是内核,内核主要负责以下四种功能:

  • 系统内存管理
  • 软件程序管理
  • 硬件设备管理
  • 文件系统管理

2. GNU工具

核心GNU工具

GNU项目的主旨在于为Unix系统管理员设计出一套类似于Unix的环境。GNU coreutils软件包由三部分构成:

  • 用以处理文件的工具
  • 用以操作文本的工具
  • 用以管理进程的工具

shell

GNU/Linux shell是一种特殊的交互式工具。它为用户提供了启动程序、管理文件系统中的文件以及运行在Linux系统上的进程的途径。shell的核心是命令行提示符。

3. Linux 版本

核心 Linux 发行版

发 行 版 描 述
Slackware 最早的Linux发行版中的一员,在Linux极客中比较流行
Red Hat 主要用于Internet服务器的商业发行版
Fedora 从Red Hat分离出的家用发行版
Gentoo 为高级Linux用户设计的发行版,仅包含Linux源代码
openSUSE 用于商用和家用的发行版
Debian 在Linux专家和商用Linux产品中流行的发行版

特定用途的Linux发行版

发 行 版 描 述
CentOS 一款基于Red Hat企业版Linux源代码构建的免费发行版
Ubuntu 一款用于学校和家庭的免费发行版
PCLinuxOS 一款用于家庭和办公的免费发行版
Mint 一款用于家庭娱乐的免费发行版
dyne:bolic 一款用于音频和MIDI应用的免费发行版
Puppy Linux 一款适用于老旧PC的小型免费发行版

第二章、 走进shell

1. 浏览文件系统

Linux会在根驱动器上创建一些特别的目录,我们称之为挂载点(mount point)。挂载点是虚拟目录中用于分配额外存储设备的目录。

常见Linux目录名称

目 录 用 途
/ 虚拟目录的根目录。通常不会在这里存储文件
/bin 二进制目录,存放许多用户级的GNU工具
/boot 启动目录,存放启动文件
/dev 设备目录,Linux在这里创建设备节点
/etc 系统配置文件目录
/home 主目录,Linux在这里创建用户目录
/lib 库目录,存放系统和应用程序的库文件
/media 媒体目录,可移动媒体设备的常用挂载点
/mnt 挂载目录,另一个可移动媒体设备的常用挂载点
/opt 可选目录,常用于存放第三方软件包和数据文件
/proc 进程目录,存放现有硬件及当前进程的相关信息
/root root用户的主目录
/sbin 系统二进制目录,存放许多GNU管理员级工具
/run 运行目录,存放系统运作时的运行时数据
/srv 服务目录,存放本地服务的相关文件
/sys 系统目录,存放系统硬件信息的相关文件
/tmp 临时目录,可以在该目录中创建和删除临时工作文件
/usr 用户二进制目录,大量用户级的GNU工具和数据文件都存储在这里
/var 可变目录,用以存放经常变化的文件,比如日志文件

遍历目录

cd destination
cd命令可接受单个参数destination,用以指定想切换到的目录名。
destination参数可以用两种方式表示:

  • 一种是使用绝对文件路径
  • 另一种是使用相对文件路径

绝对路径

绝对文件路径总是以正斜线(/)作为起始,指明虚拟文件系统的根目录。
pwd命令可以显示出shell会话的当前目录,这个目录被称为当前工作目录。

相对文件路径

相对文件路径允许用户指定一个基于当前位置的目标文件路径。相对文件路径不以代表根目录的正斜线(/)开头,而是以目录名(如果用户准备切换到当前工作目录下的一个目录)或是一个特殊字符开始。
有两个特殊字符可用于相对文件路径中:

  • 单点符(.),表示当前目录;
  • 双点符(…),表示当前目录的父目录。

2.文件和目录列表

ls命令最基本的形式会显示当前目录下的文件和目录,

ls相关的参数命令

参数 作用
-F 轻松区分文件和目录
-a 把隐藏文件和普通文件及目录一起显示出来
-R 列出了当前目录下包含的子目录中的文件
-l 产生长列表格式的输出,包含了目录中每个文件的更多相关信息
-i 查看文件或目录的inode编号

ls -l参数的文件内容

每一行都包含了关于文件(或目录)的下述信息:

  • 文件类型,比如目录(d)、文件(-)、字符型文件(c)或块设备(b);
  • 文件的权限(参见第6章);
  • 文件的硬链接总数;
  • 文件属主的用户名;
  • 文件属组的组名;
  • 文件的大小(以字节为单位);
  • 文件的上次修改时间;
  • 文件名或目录名。

过滤输出列表

用过滤器来决定应该在输出中显示哪些文件
或目录。这个过滤器就是一个进行简单文本匹配的字符串。可以在要用的命令行参数之后添加这个过滤器:

$ ls -l my_script

ls命令能够识别标准通配符,并在过滤器中用它们进行模式匹配:

  • 问号(?)代表一个字符;
  • 星号(*)代表零个或多个字符。
  • 中括号([])表示一个字符位置并给出多个可能的选择
a或i
$ ls -l my_scr[ai]pt 
字母范围[a – i]。
$ ls -l f[a-i]ll

在过滤器中使用星号和问号被称为文件扩展匹配(file globbing),指的是使用通配符进行模式匹配的过程。通配符正式的名称叫作元字符通配符(metacharacter wildcards)。

3. 处理文件

3.1 创建文件

touch命令轻松创建空文件。

$ touch test_one

如果只想改变访问时间,可用-a参数。

$ touch -a test_one 
$ ls -l test_one 
-rw-rw-r-- 1 christine christine 0 May 21 14:35 test_one 
$ ls -l --time=atime test_one 
-rw-rw-r-- 1 christine christine 0 May 21 14:55 test_one 
$

只使用ls –l命令,并不会显示访问时间。这是因为默认显示的是修改时间。要想查看文件的访问时间,需要加入另外一个参数:- -time=atime。有了这个参数,就能够显示出已经更改过的文件访问时间。

3.2 复制文件

cp命令需要两个参数——源对象和目标对象:

  • 当source和destination参数都是文件名时,cp命令将源文件复制成一个新文件,并且以destination命名。
cp source destination
  • 如果目标文件已经存在,cp命令可能并不会提醒这一点。最好是加上-i选项,强制shell询问是否需要覆盖已有文件。
 cp -i test_one test_two 
  • 其中的单点符(.)就很适合用于cp命令。记住,单点符表示当前工作目录。如果需要将一个带有很长的源对象名的文件复制到当前工作目录中时,单点符能够简化该任务。
cp -i /etc/NetworkManager/NetworkManager.conf . 
  • cp命令的-R参数威力强大。可以用它在一条命令中递归地复制整个目录的内容。
cp -R 目标文件夹/ 新建的文件夹
  • 也可以在cp命令中使用通配符。该命令将所有以script结尾的文件复制到Mod_Scripts目录中。在这里,只需要复制一个文件:
    my_script。
cp *script Mod_Scripts/

3.3 链接文件

如需要在系统上维护同一文件的两份或多份副本,除了保存多份单独的物理文件副本之外,还可以采用保存一份物理文件副本和多个虚拟副本的方法。这种虚拟的副本就称为链接。链接是目录中指向文件真实位置的占位符。在Linux中有两种不同类型的文件链接:

  • 符号链接
  • 硬链接

要为一个文件创建符号链接,原始文件必须事先存在。然后可以使用ln命令以及-s选项来创建符号链接。

ln -s data_file sl_data_file

3.4 重命名文件

重命名文件称为移动(moving),mv命令可以将文件和目录移动到另一个位置或重新命名。

mv 旧文件名 新文件名

inode编号和时间戳保持不变。这是因为mv只影响文件名

  • 使用mv来移动文件的位置。
mv 旧文件名 文件夹/
  • 使用mv命令移动文件位置并修改文件名称,文件的时间戳和inode编号都没有改变,只改变位置和名称。
mv /home/christine/Pictures/fzll /home/christine/fall

3.5 删除文件

bash shell中删除文件的命令是rm。rm命令的基本格式非常简单。

rm -i fall
  • -i命令参数提示你是不是要真的删除该文件。bash shell中没有回收站或垃圾箱,文件一旦删除,就无法再找回。
  • rm命令的另外一个特性是,如果要删除很多文件且不受提示符的打扰,可以用-f参数强制删除。

4. 处理目录

4.1 创建目录

系统创建了一个名为New_Dir的新目录。

 mkdir New_Dir

想单单靠mkdir命令来实现,就会得到错误消息,要想同时创建多个目录和子目录,需要加入-p参数。-p参数可以根据需要创建缺失的父目录。父目录是包含目录树中下一级目录的目录。

 mkdir -p New_Dir/Sub_Dir/Under_Dir

4.2 删除目录

删除目录的基本命令是rmdir,rmdir命令只删除空目录。

rmdir New_Dir

要想删除非空目录,得先把目录中的文件删掉,在整个非空目录上使用rm命令。使用-r选项使得命令可以向下进入目录,删除其中的文件,然后再删除目录本身

rm -ri My_Dir 

5. 查看文件内容

5.1 查看文件类型

file my_file
#file命令不仅能确定文件中包含的文本信息,还能确定该文本文件的字符编码,ASCII;
#也能展示链接,甚至能够告诉你它链接到了哪个文件上
#还能展示了file命令对脚本文件的返回结果
#如果文件是个二进制可执行程序,file命令能够确定该程序编译时所面向的平台以及需要何种类型的库。

file New_Dir
#后可以使用file命令作为另一种区分目录的方法

5.2 查看整个文件

  • cat命令
cat test1 

cat -n test1 
#-n参数会给所有的行加上行号

cat -b test1 
#如果只想给有文本的行加上行号,可以用-b参数。

cat -T test1
#如果不想让制表符出现,可以用-T参数。
  • more命令
    more命令会显示文本文件的内容,但会在显示每页数据之后停下来,more命令只支持文本文件中的基本移动。
  • less命令
    less命令的操作和more命令基本一样,一次显示一屏的文件文本。除了支持和more命令相同的命令集,它还包括更多的选项。less命令能够识别上下键以及上下翻页键

5.3 查看部分文件

  • tail命令
    tail命令会显示文件最后几行的内容(文件的“尾部”)。默认情况下,它会显示文件的末尾10行。
tail log_file

#可以向tail命令中加入-n参数来修改所显示的行数。
tail -n 2 log_file 

#-f参数是tail命令的一个突出特性。它允许你在其他进程使用该文件时查看文件的内容。
  • head
    head命令,顾名思义,会显示文件开头那些行的内容。
head log_file 

#可以向head命令中加入-n参数来修改所显示的行数。
tail -2 log_file 

#head命令并像tail命令那样支持-f参数特性

第四章、 更多的bash shell命令

1. 监控程序

1.1 探查进程

ps命令能输出运行在系统上的所有程序的许多信息。ps命令只会显示运行在当前控制台下的属于当前用户的进程。

ps

Linux系统中使用的GNU ps命令支持3种不同类型的命令行参数:

  • Unix风格的参数,前面加单破折线;
  • BSD风格的参数,前面不加破折线;
  • GNU风格的长参数,前面加双破折线

1.1.1 Unix风格的ps命令参数

参 数 描 述
-A 显示所有进程
-N 显示与指定参数不符的所有进程
-a 显示除控制进程(session leader①)和无终端进程外的所有进程
-d 显示除控制进程外的所有进程
-e 显示所有进程
-C cmdlist 显示包含在cmdlist列表中的进程
-G grplist 显示组ID在grplist列表中的进程
-U userlist 显示属主的用户ID在userlist列表中的进程
-g grplist 显示会话或组ID在grplist列表中的进程②
-p pidlist 显示PID在pidlist列表中的进程
-s sesslist 显示会话ID在sesslist列表中的进程
-t ttylist 显示终端ID在ttylist列表中的进程
-u userlist 显示有效用户ID在userlist列表中的进程
-F 显示更多额外输出(相对-f参数而言)
-O format 显示默认的输出列以及format列表指定的特定列
-M 显示进程的安全信息
-c 显示进程的额外调度器信息
-f 显示完整格式的输出
-j 显示任务信息
-l 显示长列表
-o format 仅显示由format指定的列
-y 不要显示进程标记(process flag,表明进程状态的标记)
-Z 显示安全标签(security context)①信息
-H 用层级格式来显示进程(树状,用来显示父进程)
-n namelist 定义了WCHAN列显示的值
-w 采用宽输出模式,不限宽度显示
-L 显示进程中的线程
-V 显示ps命令的版本号

-f参数则扩展了输出,这些扩展的列包含了有用的信息。

  • UID:启动这些进程的用户。
  • PID:进程的进程ID。
  • PPID:父进程的进程号(如果该进程是由另一个进程启动的)。
  • C:进程生命周期中的CPU利用率。
  • STIME:进程启动时的系统时间。
  • TTY:进程启动时的终端设备。
  • TIME:运行进程需要的累计CPU时间。
  • CMD:启动的程序名称。

1.1.2 BSD风格的ps命令参数

参 数 描 述
T 显示跟当前终端关联的所有进程
a 显示跟任意终端关联的所有进程
g 显示所有的进程,包括控制进程
r 仅显示运行中的进程
x 显示所有的进程,甚至包括未分配任何终端的进程
U userlist 显示归userlist列表中某用户ID所有的进程
p pidlist 显示PID在pidlist列表中的进程
t ttylist 显示所关联的终端在ttylist列表中的进程
O format 除了默认输出的列之外,还输出由format指定的列
X 按过去的Linux i386寄存器格式显示
Z 将安全信息添加到输出中
j 显示任务信息
l 采用长模式
o format 仅显示由format指定的列
s 采用信号格式显示
u 采用基于用户的格式显示
v 采用虚拟内存格式显示
N namelist 定义在WCHAN列中使用的值
O order 定义显示信息列的顺序
S 将数值信息从子进程加到父进程上,比如CPU和内存的使用情况
c 显示真实的命令名称(用以启动进程的程序名称)
e 显示命令使用的环境变量
f 用分层格式来显示进程,表明哪些进程启动了哪些进程
h 不显示头信息
k sort 指定用以将输出排序的列
n 和WCHAN信息一起显示出来,用数值来表示用户ID和组ID
w 为较宽屏幕显示宽输出
H 将线程按进程来显示
m 在进程后显示线程
L 列出所有格式指定符
V 显示ps命令的版本号

1.1.3 GNU风格的ps命令参数

参 数 描 述
–deselect 显示所有进程,命令行中列出的进程
–Group grplist 显示组ID在grplist列表中的进程
–User userlist 显示用户ID在userlist列表中的进程
–group grplist 显示有效组ID在grplist列表中的进程
–pid pidlist 显示PID在pidlist列表中的进程
–ppid pidlist 显示父PID在pidlist列表中的进程
–sid sidlist 显示会话ID在sidlist列表中的进程
–tty ttylist 显示终端设备号在ttylist列表中的进程
–user userlist 显示有效用户ID在userlist列表中的进程
–format format 仅显示由format指定的列
–context 显示额外的安全信息
–cols n 将屏幕宽度设置为n列
–columns n 将屏幕宽度设置为n列
–cumulative 包含已停止的子进程的信息
–forest 用层级结构显示出进程和父进程之间的关系
–headers 在每页输出中都显示列的头
–no-headers 不显示列的头
–lines n 将屏幕高度设为n行
–rows n 将屏幕高度设为n排
–sort order 指定将输出按哪列排序
–width n 将屏幕宽度设为n列
–help 显示帮助信息
–info 显示调试信息
–version 显示ps命令的版本号

1.2 实时监测进程

top命令跟ps命令相似,能够显示进程信息,但它是实时显示的。
top命令的输出中将进程叫作任务(task):有多少进程处在运行、休眠、停止或是僵化状态(僵化状态是指进程完成了,但父进程没有响应)

1.3 结束进程

  1. kill命令
    kill命令可通过进程ID(PID)给进程发信号。默认情况下,kill命令会向命令行中列出的全部PID发送一个TERM信号。
  2. killall命令
    killall命令非常强大,它支持通过进程名而不是PID来结束进程。killall命令也支持通配符,这在系统因负载过大而变得很慢时很有用。

2. 监测磁盘空间

2.1 挂载存储媒体

Linux文件系统将所有的磁盘都并入一个虚拟目录下。在使用新的存储媒体之前,需要把它放到虚拟目录下。这项工作称为挂载(mounting)。

2.1.1 mount命令

Linux上用来挂载媒体的命令叫作mount。默认情况下,mount命令会输出当前系统上挂载的设备列表。
mount命令提供如下四部分信息:

  • 媒体的设备文件名
  • 媒体挂载到虚拟目录的挂载点
  • 文件系统类型
  • 已挂载媒体的访问状态
    mount命令的参数
参 数 描 述
-a 挂载/etc/fstab文件中指定的所有文件系统
-f 使mount命令模拟挂载设备,但并不真的挂载
-F 和-a参数一起使用时,会同时挂载所有文件系统
-v 详细模式,将会说明挂载设备的每一步
-I 不启用任何/sbin/mount.filesystem下的文件系统帮助文件
-l 给ext2、ext3或XFS文件系统自动添加文件系统标签
-n 挂载设备,但不注册到/etc/mtab已挂载设备文件中
-p num 进行加密挂载时,从文件描述符num中获得密码短语
-s 忽略该文件系统不支持的挂载选项
-r 将设备挂载为只读的
-w 将设备挂载为可读写的(默认参数)
-L label 将设备按指定的label挂载
-U uuid 将设备按指定的uuid挂载
-O 和-a参数一起使用,限制命令只作用到特定的一组文件系统上
-o 给文件系统添加特定的选项

2.1.2 umount命令

支持通过设备文件或者是挂载点来指定要卸载的设备。

umount [directory | device ] 

2.2 使用 df 命令

查看所有已挂载磁盘的使用情况。

$ df 
Filesystem 1K-blocks Used Available Use% Mounted on 
/dev/sda2 18251068 7703964 9605024 45% / 
/dev/sda1 101086 18680 77187 20% /boot 
tmpfs 119536 0 119536 0% /dev/shm 
/dev/sdb1 127462 113892 13570 90% /media/disk 
$ 

命令输出如下:
 设备的设备文件位置;
 能容纳多少个1024字节大小的块;
 已用了多少个1024字节大小的块;
 还有多少个1024字节大小的块可用;
 已用空间所占的比例;
 设备挂载到了哪个挂载点上

一个常用的参数是-h。它会把输出中的磁盘空间按照用户易读的形式显示,通常用M来替代兆字节,用G替代吉字节。

2.3 使用 du 命令

du命令可以显示某个特定目录(默认情况下是当前目录)的磁盘使用情况。

下面是能让du命令用起来更方便的几个命令行参数。
 -c:显示所有已列出文件总的大小。
 -h:按用户易读的格式输出大小,即用K替代千字节,用M替代兆字节,用G替代吉字节。
 -s:显示每个输出参数的总计。

3. 处理数据文件

3.1 排序数据

$ sort file1 

#上面的命令如果输出有数字还是按字符串排序,加上-n参数以后数字信息就会按数字排序
$ sort -n file2 

#将含有时间戳日期的文件按默认的排序方法来排序
$ sort -M file3 

sort命令参数

单破折线 双破折线 描 述
-b –ignore-leading-blanks 排序时忽略起始的空白
-C –check=quiet 不排序,如果数据无序也不要报告
-c –check 不排序,但检查输入数据是不是已排序;未排序的话,报告
-d –dictionary-order 仅考虑空白和字母,不考虑特殊字符
-f –ignore-case 默认情况下,会将大写字母排在前面;这个参数会忽略大小写
-g –general-number-sort 按通用数值来排序(跟-n不同,把值当浮点数来排序,支持科学计数法表示的值)
-i –ignore-nonprinting 在排序时忽略不可打印字符
-k –key=POS1[,POS2] 排序从POS1位置开始;如果指定了POS2的话,到POS2位置结束
-M –month-sort 用三字符月份名按月份排序
-m –merge 将两个已排序数据文件合并
-n –numeric-sort 按字符串数值来排序(并不转换为浮点数)
-o –output=file 将排序结果写出到指定的文件中
-R –random-sort 按随机生成的散列表的键值排序
–random-source=FILE 指定-R参数用到的随机字节的源文件
-r –reverse 反序排序(升序变成降序)
-S –buffer-size=SIZE 指定使用的内存大小
-s –stable 禁用最后重排序比较
-T –temporary-directory=DIR 指定一个位置来存储临时工作文件
-t –field-separator=SEP 指定一个用来区分键位置的字符
-u –unique 和-c参数一起使用时,检查严格排序;不和-c参数一起用时,仅输出第一例相似的两行
-z –zero-terminated 用NULL字符作为行尾,而不是用换行符

3.2 搜索数据

#grep命令行格式如下
grep [options] pattern [file]

#实例
$ grep t file1 
two 
three 

#进行反向搜索(输出不匹配该模式的行),可加-v参数。
$ grep -v t file1 

#显示匹配模式的行所在的行号,可加-n参数。
$ grep -n t file1 

#只要知道有多少行含有匹配的模式,可用-c参数。
$ grep -c t file1 

#要指定多个匹配模式,可用-e参数来指定每个模式。
$ grep -e t -e f file1 

3.3 压缩数据

Linux文件压缩工具

工 具 文件扩展名 描 述
bzip2 .bz2 采用Burrows-Wheeler块排序文本压缩算法和霍夫曼编码
compress .Z 最初的Unix文件压缩工具,已经快没人用了
gzip .gz GNU压缩工具,用Lempel-Ziv编码
zip .zip Windows上PKZIP工具的Unix实现

3.4 归档数据

tar命令最开始是用来将文件写到磁带设备上归档的,然而它也能把输出写到文件里。

tar命令语法

tar function [options] object1 object2 ... 

tar命令的功能

功 能 长 名 称 描 述
-A –concatenate 将一个已有tar归档文件追加到另一个已有tar归档文件
-c –create 创建一个新的tar归档文件
-d –diff 检查归档文件和文件系统的不同之处
–delete 从已有tar归档文件中删除
-r –append 追加文件到已有tar归档文件末尾
-t –list 列出已有tar归档文件的内容
-u –update 将比tar归档文件中已有的同名文件新的文件追加到该tar归档文件中
-x –extract 从已有tar归档文件中提取文件

tar命令选项

选 项 描 述
-C dir 切换到指定目录
-f file 输出结果到文件或设备file
-j 将输出重定向给bzip2命令来压缩内容
-p 保留所有文件权限
-v 在处理文件时显示文件
-z 将输出重定向给gzip命令来压缩内容

示例代码:

#来创建一个归档文件:
tar -cvf test.tar test/ test2/

第五章、 理解shell

1. shell 的类型

用户christine使用GNU bash shell作为自己的默认shell程序。

bash shell程序位于/bin目录内。从长列表中可以看出/bin/bash(bash shell)是一个可执行程序。

用户christine默认的交互shell是/bin/bash,也就是bash shell。

并不是必须一直使用默认的交互shell。可以使用发行版中所有可用的shell,只需要输入对应的文件名就行了。例如,你可以直接输入命令/bin/dash来启动dash shell。

除启动了dash shell程序之外,看起来似乎什么都没有发生。提示符$是dash shell的CLI提示符。可以输入exit来退出dash shell。

2. shell 的父子关系

在CLI提示符后输入/bin/bash命令或其他等效的bash命令时,会创建一个新的shell程序。这个shell程序被称为子shell(child shell)。子shell也拥有CLI提示符,同样会等待命令输入。

2.1 进程列表

可以在一行中指定要依次运行的一系列命令。这可以通过命令列表来实现,只需要在命令之间加入分号(;)即可。

$ pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls

如果该命令返回0,就表明没有子shell。如果返回1或者其他更大的数字,就表明存在子shell。

2.2 别出心裁的子 shell 用法

2.2.1 探索后台模式

sleep命令接受一个参数,该参数是你希望进程等待(睡眠)的秒数。这个命令在脚本中常用于引入一段时间的暂停。

#命令sleep 10会将会话暂停10秒钟,然后返回shell CLI提示符。
$ sleep 10 

可以使用jobs命令来显示后台作业信息。jobs命令可以显示出当前运行在后台模式中的所有用户的进程(作业)。

$ jobs 
[1]+ Running sleep 3000 &
#除了默认信息之外,-l选项还能够显示出命令的PID。
jobs -l 
[1]+ 2396 Running sleep 3000 & 

2.2.2 将进程列表置入后台

将进程列表置入后台模式并不是子shell在CLI中仅有的创造性用法。协程就是另一种方法。

2.2.3 协程

协程可以同时做两件事。它在后台生成一个子shell,并在这个子shell中执行命令。
要进行协程处理,得使用coproc命令,还有要在子shell中执行的命令。

$ coproc sleep 10 
[1] 2544 

在命令行中使用子shell能够获得灵活性和便利。要想获得这些优势,重要的是理解子shell的行为方式。对

2.3 理解 shell 的内建命令

2.3.1 外部命令

外部命令程序通常位于/bin、/usr/bin、/sbin或/usr/sbin中。
可以使用which和type命令找到它。

$ which ps 
/bin/ps 
$ 
$ type -a ps 
ps is /bin/ps 

当进程必须执行衍生操作时,它需要花费时间和精力来设置新子进程的环境。所以说,外部命令多少还是有代价的。

2.3.2 内建命令

内建命令和外部命令的区别在于前者不需要使用子进程来执行。

  • 使用history命令
    要查看最近用过的命令列表,可以输入不带选项的history命令。

可以在退出shell会话之前强制将命令历史记录写入.bash_history文件。要实现强制写入,需要使用history命令的-a选项。

$ history -a

命令别名
alias命令是另一个shell的内建命令。命令别名允许你为常用的命令(及其参数)创建另一个名称,从而将输入量减少到最低。使用alias命令以及选项-p。

$ alias -p 

第六章 使用Linux环境变量

1. 什么是环境变量

bash shell用一个叫作环境变量(environment variable)的特性来存储有关shell会话和工作环境的信息(这也是它们被称作环境变量的原因)。
在bash shell中,环境变量分为两类:
 全局变量
 局部变量

1.1 全局环境变量

全局环境变量对于shell会话和所有生成的子shell都是可见的。

系统环境变量基本上都是使用全大写字母,以区别于普通用户的环境变量。

要查看全局变量,可以使用env或printenv命令。

$ printenv HOME 
/home/Christine 
$ 
$ env HOME 
env: HOME: No such file or directory
$ echo $HOME 
/home/Christine 

1.2 局部环境变量

局部环境变量只能在定义它们的进程中可见。
在Linux系统并没有一个只显示局部环境变量的命令。set命令会显示为某个特定进程设置的所有环境变量,包括局部变量、全局变量以及用户定义变量。

2. 设置用户定义变量

2.1 设置局部用户定义变量

一旦启动了bash shell(或者执行一个shell脚本),就能创建在这个shell进程内可见的局部变量了。可以通过等号给环境变量赋值,值可以是数值或字符串。

$ echo $my_variable 
$ my_variable=Hello 
$ 
$ echo $my_variable 

注意

  • 所有的环境变量名均使用大写字母,这是bash shell的标准惯例。如果是你自己创建的局部变量或是shell脚本,请使用小写字母。变量名区分大小写。
  • 变量名、等号和值之间没有空格,这一点非常重要。如果在赋值表达式中加上了空格,bash shell就会把值当成一个单独的命令。
  • 设置了局部环境变量后,就能在shell进程的任何地方使用它了。但是,如果生成了另外一个shell,它在子shell中就不可用。

2.2 设置全局环境变量

在设定全局环境变量的进程所创建的子进程中,该变量都是可见的。创建全局环境变量的方法是先创建一个局部环境变量,然后再把它导出到全局环境中。
通过export命令来完成,变量名前面不需要加$。

$ my_variable="I am Global now" 
$ export my_variable 
$ 
$ echo $my_variable 
I am Global now 
$ 
$ bash 
$ 
$ echo $my_variable 
I am Global now 
$ 
$ my_variable="Null" 
$ 
$ echo $my_variable 
Null

3. 删除环境变量

可以用unset命令完成这个操作。

$ echo $my_variable 
I am Global now 
$ 
$ unset my_variable 
$ 
$ echo $my_variable 

4. 默认的 shell 环境变量

默认情况下,bash shell会用一些特定的环境变量来定义系统环境。

bash shell支持的Bourne变量

变 量 描 述
CDPATH 冒号分隔的目录列表,作为cd命令的搜索路径
HOME 当前用户的主目录
IFS shell用来将文本字符串分割成字段的一系列字符
MAIL 当前用户收件箱的文件名(bash shell会检查这个文件,看看有没有新邮件)
MAILPATH 冒号分隔的当前用户收件箱的文件名列表(bash shell会检查列表中的每个文件,看看有没有新邮件)
OPTARG getopts命令处理的最后一个选项参数值
OPTIND getopts命令处理的最后一个选项参数的索引号
PATH shell查找命令的目录列表,由冒号分隔
PS1 shell命令行界面的主提示符
PS2 shell命令行界面的次提示符

除了默认的Bourne的环境变量,bash shell还提供一些自有的变量

5. 设置 PATH 环境变量

当你在shell命令行界面中输入一个外部命令时(参见第5章),shell必须搜索系统来找到对应的程序。PATH环境变量定义了用于进行命令和程序查找的目录。

$ echo $PATH

PATH中的目录使用冒号分隔。
可以把新的搜索目录添加到现有的PATH环境变量中,无需从头定义。

$ echo $PATH 
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin: /sbin:/bin:/usr/games:/usr/local/games 
$ 
$ PATH=$PATH:/home/christine/Scripts 
$ 
$ echo $PATH 
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/christine/Scripts 

注意:如果希望子shell也能找到你的程序的位置,一定要记得把修改后的PATH环境变量导出。

6. 定位系统环境变量

在你登入Linux系统启动一个bash shell时,默认情况下bash会在几个文件中查找命令。这些文件叫作启动文件或环境文件。bash检查的启动文件取决于你启动bash shell的方式。启动bash shell有3种方式:
 登录时作为默认登录shell
 作为非登录shell的交互式shell
 作为运行脚本的非交互shell

6.1 登录 shell

bash shell会作为登录shell启动。登录shell会从5个不同的启动文件里读取命令:
 /etc/profile
 $HOME/.bash_profile
 $HOME/.bashrc
 $HOME/.bash_login
 $HOME/.profile

6.1.1 /etc/profile文件

/etc/profile文件是bash shell默认的的主启动文件。

6.1.2 $HOME目录下的启动文件

剩下的启动文件都起着同一个作用:提供一个用户专属的启动文件来定义该用户所用到的环境变量,大多数Linux发行版只用这四个启动文件中的一到两个。

6.2 交互式 shell 进程

如果你的bash shell不是登录系统时启动的(比如是在命令行提示符下敲入bash时启动),那么你启动的shell叫作交互式shell。

6.3 非交互式 shell

系统执行shell脚本时用的就是这种shell。当shell启动一个非交互式shell进程时,它会检查这个环境变量来查看要执行的启动文件。如果有指定的文件,shell会执行该文件里的命令,这通常包括shell脚本变量设置。

6.4 环境变量持久化

对全局环境变量来说(Linux系统中所有用户都需要使用的变量),可能更倾向于将新的或修改过的变量设置放在/etc/profile文件中。最好是在/etc/profile.d目录中创建一个以.sh结尾的文件。把所有新的或修改过的全局环境变量设置放在这个文件中。

7. 数组变量

$ mytest=(one two three four five)
$ echo $mytest 
one 
# 只有数组的第一个值显示出来了。
# 索引值要用方括号括起来。
$ echo ${mytest[2]}
three 
# 要显示整个数组变量,可用星号作为通配符放在索引值的位置。
$ echo ${mytest[*]} 
one two three four five
# 改变某个索引值位置的值。
$ mytest[2]=seven
$ 
$ echo ${mytest[*]}
one two seven four five
#甚至能用unset命令删除数组中的某个值,但是要小心,这可能会有点复杂。
$ unset mytest[2]
$ 
$ echo ${mytest[*]}
one two four five 
$ 
$ echo ${mytest[2]}
$ echo ${mytest[3]}
four 

第七章 理解Linux文件权限

1. Linux 的安全性

Linux安全系统的核心是用户账户。每个能进入Linux系统的用户都会被分配唯一的用户账户。

1.1 /etc/passwd 文件

它包含了一些与用户有关的信息。root用户账户是Linux系统的管理员,固定分配给它的UID是0。

/etc/passwd文件的字段包含了如下信息:
 登录用户名
 用户密码
 用户账户的UID(数字形式)
 用户账户的组ID(GID)(数字形式)
 用户账户的文本描述(称为备注字段)
 用户HOME目录的位置
 用户的默认shell

1.2 /etc/shadow 文件

/etc/shadow文件对Linux系统密码管理提供了更多的控制。
在/etc/shadow文件的每条记录中都有9个字段:
 与/etc/passwd文件中的登录名字段对应的登录名
 加密后的密码
 自上次修改密码后过去的天数密码(自1970年1月1日开始计算)
 多少天后才能更改密码
 多少天后必须更改密码
 密码过期前提前多少天提醒用户更改密码
 密码过期后多少天禁用用户账户
 用户账户被禁用的日期(用自1970年1月1日到当天的天数表示)
 预留字段给将来使用

1.3 添加新用户

系统默认值被设置在/etc/default/useradd文件中。可以使用加入了-D选项的useradd命令查看所用Linux系统中的这些默认值。

# /usr/sbin/useradd -D 
GROUP=100 
HOME=/home 
INACTIVE=-1 
EXPIRE= 
SHELL=/bin/bash 
SKEL=/etc/skel 
CREATE_MAIL_SPOOL=yes 
#

 新用户会被添加到GID为100的公共组;
 新用户的HOME目录将会位于/home/loginname;
 新用户账户密码在过期后不会被禁用;
 新用户账户未被设置过期日期;
 新用户账户将bash shell作为默认shell;
 系统会将/etc/skel目录下的内容复制到用户的HOME目录下;
 系统为该用户账户在mail目录下创建一个用于接收邮件的文件。

# useradd命令不会创建HOME目录,但是-m命令行选项会使其创建HOME目录。
useradd -m test 
# 检查一下新用户的HOME目录。
 ls -al /home/test 

useradd命令行参数

参 数 描 述
-c comment 给新用户添加备注
-d home_dir 为主目录指定一个名字(如果不想用登录名作为主目录名的话)
-e expire_date 用YYYY-MM-DD格式指定一个账户过期的日期
-f inactive_days 指定这个账户密码过期后多少天这个账户被禁用;0表示密码一过期就立即禁用,1表示禁用这个功能
-g initial_group 指定用户登录组的GID或组名
-G group … 指定用户除登录组之外所属的一个或多个附加组
-k 必须和-m一起使用,将/etc/skel目录的内容复制到用户的HOME目录
-m 创建用户的HOME目录
-M 不创建用户的HOME目录(当默认设置里要求创建时才使用这个选项)
-n 创建一个与用户登录名同名的新组
-r 创建系统账户
-p passwd 为用户账户指定默认密码
-s shell 指定默认的登录shell
-u uid 为账户指定唯一的UID

useradd更改默认值的参数

参 数 描 述
-b default_home 更改默认的创建用户HOME目录的位置
-e expiration_date 更改默认的新账户的过期日期
-f inactive 更改默认的新用户从密码过期到账户被禁用的天数
-g group 更改默认的组名称或GID
-s shell 更改默认的登录shell

1.4 删除用户

默认情况下,userdel命令会只删除/etc/passwd文件中的用户信息,而不会删除系统中属于该账户的任何文件。如果加上-r参数,userdel会删除用户的HOME目录以及邮件目录。。然而,系统上仍可能存有已删除用户的其他文件。这在有些环境中会造成问题。

1.5 修改用户

用户账户修改工具

命 令 描 述
usermod 修改用户账户的字段,还可以指定主要组以及附加组的所属关系
passwd 修改已有用户的密码
chpasswd 从文件中读取登录名密码对,并更新密码
chage 修改密码的过期日期
chfn 修改用户账户的备注信息
chsh 修改用户账户的默认登录shell

1.5.1 usermod

它能用来修改/etc/passwd文件中的大部分字段
 -c修改备注字段
 -e修改过期日期
 -g修改默认的登录组
 -l修改用户账户的登录名。
 -L锁定账户,使用户无法登录。
 -p修改账户的密码。
 -U解除锁定,使用户能够登录。

1.5.2 passwd和chpasswd

改变用户密码的一个简便方法就是用passwd命令。系统上的任何用户都能改自己的密码,但只有root用户才有权限改别人的密码。

chpasswd命令能从标准输入自动读取登录名和密码对(由冒号分割)列表,给密码加密。

1.5.3 chsh、chfn和chage

chsh、chfn和chage工具专门用来修改特定的账户信息。

# 使用时必须用shell的全路径名作为参数
chsh -s /bin/csh test

#如果在使用chfn命令时没有参数,它会向你询问要将哪些适合的内容加进备注字段
# chfn test 
Changing finger information for test. 
Name []: Ima Test 
Office []: Director of Technology 
Office Phone []: (123)555-1234 
Home Phone []: (123)555-9876 
Finger information changed. 

chage命令参数

参 数 描 述
-d 设置上次修改密码到现在的天数
-E 设置密码过期的日期
-I 设置密码过期到锁定账户的天数
-m 设置修改密码之间最少要多少天
-W 设置密码过期前多久开始出现提醒信息

chage命令的日期值可以用下面两种方式中的任意一种:
 YYYY-MM-DD格式的日期
 代表从1970年1月1日起到该日期天数的数值

2. 使用 Linux 组

2.1 /etc/group 文件

/etc/group文件有4个字段:
 组名
 组密码
 GID
 属于该组的用户列表

2.2 创建新组

groupadd命令可在系统上创建新组。

/usr/sbin/groupadd shared 

groupadd命令没有提供将用户添加到组中的
选项,但可以用usermod命令来弥补这一点。

/usr/sbin/usermod -G shared rich

2.3 修改组

groupmod命令可以修改已有组的GID(加-g选项)或组名(加-n选项)。

/usr/sbin/groupmod -n sharing shared

3. 理解文件权限

3.1 使用文件权限符

ls命令可以用来查看Linux系统上的文件、目录和设备的权限。

$ ls –l 

输出字段的第一个字符代表了对象的类型:
 -代表文件
 d代表目录
 l代表链接
 c代表字符型设备
 b代表块设备
 n代表网络设备
之后有3组三字符的编码。每一组定义了3种访问权限:
 r代表对象是可读的
 w代表对象是可写的
 x代表对象是可执行的
若没有某种权限,在该权限位会出现单破折线。这3组权限分别对应对象的3个安全级别:
 对象的属主
 对象的属组
 系统其他用户
《Linux命令行与shell脚本编程》学习笔记_第2张图片

3.2 默认文件权限

umask命令用来设置所创建文件和目录的默认权限,显示和设置这个默认权限。

$ umask 
0022 

第一位代表了一项特别的安全特性,叫作粘着位(sticky bit)。后面三个分别是属主,组,其他用户的权限。

Linux文件权限码

权 限 二进制值 八进制值 描 述
000 0 没有任何权限
–x 001 1 只有执行权限
-w- 010 2 只有写入权限
-wx 011 3 有写入和执行权限
r– 100 4 只有读取权限
r-x 101 5 有读取和执行权限
rw- 110 6 有读取和写入权限
rwx 111 7 有全部权限
把umask值从对象的全权限值中减掉。
$ umask 026 
#文件一开始的权限是666,在把umask值设成026后,默认的文件权限变成了640,因此新文件现在对组成员来说是只读的,而系统里的其他成员则没有任何权限。

4. 改变安全性设置

4.1 改变权限

# chmod命令用来改变文件和目录的安全性设置。
chmod options mode file
# mode参数可以使用八进制模式或符号模式进行安全性设置。
$ chmod 760 newfile
$ ls -l newfile 
-rwxrw---- 1 rich rich 0 Sep 20 19:16 newfile 

符号模式下指定权限的格式。

[ugoa…][[+-=][rwxXstugo…]

 $ chmod o+r newfile 
 $ ls -lF newfile 
 -rwxrw-r-- 1 rich rich 0 Sep 20 19:16 newfile* 

 X:如果对象是目录或者它已有执行权限,赋予执行权限。
 s:运行时重新设置UID或GID。
 t:保留文件或目录。
 u:将权限设置为跟属主一样。
 g:将权限设置为跟属组一样。
 o:将权限设置为跟其他用户一样。
options为chmod命令提供了另外一些功能。-R选项可以让权限的改变递归地作用到文件和子目录。你可以使用通配符指定多个文件,然后利用一条命令将权限更改应用到这些文件上。

4.2 改变所属关系

chown命令用来改变文件的属主,chgrp命令用来改变文件的默认属组。

chown options owner[.group] file

# 可用登录名或UID来指定文件的新属主。
chown dan newfile 
#chown命令也支持同时改变文件的属主和属组。
chown dan.shared newfile
# chgrp命令可以更改文件或目录的默认属组。
$ chgrp shared newfile

5. 共享文件

Linux系统上共享文件的方法是创建组。
Linux还为每个文件和目录存储了3个额外的信息位。
 设置用户ID(SUID):当文件被用户使用时,程序会以文件属主的权限运行。
 设置组ID(SGID):对文件来说,程序会以文件属组的权限运行;对目录来说,目录中创建的新文件会以目录的默认属组作为默认属组。
 粘着位:进程结束后文件还驻留(粘着)在内存中。

第八章、 管理文件系统

1. 探索 Linux 文件系统

1.1 基本的 Linux 文件系统

1.1.1 ext文件系统

Linux操作系统中引入的最早的文件系统叫作扩展文件系统(extended filesystem,简记为ext)。它为Linux提供了一个基本的类Unix文件系统:使用虚拟目录来操作硬件设备,在物理设备上按定长的块来存储数据。

1.1.2 ext2文件系统

ext2文件系统是ext文件系统基本功能的一个扩展,但保持了同样的结构。ext2文件系统扩展了索引节点表的格式来保存系统上每个文件的更多信息。

1.2 日志文件系统

1.2.1 ext3文件系统

它采用和ext2文件系统相同的索引节点表结构,但给每个存储设备增加了一个日志文件,以将准备写入存储设备的数据先记入日志。

1.2.2 ext4文件系统

除了支持数据压缩和加密,ext4文件系统还支持一个称作区段(extent)的特性。区段在存储设备上按块分配空间,但在索引节点表中只保存起始块的位置。

1.2.3 Reiser文件系统

1.2.4 JFS文件系统

1.2.5 XFS文件系统

1.3 写时复制文件系统

1.3.1 ZFS文件系统

1.3.2 Btrf文件系统

2. 操作文件系统

2.1 创建分区

fdisk工具用来帮助管理安装在系统上的任何存储设备上的分区。
fdisk命令

命 令 描 述
a 设置活动分区标志
b 编辑BSD Unix系统用的磁盘标签
c 设置DOS兼容标志
d 删除分区
l 显示可用的分区类型
m 显示命令选项
n 添加一个新分区
o 创建DOS分区表
p 显示当前分区表
q 退出,不保存更改
s 为Sun Unix系统创建一个新磁盘标签
t 修改分区的系统ID
u 改变使用的存储单位
v 验证分区表
w 将分区表写入磁盘
x 高级功能

2.2 创建文件系统

创建文件系统的命令行程序

工 具 用 途
mkefs 创建一个ext文件系统
mke2fs 创建一个ext2文件系统
mkfs.ext3 创建一个ext3文件系统
mkfs.ext4 创建一个ext4文件系统
mkreiserfs 创建一个ReiserFS文件系统
jfs_mkfs 创建一个JFS文件系统
mkfs.xfs 创建一个XFS文件系统
mkfs.zfs 创建一个ZFS文件系统
mkfs.btrfs 创建一个Btrfs文件系统
要想知道某个文件系统工具是否可用,可以使用type命令。
$ type mkfs.ext4 
mkfs.ext4 is /sbin/mkfs.ext4 
$ 
$ type mkfs.btrfs 
-bash: type: mkfs.btrfs: not found

2.3 文件系统的检查与修复

fsck options filesystem

可以在命令行上列出多个要检查的文件系统。

fsck的命令行选项

选 项 描 述
-a 如果检测到错误,自动修复文件系统
-A 检查/etc/fstab文件中列出的所有文件系统
-C 给支持进度条功能的文件系统显示一个进度条(只有ext2和ext3)
-N 不进行检查,只显示哪些检查会执行
-r 出现错误时提示
-R 使用-A选项时跳过根文件系统
-s 检查多个文件系统时,依次进行检查
-t 指定要检查的文件系统类型
-T 启动时不显示头部信息
-V 在检查时产生详细输出
-y 检测到错误时自动修复文件系统

3. 逻辑卷管理

3.1 Linux 中的 LVM

Linux LVM有两个可用的版本。
 LVM1:最初的LVM包于1998年发布,只能用于Linux内核2.4版本。它仅提供了基本的逻辑卷管理功能。
 LVM2:LVM的更新版本,可用于Linux内核2.6版本。它在标准的LVM1功能外提供了额外的功能。

3.1.1 快照

最初的Linux LVM允许你在逻辑卷在线的状态下将其复制到另一个设备。这个功能叫作快照。

3.1.2 条带化

LVM2提供的另一个引人注目的功能是条带化(striping)。有了条带化,可跨多个物理硬盘创建逻辑卷。

3.1.3 镜像

通过LVM安装文件系统并不意味着文件系统就不会再出问题。和物理分区一样,LVM逻辑
卷也容易受到断电和磁盘故障的影响。一旦文件系统损坏,就有可能再也无法恢复。

3.2 使用 Linux LVM

3.2.1 定义物理卷

创建过程的第一步就是将硬盘上的物理分区转换成Linux LVM使用的物理卷区段。

这可以通过pvcreate命令来完成。pvcreate定义了用于物理卷的物理分区。它只是简单地将分区标记成Linux LVM系统中的分区而已。

sudo pvcreate /dev/sdb1 

想查看创建进度的话,可以使用pvdisplay命令来显示已创建的物理卷列表。

$ sudo pvdisplay /dev/sdb1

3.2.2 创建卷组

要从命令行创建卷组,需要使用vgcreate命令。vgcreate命令需要一些命令行参数来定义卷组名以及你用来创建卷组的物理卷名。

$ sudo vgcreate Vol1 /dev/sdb1 
 Volume group "Vol1" successfully created

3.2.3 创建逻辑卷

要创建逻辑卷,使用lvcreate命令。
lvcreate的选项

选 项 长选项名 描 述
-c –chunksize 指定快照逻辑卷的单位大小
-C –contiguous 设置或重置连续分配策略
-i –stripes 指定条带数
-I –stripesize 指定每个条带的大小
-l –extents 指定分配给新逻辑卷的逻辑区段数,或者要用的逻辑区段的百分比
-L –size 指定分配给新逻辑卷的硬盘大小
–minor 指定设备的次设备号
-m –mirrors 创建逻辑卷镜像
-M –persistent 让次设备号一直有效
-n –name 指定新逻辑卷的名称
-p –permission 为逻辑卷设置读/写权限
-r –readahead 设置预读扇区数
-R –regionsize 指定将镜像分成多大的区
-s snapshot 创建快照逻辑卷
-Z –zero 将新逻辑卷的前1 KB数据设置为零
$ sudo lvcreate -l 100%FREE -n lvtest Vol1 
 Logical volume "lvtest" created 
$ 
# 如果想查看你创建的逻辑卷的详细情况,可用lvdisplay命令。
$ sudo lvdisplay Vol1 

3.2.4 创建文件系统

$ sudo mkfs.ext4 /dev/Vol1/lvtest

3.2.5 修改LVM

Linux LVM命令

命 令 功 能
vgchange 激活和禁用卷组
vgremove 删除卷组
vgextend 将物理卷加到卷组中
vgreduce 从卷组中删除物理卷
lvextend 增加逻辑卷的大小
lvreduce 减小逻辑卷的大小
通过使用这些命令行程序,就能完全控制你的Linux LVM环境。

第九章、 安装软件程序

1. 基于 Debian 的系统

dpkg命令是基于Debian系PMS工具的核心。包含在这个PMS中的其他工具有:
 apt-get
 apt-cache
 aptitude

1.1 用 aptitude 管理软件包

Linux系统管理员面对的一个常见任务是确定系统上已经安装了什么软件包。好在aptitude有个很方便的交互式界面可以轻松完成这项任务。

1.2 用 aptitude 安装软件包

aptitude search package_name
aptitude install package_name

1.3 用 aptitude 更新软件

要用软件仓库中的新版本妥善地更新系统上所有的软件包,可用safe-upgrade选项。

aptitude safe-upgrade

1.4 用 aptitude 卸载软件

要想只删除软件包而不删除数据和配置文件,可以使用aptitude的remove选项。要删除软件包和相关的数据和配置文件,可用purge选项。

$ sudo aptitude purge wine

如果在软件包名称的前面看到一个c,意味着软件已删除,但配置文件尚未从系统中清除;如果前面是个p的话,说明配置文件也已删除。

1.5 aptitude 仓库

aptitude默认的软件仓库位置是在安装Linux发行版时设置的。具体位置存储在文件/etc/apt/sources. list中。

2. 基于 Red Hat 的系统

和基于Debian的发行版类似,基于Red Hat的系统也有几种不同的可用前端工具。常见的有以下3种。
 yum:在Red Hat和Fedora中使用。
 urpm:在Mandriva中使用。
 zypper:在openSUSE中使用。

2.1 列出已安装包

yum list installed 
#输出的信息可能会在屏幕上一闪而过,所以最好是将已安装包的列表重定向到一个文件中。可以用more或less命令(或一个GUI编辑器)按照需要查看这个列表。
yum list installed > installed_software
#yum擅长找出某个特定软件包的详细信息。 
yum list xterm

如何用zypper和urpm列出已安装软件

版 本 前端工具 命 令
Mandriva urpm rpm -qa > installed_software
openSUSE zypper zypper search -I > installed_software

如何用zypper和urpm查看各种包详细信息

信息类型 前端工具 命 令
包信息 urpm urpmq -i package_name
是否安装 urpm rpm -q package_name
包信息 zypper zypper search -s package_name
是否安装 zypper 同样的命令,注意在Status列查找i

要找出系统上的某个特定文件属于哪个软件包

yum provides file_name 

2.2 用 yum 安装软件

yum install package_name

手动下载rpm安装文件并用yum安装,这叫作本地安装。基本的命令是:

yum localinstall package_name.rpm 

如何用zypper和urpm安装软件

前端工具 命 令
urpm urpmi package_name
zypper zypper install package_name

2.3 用 yum 更新软件

# 列出所有已安装包的可用更新
yum list updates 
# 想对更新列表中的所有包进行更新
yum update 

如何用zypper和urpm更新软件

前端工具 命 令
urpm urpmi --auto-update --update
zypper zypper update

2.4 用 yum 卸载软件

# 只删除软件包而保留配置文件和数据文件
yum remove package_name 
# 要删除软件和它所有的文件
yum erase package_name

如何用zypper和urpm卸载软件

前端工具 命 令
urpm urpme package_name
zypper zypper remove package_name

2.5 处理损坏的包依赖关系

# 系统出现了这个问题
yum clean all 
# 只要清理了放错位置的文件就可以了
yum deplist xterm 
# 如果这样仍未解决问题,可以输入下面命令
yum update --skip-broken
# --skip-broken选项允许你忽略依赖关系损坏的那个包,继续去更新其他软件包。

用zypper和urpm修复损坏的依赖关系

前端工具 命 令
urpm urpmi –clean
Zipper zypper verify

2.6 yum 软件仓库

要想知道你现在正从哪些仓库中获取软件,输入如下命令:

yum repolist

zypper和urpm的库

动 作 前端工具 命 令
显示仓库 urpm urpmq --list-media
添加仓库 urpm urpmi.addmedia path_name
显示仓库 zypper zypper repos
添加仓库 zypper zypper addrepo path_name

第十章、 使用编辑器

1. vim 基础

1.1 vim基础

vim编辑器有两种操作模式:
 普通模式
 插入模式
vim中有用来移动光标的命令。
 h:左移一个字符。
 j:下移一行(文本中的下一行)。
 k:上移一行(文本中的上一行)。
 l:右移一个字符。
 PageDown(或Ctrl+F):下翻一屏。
 PageUp(或Ctrl+B):上翻一屏。
 G:移到缓冲区的最后一行。
 num G:移动到缓冲区中的第num行。
 gg:移到缓冲区的第一行。

在命令行模式下有几个命令可以将缓冲区的数据保存到文件中并退出vim。
 q:如果未修改缓冲区数据,退出。
 q!:取消所有对缓冲区数据的修改并退出。
 w filename:将文件保存到另一个文件中。
 wq:将缓冲区数据保存到文件中并退出。

1.2 编辑数据

vim编辑命令

命 令 描 述
x 删除当前光标所在位置的字符
dd 删除当前光标所在行
dw 删除当前光标所在位置的单词
d$ 删除当前光标所在位置至行尾的内容
J 删除当前光标所在行行尾的换行符(拼接行)
u 撤销前一编辑命令
a 在当前光标后追加数据
A 在当前光标所在行行尾追加数据
r char 用char替换当前光标所在位置的单个字符
R text 用text覆盖当前光标所在位置的数据,直到按下ESC键

1.3 复制和粘贴

vim中复制命令是y(代表yank),在复制文本后,把光标移动到你想放置文本的地方,输入p命令。复制的文本就会出现在该位置。

1.4 查找和替换

要输入一个查找字符串,就按下斜线(/)键。光标会跑到消息行,然后vim会显示出斜线。
vim编辑器会采用以下三种回应中的一种
 如果要查找的文本出现在光标当前位置之后,则光标会跳到该文本出现的第一个位置。
 如果要查找的文本未在光标当前位置之后出现,则光标会绕过文件末尾,出现在该文本
所在的第一个位置(并用一条消息指明)。
 输出一条错误消息,说明在文件中没有找到要查找的文本。

2. nano 编辑器

nano myprog.c 

nano控制命令

命 令 描 述
CTRL+C 显示光标在文本编辑缓冲区中的位置
CTRL+G 显示nano的主帮助窗口
CTRL+J 调整当前文本段落
CTRL+K 剪切文本行,并将其保存在剪切缓冲区
CTRL+O 将当前文本编辑缓冲区的内容写入文件
CTRL+R 将文件读入当前文本编辑缓冲区
CTRL+T 启动可用的拼写检查器
CTRL+U 将剪切缓冲区中的内容放入当前行
CTRL+V 翻动到文本编辑缓冲区中的下一页内容
CTRL+W 在文本编辑缓冲区中搜索单词或短语
CTRL+X 关闭当前文本编辑缓冲区,退出nano,返回shell
CTRL+Y 翻动到文本编辑缓冲区中的上一页内容

第十一章、 构建基本脚本

1. 创建 shell 脚本文件

在创建shell脚本文件时,必须在文件的第一行指定要使用的shell。其格式为:

#!/bin/bash

在通常的shell脚本中,井号(#)用作注释行。shell并不会处理shell脚本中的注释行。然而,shell脚本文件的第一行是个例外,#后面的惊叹号会告诉shell用哪个shell来运行脚本

PATH环境变量被设置成只在一组目录中查找命令。要让shell找到test1脚本,只需采取以下两种作法之一:
 将shell脚本文件所处的目录添加到PATH环境变量中;
 在提示符中用绝对或相对文件路径来引用shell脚本文件。

2. 显示消息

$ echo This is a test 
This is a test 

echo命令是shell脚本中与用户交互的重要工具

3. 命令替换

shell脚本中最有用的特性之一就是可以从命令输出中提取信息,并将其赋给变量。把输出赋给变量之后,就可以随意在脚本中使用了。这个特性在处理脚本数据时尤为方便。
有两种方法可以将命令输出赋给变量:
 反引号字符(`)
 $()格式

4. 重定向输入和输出

4.1 输出重定向

最基本的重定向将命令的输出发送到一个文件中。bash shell用大于号(>)来完成这项功能:

command > outputfile

如果输出文件已经存在了,重定向操作符会用新的文件数据覆盖已有文件。

4.2 输入重定向

输入重定向符号是小于号(<):

command < inputfile

wc命令可以对对数据中的文本进行计数。默认情况下,它会输出3个值:
 文本的行数
 文本的词数
 文本的字节数

5. 管道

将一个命令的输出作为另一个命令的输入。
管道被放在命令之间,将一个命令的输出重定向到另一个命令中:

command1 | command2

sort命令的输出会一闪而过,所以可以用一条文本分页命令(例如less或more)来强行将输出按屏显示。

$ rpm -qa | sort | more

这行命令序列会先执行rpm命令,将它的输出通过管道传给sort命令,然后再将sort的输出通过管道传给more命令来显示。

6. 执行数学运算

在shell脚本中有两种途径来进行数学运算。

6.1 expr 命令

$ expr 1 + 5 
6 

expr命令能够识别少数的数学和字符串操作符
expr命令操作符

操 作 符 描 述
ARG1 | ARG2 如果ARG1既不是null也不是零值,返回ARG1;否则返回ARG2
ARG1 & ARG2 如果没有参数是null或零值,返回ARG1;否则返回0
ARG1 < ARG2 如果ARG1小于ARG2,返回1;否则返回0
ARG1 <= ARG2 如果ARG1小于或等于ARG2,返回1;否则返回0
ARG1 = ARG2 如果ARG1等于ARG2,返回1;否则返回0
ARG1 != ARG2 如果ARG1不等于ARG2,返回1;否则返回0
ARG1 >= ARG2 如果ARG1大于或等于ARG2,返回1;否则返回0
ARG1 > ARG2 如果ARG1大于ARG2,返回1;否则返回0
ARG1 + ARG2 返回ARG1和ARG2的算术运算和
ARG1 - ARG2 返回ARG1和ARG2的算术运算差
ARG1 * ARG2 返回ARG1和ARG2的算术乘积
ARG1 / ARG2 返回ARG1被ARG2除的算术商
ARG1 % ARG2 返回ARG1被ARG2除的算术余数
STRING : REGEXP 如果REGEXP匹配到了STRING中的某个模式,返回该模式匹配
match STRING REGEXP 如果REGEXP匹配到了STRING中的某个模式,返回该模式匹配
substr STRING POS LENGTH 返回起始位置为POS(从1开始计数)、长度为LENGTH个字符的子字符串
index STRING CHARS 返回在STRING中找到CHARS字符串的位置;否则,返回0
length STRING 返回字符串STRING的数值长度
+ TOKEN 将TOKEN解释成字符串,即使是个关键字
(EXPRESSION) 返回EXPRESSION的值

6.2 使用方括号

bash shell为了保持跟Bourne shell的兼容而包含了expr命令,但它同样也提供了一种更简单的方法来执行数学表达式。在bash中,在将一个数学运算结果赋给某个变量时,可以用美元符和方括号($[ operation ])将数学表达式围起来。

$ var1=$[1 + 5] 
$ echo $var1 
6 

7. 退出脚本

7.1 查看退出状态码

Linux提供了一个专门的变量$?来保存上个已执行命令的退出状态码。

Linux退出状态码

状 态 码 描 述
0 命令成功结束
1 一般性未知错误
2 不适合的shell命令
126 命令不可执行
127 没找到命令
128 无效的退出参数
128+x 与Linux信号x相关的严重错误
130 通过Ctrl+C终止的命令
255 正常范围之外的退出状态码

7.2 exit 命令

可以改变这种默认行为,返回自己的退出状态码。exit命令允许你在脚本结束时指定一个退出状态码。

$ cat test13 
#!/bin/bash 
# testing the exit status 
var1=10 
var2=30 
var3=$[$var1 + $var2] 
echo The answer is $var3 
exit 5 
$ 

退出状态码最大只能是255,退出状态码被缩减到了0~255的区间。shell通过模运算得到这个结果。一个值的模就是被除后的余数。最终的结果是指定的数值除以256后得到的余数。在这个例子中,指定的值是300(返回值),余数是44,因此这个余数就成了最后的状态退出码。

The value is 300 
$ echo $? 
44 
$ 

第十二章、 使用结构化命令

1. 使用 if-then 语句

最基本的结构化命令就是if-then语句。if-then语句有如下格式。

if command
then 
 commands
fi 

bash shell的if语句会运行if后面的那个命令。如果该命令的退出状态码(参见第11章)是0(该命令成功运行),位于then部分的命令就会被执行。如果该命令的退出状态码是其他值,then部分的命令就不会被执行,bash shell会继续执行脚本中的下一个命令。fi语句用来表示if-then语句到此结束。

2. if-then-else 语句

if-then-else语句在语句中提供了另外一组命令。

if command
then 
 commands
else 
 commands
fi

3. 嵌套 if

else部分的另一种形式:elif。这样就不用再书写多个if-then语句了。elif使用另一个if-then语句延续else部分。

if command1
then 
 commands
elif command2
then 
 more commands
fi 

if-then-elif嵌套组合。

if command1
then 
 command set 1 
elif command2
then 
 command set 2 
elif command3
then 
 command set 3 
elif command4
then 
 command set 4 
fi

4. test 命令

test命令提供了在if-then语句中测试不同条件的途径。如果条件不成立,test命令就会退出并返回非零的退出状态码。
test命令的格式非常简单。

test condition 

condition是test命令要测试的一系列参数和值。

if test condition
then 
 commands
fi 

bash shell提供了另一种条件测试方法,无需在if-then语句中声明test命令。

if [ condition ] 
then 
 commands
fi 

方括号定义了测试条件。注意,第一个方括号之后和第二个方括号之前必须加上一个空格,否则就会报错。
test命令可以判断三类条件:
 数值比较
 字符串比较
 文件比较

4.1 数值比较

test命令的数值比较功能

比 较 描 述
n1 -eq n2 检查n1是否与n2相等
n1 -ge n2 检查n1是否大于或等于n2
n1 -gt n2 检查n1是否大于n2
n1 -le n2 检查n1是否小于或等于n2
n1 -lt n2 检查n1是否小于n2
n1 -ne n2 检查n1是否不等于n2

4.2 字符串比较

字符串比较测试

比 较 描 述
str1 = str2 检查str1是否和str2相同
str1 != str2 检查str1是否和str2不同
str1 < str2 检查str1是否比str2小
str1 > str2 检查str1是否比str2大
-n str1 检查str1的长度是否非0
-z str1 检查str1的长度是否为0

 大于号和小于号必须转义,否则shell会把它们当作重定向符号,把字符串值当作文件名;
 大于和小于顺序和sort命令所采用的不同。

4.3 文件比较

test命令的文件比较功能

比 较 描 述
-d file 检查file是否存在并是一个目录
-e file 检查file是否存在
-f file 检查file是否存在并是一个文件
-r file 检查file是否存在并可读
-s file 检查file是否存在并非空
-w file 检查file是否存在并可写
-x file 检查file是否存在并可执行
-O file 检查file是否存在并属当前用户所有
-G file 检查file是否存在并且默认组与当前用户相同
file1 -nt file2 检查file1是否比file2新
file1 -ot file2 检查file1是否比file2旧

5. 复合条件测试

if-then语句允许你使用布尔逻辑来组合测试。有两种布尔运算符可用:
 [ condition1 ] && [ condition2 ]
 [ condition1 ] || [ condition2 ]
使用AND布尔运算符时,两个比较都必须满足。

6. if-then 的高级特性

bash shell提供了两项可在if-then语句中使用的高级特性:
 用于数学表达式的双括号
 用于高级字符串处理功能的双方括号
双括号命令的格式如下:

(( expression )) 

expression可以是任意的数学赋值或比较表达式。

双括号命令符号

符 号 描 述
val++ 后增
val– 后减
++val 先增
–val 先减
! 逻辑求反
~ 位求反
** 幂运算
<< 左位移
>> 右位移
& 位布尔和
| 位布尔或
&& 逻辑和
|| 逻辑或

双方括号命令的格式如下:

[[ expression ]]

7. case 命令

case命令会采用列表格式来检查单个变量的多个值。

case variable in 
pattern1 | pattern2) commands1;; 
pattern3) commands2;; 
*) default commands;; 
esac 

case命令提供了一个更清晰的方法来为变量每个可能的值指定不同的选项。

第十三章、 更多的结构化命令

1. for 命令

for命令的基本格式。

for var in list 
do 
 commands 
done
  • 读取列表中的值,实例:
#!/bin/bash 
# basic for command 
for test in Alabama Alaska Arizona Arkansas California Colorado 
do 
 echo The next state is $test 
done 
  • 读取列表中的复杂值,实例:
#!/bin/bash 
# another example of how not to use the for command 
for test in I don\'t know if "this'll" work 
do 
 echo "word:$test" 
done 

 使用转义字符(反斜线)来将单引号转义;
 使用双引号来定义用到单引号的值。

  • 从变量读取列表,实例:
#!/bin/bash 
# using a variable to hold the list 
list="Alabama Alaska Arizona Arkansas Colorado" 
list=$list" Connecticut"
for state in $list 
do 
 echo "Have you ever visited $state?" 
done 
  • 从命令读取值,实例:
#!/bin/bash 
# reading values from a file 
file="states" 
for state in $(cat $file) 
do 
 echo "Visit beautiful $state" 
done 
  • 更改字段分隔符
    bash shell会将下列字符当作字段分隔符:
     空格
     制表符
     换行符
    想修改IFS的值,使其只能识别换行符,那就必须这么做:
    IFS=$‘\n’
#!/bin/bash 
# reading values from a file 
file="states" 
IFS=$'\n' 
for state in $(cat $file) 
do 
 echo "Visit beautiful $state" 
done 

2. C 语言风格的 for 命令

bash中C语言风格的for循环的基本格式。

for (( variable assignment ; condition ; iteration process )) 

for (( a = 1; a < 10; a++ ))

有些部分并没有遵循bash shell标准的for命令:
 变量赋值可以有空格;
 条件中的变量不以美元符开头;
 迭代过程的算式未用expr命令格式。

3. while 命令

while命令的格式是:

while test command 
do 
 other commands 
done 

4. until 命令

until命令和while命令工作的方式完全相反。until命令要求你指定一个通常返回非零退
出状态码的测试命令。

until test commands 
do 
 other commands 
done 

在until命令语句中放入多个测试命令。只有最后一个命令的退出状态码决定了bash shell是否执行已定义的other commands。

5. 控制循环

有两个命令能帮我们控制循环内部的情况:
 break命令
 continue命令

5.1 break 命令

break命令是退出循环的一个简单方法。可以用break命令来退出任意类型的循环,包括while和until循环。

break命令接受单个命令行参数值:

break n

其中n指定了要跳出的循环层级。默认情况下,n为1,表明跳出的是当前的循环。如果你将n设为2,break命令就会停止下一级的外部循环。

5.2 continue 命令

continue命令可以提前中止某次循环中的命令,但并不会完全终止整个循环。
和break命令一样,continue命令也允许通过命令行参数指定要继续执行哪一级循环:

continue n

其中n定义了要继续的循环层级。

6. 处理循环的输出

done > output.txt 

shell会将for命令的结果重定向到文件output.txt中,而不是显示在屏幕上。

第十四章、 处理用户输入

1. 命令行参数

1.1 读取参数

位置参数变量是标准的数字:$0是程序名,$1是第一个参数,$2是第二个参数,依次类推,直到第九个参数$9。

#!/bin/bash 
# testing string parameters 
# 
echo Hello $1, glad to meet you. 

$ ./test3.sh Rich
Hello Rich, glad to meet you.

1.2 读取脚本名

可以用$0参数获取shell在命令行启动的脚本名。

$ cat test5.sh
#!/bin/bash 
# Testing the $0 parameter 
# 
echo The zero parameter is set to: $0 
# 
$ 
$ bash test5.sh
The zero parameter is set to: test5.sh 

1.3 测试参数

使用了-n测试来检查命令行参数$1中是否有数据。

2. 特殊参数变量

2.1 参数统计

特殊变量$#含有脚本运行时携带的命令行参数的个数。

$ ./test8.sh 1 2 3 4 5
There were 5 parameters supplied. 

if-then语句用-ne测试命令行参数数量。

if [ $# -ne 2 ] 
then 
 echo 
 echo Usage: test9.sh a b 
 echo

$ bash test9.sh
Usage: test9.sh a b

变量${$#}就代表了最后一个命令行参数变量。

2.2 抓取所有的数据

$*和$@变量可以用来轻松访问所有的参数。
$*变量会将这些参数视为一个整体,而不是多个个体。
$@变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。

3. 移动变量

bash shell的shift命令能够用来操作命令行参数。在使用shift命令时,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3的值会移到$2中

4. 处理选项

4.1 查找选项

$ cat test15.sh
#!/bin/bash 
# extracting command line options as parameters 
# 
echo 
while [ -n "$1" ] 
do 
 case "$1" in 
 -a) echo "Found the -a option" ;; 
 -b) echo "Found the -b option" ;; 
 -c) echo "Found the -c option" ;; 
 *) echo "$1 is not an option" ;; 
 esac 
 shift 
done 
$ 
$ ./test15.sh -a -b -c -d
Found the -a option 
Found the -b option 
Found the -c option 
-d is not an option

4.2 使用 getopt 命令

getopt命令是一个在处理命令行选项和参数时非常方便的工具。它能够识别命令行参数,从而在脚本中解析它们时更方便。

getopt optstring parameters

optstring是这个过程的关键所在。它定义了命令行有效的选项字母,还定义了哪些选项字
母需要参数值。

$ getopt ab:cd -a -b test1 -cd test2 test3
 -a -b test1 -c -d -- test2 test3 
$ 

optstring定义了四个有效选项字母:a、b、c和d。冒号(:)被放在了字母b后面,因为b选项需要一个参数值。当getopt命令运行时,它会检查提供的参数列表(-a -b test1 -cd test2 test3),并基于提供的optstring进行解析。注意,它会自动将-cd选项分成两个单独的选项,并插入双破折线来分隔行中的额外参数。

5. 将选项标准化

常用的Linux命令选项

选 项 描 述
-a 显示所有对象
-c 生成一个计数
-d 指定一个目录
-e 扩展一个对象
-f 指定读入数据的文件
-h 显示命令的帮助信息
-i 忽略文本大小写
-l 产生输出的长格式版本
-n 使用非交互模式(批处理)
-o 将所有输出重定向到的指定的输出文件
-q 以安静模式运行
-r 递归地处理目录和文件
-s 以安静模式运行
-v 生成详细输出
-x 排除某个对象
-y 对所有问题回答yes

6. 获得用户输入

6.1 基本的读取

read命令从标准输入(键盘)或另一个文件描述符中接受输入。

$ cat test22.sh
#!/bin/bash 
# testing the read -p option 
# 
read -p "Please enter your age: " age 
days=$[ $age * 365 ] 
echo "That makes you over $days days old! " 
# 
$ 
$ ./test22.sh
Please enter your age: 10
That makes you over 3650 days old! 
$

read命令会将提示符后输入的所有数据分配给单个变量,要么你就指定多个变量。输入的每个数据值都会分配给变量列表中的下一个变量。如果变量数量不够,剩下的数据就全部分配给最后一个变量。

6.2 超时

-t选项指定了read命令等待输入的秒数。当计时器过期后,read命令会返回一个非零退出状态码。

 $ cat test25.sh
#!/bin/bash 
# timing the data entry 
# 
if read -t 5 -p "Please enter your name: " name 
then 
 echo "Hello $name, welcome to my script" 
else 
 echo 
 echo "Sorry, too slow! " 
fi 

如果计时器过期,read命令会以非零退出状态码退出,可以使用如if-then语句或while循环这种标准的结构化语句来理清所发生的具体情况。

也可以不对输入过程计时,而是让read命令来统计输入的字符数。当输入的字符达到预设的字符数时,就自动退出,将输入的数据赋给变量。-n选项和值1一起使用,告诉read命令在接受单个字符后退出。

6.3 隐藏方式读取

-s选项可以避免在read命令中输入的数据出现在显示器上

 read -s -p "Enter your password: " pass 

$ ./test27.sh
Enter your password: 

6.4 从文件中读取

也可以用read命令来读取Linux系统上文件里保存的数据。当文件中再没有内容时,read命令会退出并返回非零退出状态码。

$ cat test28.sh 
#!/bin/bash 
# reading data from a file 
# 
count=1 
cat test | while read line 
do 
 echo "Line $count: $line" 
 count=$[ $count + 1] 
done 
echo "Finished processing the file" 
$ 
$ cat test
The quick brown dog jumps over the lazy fox. 
This is a test, this is only a test. 
O Romeo, Romeo! Wherefore art thou Romeo? 
$ 
$ ./test28.sh
Line 1: The quick brown dog jumps over the lazy fox. 
Line 2: This is a test, this is only a test. 
Line 3: O Romeo, Romeo! Wherefore art thou Romeo? 
Finished processing the file 

第十五章、 呈现数据

1. 理解输入和输出

两种显示脚本输出的方法:

  • 在显示器屏幕上显示输出
  • 将输出重定向到文件中

1.1 标准文件描述符

Linux用文件描述符(file descriptor)来标识每个文件对象。文件描述符是一个非负整数,可以唯一标识会话中打开的文件。

出于特殊目的,bash shell保留了前三个文件描述符(0、1和2),如下表
Linux的标准文件描述符

文件描述符 缩 写 描 述
0 STDIN 标准输入
1 STDOUT 标准输出
2 STDERR 标准错误

1.2 重定向错误

$ ls -al badfile 2> test4 
$ cat test4 
ls: cannot access badfile: No such file or directory

现在运行该命令,错误消息不会出现在屏幕上了。该命令生成的任何错误消息都会保存在输出文件中。

如果愿意,也可以将STDERR和STDOUT的输出重定向到同一个输出文件。为此bash shell提供了特殊的重定向符号&>。

$ ls -al test test2 test3 badtest &> test7 
$ cat test7 
ls: cannot access test: No such file or directory 
ls: cannot access badtest: No such file or directory 
-rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2 
-rw-rw-r-- 1 rich rich 0 2014-10-16 11:33 test3 
$ 

当使用&>符时,命令生成的所有输出都会发送到同一位置,包括数据和错误。

2. 在脚本中重定向输出

有两种方法来在脚本中重定向输出:
 临时重定向行输出
 永久重定向脚本中的所有命令

在重定向到文件描述符时,你必须在文件描述符数字之前加一个&

echo "This is an error message" >&2

如果脚本中有大量数据需要重定向,那重定向每个echo语句就会很烦琐。取而代之,你可以用exec命令告诉shell在脚本执行期间重定向某个特定文件描述符。

3. 在脚本中重定向输入

exec命令允许你将STDIN重定向到Linux系统上的文件中:

exec 0< testfile 

4. 创建自己的重定向

4.1 创建输出文件描述符

可以用exec命令来给输出分配文件描述符。

$ cat test13 
#!/bin/bash 
# using an alternative file descriptor 
exec 3>test13out 
echo "This should display on the monitor" 
echo "and this should be stored in the file" >&3 
echo "Then this should be back on the monitor" 
$ ./test13 
This should display on the monitor 
Then this should be back on the monitor 
$ cat test13out 
and this should be stored in the file 
$ 

这个脚本用exec命令将文件描述符3重定向到另一个文件。

4.2 重定向文件描述符

怎么恢复已重定向的文件描述符。你可以分配另外一个文件描述符给标准文件描述符,反之亦然。这意味着你可以将STDOUT的原来位置重定向到另一个文件描述符。

4.3 关闭文件描述符

要关闭文件描述符,将它重定向到特殊符号&-。脚本中看起来如下:

exec 3>&- 

一旦关闭了文件描述符,就不能在脚本中向它写入任何数据,否则shell会生成错误消息。

5. 列出打开的文件描述符

lsof命令会列出整个Linux系统打开的所有文件描述符。lsof命令位于/usr/sbin目录。要想以普通用户账户来运行它,必须通过全路径名来引用:

$ /usr/sbin/lsof 

该命令会产生大量的输出。它会显示当前Linux系统上打开的每个文件的有关信息。这包括后台运行的所有进程以及登录到系统的任何用户。

可以用特殊环境变量$$(shell会将它设为当前PID)。-a选项用来对其他两个选项的结果执行布尔AND运算,这会产生如下输出。

$ /usr/sbin/lsof -a -p $$ -d 0,1,2 
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME 
bash 3344 rich 0u CHR 136,0 2 /dev/pts/0 
bash 3344 rich 1u CHR 136,0 2 /dev/pts/0 
bash 3344 rich 2u CHR 136,0 2 /dev/pts/0 
$ 

当前进程(bash shell)的默认文件描述符(0、1和2)

lsof的默认输出

描 述
COMMAND 正在运行的命令名的前9个字符
PID 进程的PID
USER 进程属主的登录名
FD 文件描述符号以及访问类型(r代表读,w代表写,u代表读写)
TYPE 文件的类型(CHR代表字符型,BLK代表块型,DIR代表目录,REG代表常规文件)
DEVICE 设备的设备号(主设备号和从设备号)
SIZE 如果有的话,表示文件的大小
NODE 本地文件的节点号
NAME 文件名

6. 阻止命令输出

在Linux系统上null文件的标准位置是/dev/null。你重定向到该位置的任何数据都会被丢掉,不会显示。

$ ls -al badfile test16 2> /dev/null 
-rwxr--r-- 1 rich rich 135 Oct 29 19:57 test16* 
$ 

7. 创建临时文件

7.1 创建本地临时文件

要用mktemp命令在本地目录中创建一个临时文件,你只要指定一个文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾加上6个X就行了。

$ mktemp testing.XXXXXX 
$ ls -al testing* 
-rw------- 1 rich rich 0 Oct 17 21:30 testing.UfIi13

mktemp命令会用6个字符码替换这6个X,从而保证文件名在目录中是唯一的。

7.2 在/tmp 目录创建临时文件

-t选项会强制mktemp命令来在系统的临时目录来创建该文件。在用这个特性时,mktemp命令会返回用来创建临时文件的全路径,而不是只有文件名。

$ mktemp -t test.XXXXXX 
/tmp/test.xG3374 
$ ls -al /tmp/test* 
-rw------- 1 rich rich 0 2014-10-29 18:41 /tmp/test.xG3374 
$ 

7.3 创建临时目录

-d选项告诉mktemp命令来创建一个临时目录而不是临时文件。

8. 记录消息

tee命令相当于管道的一个T型接头。

tee filename

由于tee会重定向来自STDIN的数据,你可以用它配合管道命令来重定向命令输出。

第十六章、 控制脚本

1. 处理信号

Linux信号

信 号 描 述
1 SIGHUP 挂起进程
2 SIGINT 终止进程
3 SIGQUIT 停止进程
9 SIGKILL 无条件终止进程
15 SIGTERM 尽可能终止进程
17 SIGSTOP 无条件停止进程,但不是终止进程
18 SIGTSTP 停止或暂停进程,但不终止进程
19 SIGCONT 继续运行停止的进程

1.1 生成信号

Ctrl+C组合键会生成SIGINT信号
Ctrl+Z组合键会生成一个SIGTSTP信号
可以用kill命令来发送一个SIGKILL信号来终止。

$ kill -9 2456

1.2 捕获信号

trap命令允许你来指定shell脚本要监看并从shell中拦截的Linux信号。
trap命令的格式是:

trap commands signals 

1.3 捕获脚本退出

除了在shell脚本中捕获信号,你也可以在shell脚本退出时进行捕获。

trap "echo Goodbye..." EXIT 

当脚本运行到正常的退出位置时,捕获就被触发了,shell会执行在trap命令行指定的命令。
如果提前退出脚本,同样能够捕获到EXIT。

2. 以后台模式运行脚本

以后台模式运行shell脚本非常简单。只要在命令后加个&符就行了。

$ ./test4.sh &

3. 在非控制台下运行脚本

nohup命令运行了另外一个命令来阻断所有发送给该进程的SIGHUP信号。这会在退出终端会话时阻止进程退出。
nohup命令的格式如下:

$ nohup ./test1.sh &
[1] 3856 
$ nohup: ignoring input and appending output to 'nohup.out' 

4. 作业控制

启动、停止、终止以及恢复作业的这些功能统称为作业控制。通过作业控制,就能完全控制shell环境中所有进程的运行方式了。

4.1 查看作业

作业控制中的关键命令是jobs命令。

$ jobs
[1]+ Stopped ./test10.sh 
[2]- Running ./test10.sh > test10.out &

要想查看作业的PID,可以在jobs命令中加入-l选项(小写的L)。
$ jobs -l
jobs命令参数
参 数 描 述
-l 列出进程的PID以及作业号
-n 只列出上次shell发出的通知后改变了状态的作业
-p 只列出作业的PID
-r 只列出运行中的作业
-s 只列出已停止的作业

4.2 重启停止的作业

只需要使用bg命令就可以将其以后台模式重启。
要以前台模式重启作业,可用带有作业号的fg命令

5. 调整谦让度

在多任务操作系统中(Linux就是),内核负责将CPU时间分配给系统上运行的每个进程。调度优先级(scheduling priority)是内核分配给进程的CPU时间(相对于其他进程)。调度优先级是个整数值,从-20(最高优先级)到+19(最低优先级)。默认情况下,bash shell以优先级0来启动所有进程。

5.1 nice 命令

nice命令允许你设置命令启动时的调度优先级。

$ nice -n 10 ./test4.sh > test4.out &
[1] 4973 
$ 
$ ps -p 4973 -o pid,ppid,ni,cmd
 PID PPID NI CMD 
 4973 4721 10 /bin/bash ./test4.sh 
$ 

注意,必须将nice命令和要启动的命令放在同一行中。ps命令的输出验证了谦让度值(NI列)已经被调整到了10。

5.2 renice 命令

改变系统上已运行命令的优先级。

$ renice -n 10 -p 5055
5055: old priority 0, new priority 10 
$ 
$ ps -p 5055 -o pid,ppid,ni,cmd
 PID PPID NI CMD 
 5055 4721 10 /bin/bash ./test11.sh 
$ 

renice命令会自动更新当前运行进程的调度优先级。和nice命令一样,renice命令也有一些限制:
 只能对属于你的进程执行renice;
 只能通过renice降低进程的优先级;
 root用户可以通过renice来任意调整进程的优先级。

6. 定时运行作业

6.1 用 at 命令来计划执行作业

6.1.1 at命令的格式

at命令的基本格式非常简单:

at [-f filename] time

at命令会将STDIN的输入放到队列中。你可以用-f参数来指定用于读取命令(脚本文件)的文件名。time参数指定了Linux系统何时运行该作业。

at命令能识别多种不同的时间格式。
 标准的小时和分钟格式,比如10:15。
 AM/PM指示符,比如10:15 PM。
 特定可命名时间,比如now、noon、midnight或者teatime(4 PM)。

除了指定运行作业的时间,也可以通过不同的日期格式指定特定的日期。
 标准日期格式,比如MMDDYY、MM/DD/YY或DD.MM.YY。
 文本日期,比如Jul 4或Dec 25,加不加年份均可。
 你也可以指定时间增量。

  • 当前时间+25 min
  • 明天10:15 PM
  • 10:15+7天

6.1.2. 获取作业的输出

$ at -f test13.sh now
job 7 at 2015-07-14 12:38

at命令会显示分配给作业的作业号以及为作业安排的运行时间。-f选项指明使用哪个脚本文件,now指示at命令立刻执行该脚本。

6.1.3 列出等待的作业

atq命令可以查看系统中有哪些作业在等待

6.1.4 删除作业

atrm命令来删除等待中的作业。

6.2 安排需要定期执行的脚本

Linux系统使用cron程序来安排要定期执行的作业。

6.2.1 cron时间表

cron时间表采用一种特别的格式来指定作业何时运行。其格式如下:

min hour dayofmonth month dayofweek command 

cron时间表允许你用特定值、取值范围(比如1~5)或者是通配符(星号)来指定条目。

在每个月的第一天中午12点执行命令。可以用下面的格式:

00 12 1 * * command

6.2.2 构建cron时间表

Linux提供了crontab命令来处理cron时间表。要列出已有的cron时间表,可以用-l选项。

$ crontab -l

6.2.3 浏览cron目录

$ ls /etc/cron.*ly

第十七章、 创建函数

1. 基本的脚本函数

1.1 创建函数

第一种格式采用关键字function,后跟
分配给该代码块的函数名。

function name { 
 commands 
}

第二种格式更接近于其他编程语言中定义函数的方式。

name() { 
commands 
}

1.2 使用函数

只需要像其他shell命令一样,在行中指定函数名就行了。

$ cat test1 
#!/bin/bash 
# using a function in a script 
function func1 { 
 echo "This is an example of a function" 
} 
count=1 
while [ $count -le 5 ] 
do 
 func1 
 count=$[ $count + 1 ] 
done 
echo "This is the end of the loop" 
func1 
echo "Now this is the end of the script" 
$ 
$ ./test1 
This is an example of a function 
This is an example of a function 
This is an example of a function 
This is an example of a function 
This is an example of a function 
This is the end of the loop 

如果在函数被定义前使用函数,你会收到一条错误消息。
函数名必须是唯一的,否则也会有问题。

2. 返回值

2.1 默认退出状态码

可以用标准变量$?来确定函数的退出状态码。

2.2 使用 return 命令

但当用这种方法从函数中返回值时,要小心了。记住下面两条技巧来避免问题:
 记住,函数一结束就取返回值;
 记住,退出状态码必须是0~255。

2.3 使用函数输出

可以对函数的输出采用同样的处理办法。可以用这种技术来获得任何类型的函数输出,并将其保存到变量中:

result='dbl' 

这个命令会将dbl函数的输出赋给$result变量。

3. 在函数中使用变量

3.1 向函数传递参数

$ cat test6 
#!/bin/bash 
# passing parameters to a function 
function addem { 
 if [ $# -eq 0 ] || [ $# -gt 2 ] 
 then 
 echo -1 
 elif [ $# -eq 1 ] 
 then 
 echo $[ $1 + $1 ] 
 else 
 echo $[ $1 + $2 ] 
 fi 
} 
echo -n "Adding 10 and 15: " 
value=$(addem 10 15) 
echo $value 
echo -n "Let's try adding just one number: " 
value=$(addem 10) 
echo $value 
echo -n "Now trying adding no numbers: " 
value=$(addem) 
echo $value 
echo -n "Finally, try adding three numbers: " 
value=$(addem 10 15 20) 
echo $value 
$ 
$ ./test6 
Adding 10 and 15: 25 
Let's try adding just one number: 20 
Now trying adding no numbers: -1 
Finally, try adding three numbers: -1 
$ 

3.2 在函数中处理变量

函数使用两种类型的变量:
 全局变量
 局部变量

3.2.1 全局变量

全局变量是在shell脚本中任何地方都有效的变量。

3.2.2 局部变量

函数内部使用的任何变量都可以被声明成局部变量。

4. 数组变量和函数

4.1 向函数传数组参数

将数组变量作为函数参数,函数只会取数组变量的第一个值。

$ cat test10 
#!/bin/bash 
# array variable to function test 
function testit { 
 local newarray 
 newarray=(;'echo "$@"') 
 echo "The new array value is: ${newarray[*]}" 
} 
myarray=(1 2 3 4 5) 
echo "The original array is ${myarray[*]}" 
testit ${myarray[*]} 
$ 
$ ./test10 
The original array is 1 2 3 4 5 
The new array value is: 1 2 3 4 5 
$ 

用$myarray变量来保存所有的数组元素,然后将它们都放在函数的命令行上。该函数随后从命令行参数中重建数组变量。在函数内部,数组仍然可以像其他数组一样使用。

4.2 从函数返回数组

从函数里向shell脚本传回数组变量也用类似的方法。

$ cat test12 
#!/bin/bash 
# returning an array value 
function arraydblr { 
 local origarray 
 local newarray 
 local elements 
 local i 
 origarray=($(echo "$@")) 
 newarray=($(echo "$@")) 
 elements=$[ $# - 1 ] 
 for (( i = 0; i <= $elements; i++ )) 
 { 
 newarray[$i]=$[ ${origarray[$i]} * 2 ] 
 } 
 echo ${newarray[*]} 
} 
myarray=(1 2 3 4 5) 
echo "The original array is: ${myarray[*]}" 
arg1=$(echo ${myarray[*]}) 
result=($(arraydblr $arg1)) 
echo "The new array is: ${result[*]}" 
$ 
$ ./test12 
The original array is: 1 2 3 4 5 
The new array is: 2 4 6 8 10

5. 函数递归

阶乘函数用它自己来计算阶乘的值:

$ cat test13 
#!/bin/bash 
# using recursion 
function factorial { 
 if [ $1 -eq 1 ] 
 then 
 echo 1 
 else 
 local temp=$[ $1 - 1 ] 
 local result=$(factorial $temp) 
 echo $[ $result * $1 ] 
 fi
} 
 
read -p "Enter value: " value 
result=$(factorial $value) 
echo "The factorial of $value is: $result" 
$ 
$ ./test13 
Enter value: 5 
The factorial of 5 is: 120 
$ 

6. 创建库

使用函数库的关键在于source命令。source命令会在当前shell上下文中执行命令,而不是创建一个新shell。可以用source命令来在shell脚本中运行库文件脚本。这样脚本就可以使用库中的函数了。
source命令有个快捷的别名,称作点操作符(dot operator)。要在shell脚本中运行myfuncs库文件,只需添加下面这行:

. ./myfuncs 
$ cat test14 
#!/bin/bash 
# using functions defined in a library file 
. ./myfuncs 
value1=10 
value2=5 
result1=$(addem $value1 $value2) 
result2=$(multem $value1 $value2) 
result3=$(divem $value1 $value2) 
echo "The result of adding them is: $result1" 
echo "The result of multiplying them is: $result2" 
echo "The result of dividing them is: $result3" 
$ 
$ ./test14 
The result of adding them is: 15 
The result of multiplying them is: 50 
The result of dividing them is: 2 
$ 

7. 在命令行上使用函数

为一旦在shell中定义了函数,你就可以在整个系统中使用它了,无需担心脚本是不是在PATH环境变量里。重点在于让shell能够识别这些函数。有几种方法可以实现。

7.1 在命令行上创建函数

因为shell会解释用户输入的命令,所以可以在命令行上直接定义一个函数。有两种方法。
一种方法是采用单行方式定义函数。

$ function divem { echo $[ $1 / $2 ]; } 
$ divem 100 5 
20 

另一种方法是采用多行方式来定义函数。在定义时,bash shell会使用次提示符来提示输入更多命令。用这种方法,你不用在每条命令的末尾放一个分号,只要按下回车键就行。

$ function multem { 
> echo $[ $1 * $2 ] 
> } 
$ multem 2 5 
10 
$

7.2 在.bashrc 文件中定义函数

在命令行上直接定义shell函数的明显缺点是退出shell时,函数就消失了。

一个非常简单的方法是将函数定义在一个特定的位置,这个位置在每次启动一个新shell的时候,都会由shell重新载入。

最佳地点就是.bashrc文件。bash shell在每次启动时都会在主目录下查找这个文件,不管是交互式shell还是从现有shell中启动的新shell。

7.2.1 直接定义函数

可以直接在主目录下的.bashrc文件中定义函数。许多Linux发行版已经在.bashrc文件中定义了一些东西,所以注意不要误删了。把你写的函数放在文件末尾就行了。

7.2.2 读取函数文件

只要是在shell脚本中,都可以用source命令(或者它的别名点操作符)将库文件中的函数添加到你的.bashrc脚本中。

$ cat .bashrc 
# .bashrc 
# Source global definitions 
if [ -r /etc/bashrc ]; then 
 . /etc/bashrc 
fi 
. /home/rich/libraries/myfuncs 
$ 

第十八章、 初识sed和gawk

1. 文本处理

1.1 sed 编辑器

sed编辑器被称作流编辑器(stream editor),和普通的交互式文本编辑器恰好相反。

sed编辑器可以根据命令来处理数据流中的数据,这些命令要么从命令行中输入,要么存储在一个命令文本文件中。sed编辑器会执行下列操作。
(1) 一次从输入中读取一行数据。
(2) 根据所提供的编辑器命令匹配数据。
(3) 按照命令修改流中的数据。
(4) 将新的数据输出到STDOUT。
sed命令的格式如下。

sed options script file
sed命令选项
选 项 描 述
-e script 在处理输入时,将script中指定的命令添加到已有的命令中
-f file 在 处理输入时,将file中指定的命令添加到已有的命令中
-n 不产生命令输出,使用print命令来完成输出

1.2 gawk 程序

gawk程序的基本格式如下:

gawk options program file
gawk选项
选 项 描 述
F fs 指定行中划分数据字段的字段分隔符
-f file 从指定的文件中读取程序
-v var=value 定义gawk程序中的一个变量及其默认值
-mf N 指定要处理的数据文件中的最大字段数
-mr N 指定数据文件中的最大数据行数
-W keyword 指定gawk的兼容模式或警告等级

2. sed 编辑器基础

2.1 更多的替换选项

有4种可用的替换标记:
 数字,表明新文本将替换第几处模式匹配的地方;
 g,表明新文本将会替换所有匹配的文本;
 p,表明原先行的内容要打印出来;
 w file,将替换的结果写到文件中。

2.2 使用地址

在sed编辑器中有两种形式的行寻址:
 以数字形式表示行区间
 用文本模式来过滤出行

2.3 删除行

删除命令d名副其实,它会删除匹配指定寻址模式的所有行。

$ sed '2,3d' data6.txt 
This is line number 1. 
This is line number 4. 
$

2.4 插入和附加文本

sed编辑器允许向数据流插入和附加文本行。两个操作的区别可能比较让人费解:
 插入(insert)命令(i)会在指定行前增加一个新行;
 附加(append)命令(a)会在指定行后增加一个新行。

sed '[address]command\ 
new line' 


$ echo "Test Line 2" | sed 'i\Test Line 1' 
Test Line 1 
Test Line 2


#将一个新行附加到数据流中第三行后。
$ sed '3a\ 
> This is an appended line.' data6.txt

new line中的文本将会出现在sed编辑器输出中你指定的位置。

2.5 修改行

修改(change)命令允许修改数据流中整行文本的内容。

$ sed '3c\ 
> This is a changed line of text.' data6.txt 
This is line number 1. 
This is line number 2. 
This is a changed line of text. 
This is line number 4. 

2.6 转换命令

转换(transform)命令(y)是唯一可以处理单个字符的sed编辑器命令。转换命令格式
如下。

[address]y/inchars/outchars/


$ echo "This 1 is a test of 1 try." | sed 'y/123/456/' 
This 4 is a test of 4 try. 

2.7 回顾打印

有3个命令也能用来打印数据流中的信息:
 p命令用来打印文本行;
 等号(=)命令用来打印行号;
 l(小写的L)命令用来列出行。

$ echo "this is a test" | sed 'p' 
this is a test 
this is a test

等号命令会打印行在数据流中的当前行号。行号由数据流中的换行符决定。

$ sed '=' data1.txt 
1 
The quick brown fox jumps over the lazy dog. 
2 
The quick brown fox jumps over the lazy dog. 
3 
The quick brown fox jumps over the lazy dog. 
4 
The quick brown fox jumps over the lazy dog. 
$

2.8 使用 sed 处理文件

w命令用来向文件写入行。该命令的格式如下:

[address]w filename

#事例
$ sed '1,2w test.txt' data6.txt 
This is line number 1. 
This is line number 2. 
This is line number 3. 
This is line number 4. 
$ 
$ cat test.txt 
This is line number 1. 
This is line number 2.

读取(read)命令(r)允许你将一个独立文件中的数据插入到数据流中。

[address]r filename

#事例
$ cat data12.txt 
This is an added line. 
This is the second added line. 
$ 
$ sed '3r data12.txt' data6.txt 
This is line number 1. 
This is line number 2. 
This is line number 3. 
This is an added line. 
This is the second added line. 
This is line number 4. 

第十九章、 正则表达式

在Linux中,有两种流行的正则表达式引擎:
 POSIX基础正则表达式(basic regular expression,BRE)引擎
 POSIX扩展正则表达式(extended regular expression,ERE)引擎

sed编辑器基本符合BRE引擎,而gawk程序则使用了ERE引擎中的大多数特性。

2. 定义 BRE 模式

最基本的BRE模式是匹配数据流中的文本字符。

2.1 纯文本

$ echo "This is a test" | sed -n '/test/p' 
This is a test 
$ echo "This is a test" | sed -n '/trial/p' 
$

2.2 特殊字符

正则表达式识别的特殊字符包括:

.*[]^${}\+?|() 

如果要用某个特殊字符作为文本字符,就必须转义。这个特殊字符就是反斜线(\)

2.3 锚字符

2.3.1 锁定在行首

脱字符(^)定义从数据流中文本行的行首开始的模式。要用脱字符,就必须将它放在正则表达式中指定的模式前面。

$ echo "The book store" | sed -n '/^book/p' 
$ 
$ echo "Books are great" | sed -n '/^Book/p' 
Books are great 
$ 

2.3.2 锁定在行尾

跟在行首查找模式相反的就是在行尾查找。特殊字符美元符($)定义了行尾锚点。

$ echo "This is a good book" | sed -n '/book$/p' 
This is a good book 
$ echo "This book is good" | sed -n '/book$/p' 
$ 

2.4 点号字符

特殊字符点号用来匹配除换行符之外的任意单个字符。它必须匹配一个字符,如果在点号字符的位置没有字符,那么模式就不成立。

$ cat data6 
This is a test of a line. 
The cat is sleeping. 
That is a very nice hat. 
This test is at line four. 
at ten o'clock we'll go home. 
$ sed -n '/.at/p' data6 
The cat is sleeping. 
That is a very nice hat. 
This test is at line four. 
$ 

2.5 字符组

使用方括号来定义一个字符组。方括号中包含所有你希望出现在该字符组中的字符。

$ sed -n '/[ch]at/p' data6 
The cat is sleeping. 
That is a very nice hat. 
$ 

2.6 排除型字符组

只要在字符组的开头加个脱字符。

$ sed -n '/[^ch]at/p' data6 
This test is at line four. 
$

2.7 区间

$ sed -n '/[c-h]at/p' data6 
The cat is sleeping. 
That is a very nice hat. 

2.8 特殊的字符组

BRE特殊字符组
描 述
[[:alpha:]] 匹配任意字母字符,不管是大写还是小写
[[:alnum:]] 匹配任意字母数字字符0~9、A~Z或a~z
[[:blank:]] 匹配空格或制表符
[[:digit:]] 匹配0~9之间的数字
[[:lower:]] 匹配小写字母字符a~z
[[:print:]] 匹配任意可打印字符
[[:punct:]] 匹配标点符号
[[:space:]] 匹配任意空白字符:空格、制表符、NL、FF、VT和CR
[[:upper:]] 匹配任意大写字母字符A~Z

2.9 星号

放置星号表明该字符必须在匹配模式的文本中出现0次或多次。

3. 扩展正则表达式

3.1 问号

问号类似于星号,不过有点细微的不同。问号表明前面的字符可以出现0次或1次

3.2 加号

加号表明前面的字符可以出现1次或多次,但必须至少出现1次。如果该字符没有出现,那么模式就不会匹配。

$ echo "beeet" | gawk '/be+t/{print $0}' 
beeet 
$ echo "beet" | gawk '/be+t/{print $0}' 
beet 
$ echo "bet" | gawk '/be+t/{print $0}' 
bet 
$ echo "bt" | gawk '/be+t/{print $0}' 
$ 

3.3 使用花括号

ERE中的花括号允许你为可重复的正则表达式指定一个上限。这通常称为间隔(interval)。可以用两种格式来指定区间。
 m:正则表达式准确出现m次。
 m, n:正则表达式至少出现m次,至多n次。

$ echo "bet" | gawk --re-interval '/be{1}t/{print $0}' 
bet 
$ echo "beet" | gawk --re-interval '/be{1}t/{print $0}' 
$

3.4 管道符号

管道符号允许你在检查数据流时,用逻辑OR方式指定正则表达式引擎要用的两个或多个模式。如果任何一个模式匹配了数据流文本,文本就通过测试。如果没有模式匹配,则数据流文本匹配失败。

expr1|expr2|... 

3.5 表达式分组

正则表达式模式也可以用圆括号进行分组。当你将正则表达式模式分组时,该组会被视为一个标准字符。

$ echo "Sat" | gawk '/Sat(urday)?/{print $0}' 
Sat 
$ echo "Saturday" | gawk '/Sat(urday)?/{print $0}' 
Saturday

第二十章、 sed进阶

1. 多行命令

sed编辑器包含了三个可用来处理多行文本的特殊命令。
 N:将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
 D:删除多行组中的一行。
 P:打印多行组中的一行。

1.1 next 命令

$ cat data4.txt 
On Tuesday, the Linux System 
Administrator's group meeting will be held. 
All System Administrators should attend. 
$ 
$ sed 'N 
> s/System\nAdministrator/Desktop\nUser/ 
> s/System Administrator/Desktop User/ 
> ' data4.txt 
On Tuesday, the Linux Desktop 
User's group meeting will be held. 
All System Administrators should attend. 
$ 

1.2 多行删除命令

单行删除命令(d),sed编辑器用它来删除模式空间中的当前行。

$ sed 'N ; /System\nAdministrator/d' data4.txt 
All System Administrators should attend. 
$ 

删除命令会在不同的行中查找单词System和Administrator,然后在模式空间中将两行都删掉。

1.3 多行打印命令

多行打印命令(P)沿用了同样的方法。它只打印多行模式空间中的第一行。这包括模式空间中直到换行符为止的所有字符。

用-n选项来阻止脚本输出时,它和显示文本的单行p命令的用法大同小异。

$ sed -n 'N ; /System\nAdministrator/P' data3.txt
On Tuesday, the Linux System 

当多行匹配出现时,P命令只会打印模式空间中的第一行。多行P命令的强大之处在和N命令及D命令组合使用时才能显现出来。

2. 保持空间

模式空间(pattern space)是一块活跃的缓冲区,在sed编辑器执行命令时它会保存待检查的文本。

sed编辑器的保持空间命令
命 令 描 述
h 将模式空间复制到保持空间
H 将模式空间附加到保持空间
g 将保持空间复制到模式空间
G 将保持空间附加到模式空间
x 交换模式空间和保持空间的内容
$ cat data2.txt 
This is the header line. 
This is the first data line. 
This is the second data line. 
This is the last line. 
$ 
$ sed -n '/first/ {h ; p ; n ; p ; g ; p }' data2.txt

(1) sed脚本在地址中用正则表达式来过滤出含有单词first的行;
(2) 当含有单词first的行出现时,h命令将该行放到保持空间;
(3) p命令打印模式空间也就是第一个数据行的内容;
(4) n命令提取数据流中的下一行(This is the second data line),并将它放到模式
空间;
(5) p命令打印模式空间的内容,现在是第二个数据行;
(6) g命令将保持空间的内容(This is the first data line)放回模式空间,替换当
前文本;
(7) p命令打印模式空间的当前内容,现在变回第一个数据行了。

3. 排除命令

感叹号命令(!)用来排除(negate)命令,也就是让原本会起作用的命令不起作用。

$ sed -n '/header/!p' data2.txt 
This is the first data line. 
This is the second data line. 
This is the last line. 

4. 改变流

4.1 分支

分支(branch)命令b的格式如下:

[address]b [label] 

#事例
$ cat data2.txt 
This is the header line. 
This is the first data line. 
This is the second data line. 
This is the last line. 
$ 
$ sed '{2,3b ; s/This is/Is this/ ; s/line./test?/}' data2.txt 
Is this the header test? 
This is the first data line. 
This is the second data line. 
Is this the last test? 
$ 

分支命令在数据流中的第2行和第3行处跳过了两个替换命令。

4.2 测试

测试(test)命令(t)也可以用来改变sed编辑器脚本的执行流程。
测试命令使用与分支命令相同的格式。

[address]t [label] 


#事例
$ sed '{ 
> s/first/matched/ 
> t 
> s/This is the/No match on/ 
> }' data2.txt 
No match on header line 
This is the matched data line 
No match on second data line 
No match on last line 
$ 

第一个替换命令会查找模式文本first。如果匹配了行中的模式,它就会替换文本,而且测试命令会跳过后面的替换命令。如果第一个替换命令未能匹配模式,第二个替换命令就会被执行。

5. 模式替代

$ echo "The cat sleeps in his hat." | sed 's/cat/"cat"/' 
The "cat" sleeps in his hat. 

5.1 &符号

&符号可以用来代表替换命令中的匹配的模式。

$ echo "The cat sleeps in his hat." | sed 's/.at/"&"/g' 
The "cat" sleeps in his "hat". 

5.2 替代单独的单词

sed编辑器用圆括号来定义替换模式中的子模式。sed编辑器会给第一个子模式分配字符\1,给第二个子模式分配字符\2,依此类推。

$ echo "The System Administrator manual" | sed ' 
> s/\(System\) Administrator/\1 User/' 
The System User manual 

6. 创建 sed 实用工具

6.1 加倍行间距

$ sed 'G' data2.txt 

可以用排除符号(!)和尾行符号($)来确
保脚本不会将空白行加到数据流的最后一行后面。

$ sed '$!G' data2.txt 
This is the header line. 
This is the first data line. 
This is the second data line. 
This is the last line. 

6.2 对可能含有空白行的文件加倍行间距

要删除已有的空白行,需要将d命令和一个匹配空白行的模式一起使用

$ cat data6.txt 
This is line one. 
This is line two. 

This is line three. 
This is line four. 
$ 
$ sed '/^$/d ; $!G' data6.txt 
This is line one. 
This is line two. 
This is line three. 
This is line four. 

6.3 给文件中的行编号

$ sed '=' data2.txt

6.4 打印末尾行

$ sed -n '$p' data2.txt 

6.5 删除行

删除连续的空白行

$ sed '/./,/^$/!d' data8.txt

区间是/./到/^$/。区间的开始地址会匹配任何含有至少一个字符的行。区间的结束地址会匹配一个空行。在这个区间内的行不会被删除。

删除开头的空白行

$ sed '/./,$!d' data9.txt

删除结尾的空白行

$ sed '{ 
> :start 
> /^\n*$/{$d ; N ; b start } 
> }' data10.txt

第二十一章、 gawk进阶

1. 使用变量

gawk编程语言支持两种不同类型的变量:
 内建变量
 自定义变量

1.1 内建变量

gawk数据字段和记录变量
变 量 描 述
FIELDWIDTHS 由空格分隔的一列数字,定义了每个数据字段确切宽度
FS 输入字段分隔符
RS 输入记录分隔符
OFS 输出字段分隔符
ORS 输出记录分隔符
$ cat data1 
data11,data12,data13,data14,data15 
data21,data22,data23,data24,data25 
data31,data32,data33,data34,data35 
$ gawk 'BEGIN{FS=","; OFS="-"} {print $1,$2,$3}' data1 
data11-data12-data13 
data21-data22-data23 
data31-data32-data33 

数据变量

更多的gawk内建变量
变 量 描 述
ARGC 当前命令行参数个数
ARGIND 当前文件在ARGV中的位置
ARGV 包含命令行参数的数组
CONVFMT 数字的转换格式(参见printf语句),默认值为%.6 g
ENVIRON 当前shell环境变量及其值组成的关联数组
ERRNO 当读取或关闭输入文件发生错误时的系统错误号
FILENAME 用作gawk输入数据的数据文件的文件名
FNR 当前数据文件中的数据行数
IGNORECASE 设成非零值时,忽略gawk命令中出现的字符串的字符大小写
NF 数据文件中的字段总数
NR 已处理的输入记录数
OFMT 数字的输出格式,默认值为%.6 g
RLENGTH 由match函数所匹配的子字符串的长度
RSTART 由match函数所匹配的子字符串的起始位置
$ gawk ' 
> BEGIN {FS=","} 
> {print $1,"FNR="FNR,"NR="NR} 
> END{print "There were",NR,"records processed"}' data1 data1 
data11 FNR=1 NR=1 
data21 FNR=2 NR=2 
data31 FNR=3 NR=3 
data11 FNR=1 NR=4 
data21 FNR=2 NR=5 
data31 FNR=3 NR=6 
There were 6 records processed

FNR变量的值在gawk处理第二个数据文件时被重置了,而NR变量则在处理第二个数据文件时继续计数。

1.2 自定义变量

1.2.1 在脚本中给变量赋值

在gawk程序中给变量赋值跟在shell脚本中赋值类似,都用赋值语句。

$ gawk ' 
> BEGIN{ 
> testing="This is a test" 
> print testing 
> }' 
This is a test

1.2.2 在命令行上给变量赋值

$ cat script1 
BEGIN{FS=","} 
{print $n} 
$ gawk -f script1 n=2 data1 
data12 
data22 
data32

2. 处理数组

2.1 定义数组变量

var[index] = element

其中var是变量名,index是关联数组的索引值,element是数据元素值。

$ gawk 'BEGIN{ 
> capital["Illinois"] = "Springfield" 
> print capital["Illinois"] 
> }' 
Springfield

2.2 遍历数组变量

用for语句的一种特殊形式。

for (var in array) 
{ 
 statements 
} 


#事例
$ gawk 'BEGIN{ 
> var["a"] = 1 
> var["g"] = 2 
> var["m"] = 3 
> var["u"] = 4 
> for (test in var) 
> { 
> print "Index:",test," - Value:",var[test] 
> } 
> }' 
Index: u - Value: 4 
Index: m - Value: 3 
Index: a - Value: 1 
Index: g - Value: 2 
$

2.3 删除数组变量

delete array[index] 

#事例
$ gawk 'BEGIN{ 
> var["a"] = 1 
> var["g"] = 2 
> for (test in var) 
> { 
> print "Index:",test," - Value:",var[test] 
> } 
> delete var["g"] 
> print "---" 
> for (test in var) 
> print "Index:",test," - Value:",var[test] 
> }' 
Index: a - Value: 1 
Index: g - Value: 2 
--- 
Index: a - Value: 1

3. 使用模式

3.1 正则表达式

可以用基础正则表达式(BRE)或扩展正则表达式(ERE)来选择程序脚本作用在数据流中的哪些行上。

$ gawk 'BEGIN{FS=","} /11/{print $1}' data1 
data11

正则表达式/11/匹配了数据字段中含有字符串11的记录

3.2 匹配操作符

匹配操作符(matching operator)允许将正则表达式限定在记录中的特定数据字段。匹配操作符是波浪线(~)。

$1 ~ /^data/


#事例
$ gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1 
data21,data22,data23,data24,data25 

匹配操作符会用正则表达式/^data2/来比较第二个数据字段,该正则表达式指明字符串要以文本data2开头。

3.3 数学表达式

可以使用任何常见的数学比较表达式。
 x == y:值x等于y。
 x <= y:值x小于等于y。
 x < y:值x小于y。
 x >= y:值x大于等于y。
 x > y:值x大于y。
也可以对文本数据使用表达式

$ gawk -F, '$1 == "data11"{print $1}' data1 
data11

4. 结构化命令

4.1 if 语句

如果条件求值为FALSE,这条语句就会被跳过。可以用这种格式:

if (condition) 
 statement1 


#事例
$ cat data4 
10 
5 
13 
50 
34 
$ gawk '{if ($1 > 20) print $1}' data4 
50 
34 

4.2 while 语句

while (condition) 
{ 
 statements 
} 

#事例
$ cat data5 
130 120 135 
160 113 140 
145 170 215 
$ gawk '{ 
> total = 0 
> i = 1 
> while (i < 4) 
> { 
> total += $i 
> i++ 
> } 
> avg = total / 3 
> print "Average:",avg 
> }' data5 
Average: 128.333 
Average: 137.667 
Average: 176.667 
$

4.3 do-while 语句

do-while语句类似于while语句,但会在检查条件语句之前执行命令。

do 
{ 
 statements 
} while (condition) 

4.4 for 语句

for( variable assignment; condition; iteration process)

5. 格式化打印

printf命令的格式:

printf "format string", var1, var2 . . . 

format string是格式化输出的关键。它会用文本元素和格式化指定符来具体指定如何呈现格式化输出。

格式化指定符的控制字母
控制字母 描 述
c 将一个数作为ASCII字符显示
d 显示一个整数值
i 显示一个整数值(跟d一样)
e 用科学计数法显示一个数
f 显示一个浮点值
g 用科学计数法或浮点数显示(选择较短的格式)
o 显示一个八进制值
s 显示一个文本字符串
x 显示一个十六进制值
X 显示一个十六进制值,但用大写字母A~F

除了控制字母外,还有3种修饰符可以用来进一步控制输出。
 width:指定了输出字段最小宽度的数字值。如果输出短于这个值,printf会将文本右对齐,并用空格进行填充。如果输出比指定的宽度还要长,则按照实际的长度输出。
 prec:这是一个数字值,指定了浮点数中小数点后面位数,或者文本字符串中显示的最大字符数。
 -(减号):指明在向格式化空间中放入数据时采用左对齐而不是右对齐。

$ gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1, $4}' data2 
Riley Mullen (312)555-1234 
Frank Williams (317)555-9876 
Haley Snell (313)555-4938 
$ 

6. 内建函数

6.1 数学函数

gawk数学函数
函 数 描 述
atan2(x, y) x/y的反正切,x和y以弧度为单位
cos(x) x的余弦,x以弧度为单位
exp(x) x的指数函数
int(x) x的整数部分,取靠近零一侧的值
log(x) x的自然对数
rand( ) 比0大比1小的随机浮点值
sin(x) x的正弦,x以弧度为单位
sqrt(x) x的平方根
srand(x) 为计算随机数指定一个种子值

gawk还支持一些按位操作数据的函数。
 and(v1, v2):执行值v1和v2的按位与运算。
 compl(val):执行val的补运算。
 lshift(val, count):将值val左移count位。
 or(v1, v2):执行值v1和v2的按位或运算。
 rshift(val, count):将值val右移count位。
 xor(v1, v2):执行值v1和v2的按位异或运算。

6.2 字符串函数

gawk字符串函数
函 数 描 述
asort(s [,d]) 将数组s按数据元素值排序。索引值会被替换成表示新的排序顺序的连续数字。另外,如果指定了d,则排序后的数组会存储在数组d中
asorti(s [,d]) 将数组s按索引值排序。生成的数组会将索引值作为数据元素值,用连续数字索引来表明排序顺序。另外如果指定了d,排序后的数组会存储在数组d中
gensub(r, s, h [, t]) 查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果h是一个以g或G开头的字符串,就用s替换掉匹配的文本。如果h是一个数字,它表示要替换掉第h处r匹配的地方
gsub(r, s [,t]) 查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果找到了,就全部替换成字符串s
index(s, t) 返回字符串t在字符串s中的索引值,如果没找到的话返回0
length([s]) 返回字符串s的长度;如果没有指定的话,返回$0的长度
match(s, r [,a]) 返回字符串s中正则表达式r出现位置的索引。如果指定了数组a,它会存储s中匹配正则表达式的那部分
split(s, a [,r]) 将s用FS字符或正则表达式r(如果指定了的话)分开放到数组a中。返回字段的总数
sprintf(format, variables) 用提供的format和variables返回一个类似于printf输出的字符串
sub(r, s [,t]) 在变量$0或目标字符串t中查找正则表达式r的匹配。如果找到了,就用字符串s替换掉第一处匹配
substr(s, i [,n]) 返回s中从索引值i开始的n个字符组成的子字符串。如果未提供n,则返回s剩下的部分
tolower(s) 将s中的所有字符转换成小写
toupper(s) 将s中的所有字符转换成大写
$ gawk 'BEGIN{x = "testing"; print toupper(x); print length(x) }' 
TESTING 
7 

6.3 时间函数

gawk的时间函数
函 数 描 述
mktime(datespec) 将一个按YYYY MM DD HH MM SS [DST]格式指定的日期转换成时间戳值①
strftime(format [,timestamp]) 将当前时间的时间戳或timestamp(如果提供了的话)转化格式化日期(采用shell函数date()的格式)
systime( ) 返回当前时间的时间戳
$ gawk 'BEGIN{ 
> date = systime() 
> day = strftime("%A, %B %d, %Y", date) 
> print day 
> }' 
Friday, December 26, 2014 
$ 

7. 自定义函数

7.1 定义函数

function name([variables]) 
{ 
 statements 
} 

函数名必须能够唯一标识函数。可以在调用的gawk程序中传给这个函数一个或多个变量。

function myrand(limit) 
{ 
 return int(limit * rand()) 
} 

7.2 使用自定义函数

它必须出现在所有代码块之前(包括BEGIN代码块)。

$ gawk ' 
> function myprint() 
> { 
> printf "%-16s - %s\n", $1, $4 
> } 
> BEGIN{FS="\n"; RS=""} 
> { 
> myprint() 
> }' data2 
Riley Mullen - (312)555-1234 
Frank Williams - (317)555-9876 
Haley Snell - (313)555-4938 
$

7.3 创建函数库

$ cat funclib 
function myprint() 
{ 
 printf "%-16s - %s\n", $1, $4 
} 
function myrand(limit) 
{ 
 return int(limit * rand()) 
} 
function printthird() 
{ 
 print $3 
} 
$ 

funclib文件含有三个函数定义。需要使用-f命令行参数来使用它们。可以在同一个命令行中使用多个-f参数。

$ cat script4 
BEGIN{ FS="\n"; RS=""} 
{ 
 myprint() 
} 
$ gawk -f funclib -f script4 data2 
Riley Mullen - (312)555-1234 
Frank Williams - (317)555-9876 
Haley Snell - (313)555-4938 

你可能感兴趣的:(linux)