本文章内容来自与“的Linux的命令行与外壳脚本编程大全。第3版”

目录

Linux命令. 1

进程相关. 5

Ps命令. 5

TOP命令. 6

Kill命令. 6

Type(查看命令的命令,which). 7

History历史命令. 7

监测磁盘. 7

Mount挂载. 7

Umount卸载. 8

Df命令. 8

Du命令. 8

文件内容处理命令. 8

Sort排序. 8

grep搜索. 9

Tar归档(压缩和解压). 9

wc命令. 9

系统命令. 10

Alias命名其他命令别名. 10

环境变量相关命令. 10

Export导出环境变量. 10

Unset删除环境变量. 10

系统默认变量(例UID). 10

系统用户相关. 12

useradd添加用户. 12

userdel删除用户. 13

chpasswd修改用户密码. 13

文件权限. 13

Linux文件权限码. 13

默认文件权限. 13

改变权限chmod 14

Readlink命令. 14

软件包管理. 15

基于Red Hat 的系统. 15

Yum安装软件包. 15

yum更新软件包. 15

yum卸载软件包. 16

处理损坏的包依赖关系. 16

Yum仓库. 16

vim编辑器命令. 16

编辑命令. 16

查找和替换. 17

Shell脚本常用命令. 17

变量赋值. 17

变量子串的常用操作(提取。。。). 18

变量替换表(空值给默认值). 18

Echo命令. 19

数学运算命令. 19

Expr命令. 19

方括号计算. 20

bc浮点数计算. 20

Linux退出状态码. 21

If语句. 22

数值比较. 22

字符串比较. 23

文件比较. 23

if语句的其他使用. 23

双括号. 23

双方括号. 24

Case语句. 24

for循环语句. 25

更改字段分隔符. 25

C 语言风格的for 命令. 27

while循环语句. 28

使用多个测试命令. 28

until 命令. 29

循环处理文件数据. 30

控制循环. 31

break 命令. 31

continue 命令. 31

处理循环的输出. 31

处理用户输入($0,$1-$9). 32

读取脚本名($0)basename用法. 32

参数统计($#). 33

抓取所有的数据($*和$@) 34

移动变量(shift). 34

处理选项. 36

选项标准化. 41

获得用户输入read 42

read超时. 42

限制read输入字符. 43

read从文件中读取. 44

呈现数据. 44

理解输入和输出. 44

重定向. 45

文件描述符. 45

使用和恢复文件描述符. 45

关闭文件描述符. 46

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

创建临时文件和文件夹. 47

信号. 49

捕获信号. 49

捕获脚本退出. 50

修改或移除捕获. 51

删除捕获. 52

nohup命令(脱离会话终端) 53

查看作业号(jobs) 53

重启被暂停的作业. 53

调整谦让度. 54

nice 命令. 54

renice命令. 54

定时任务. 55

构建cron时间表. 55

函数. 56

return 56

使用函数输出. 56

在函数中处理变量. 57

全局变量. 57

局部变量. 58

向函数传数组参数. 58

创建函数库和调用. 59

在命令行上创建函数. 61

.bashrc文件. 61

函数库实例. 61

菜单. 62

创建菜单布局. 62

sed和gawk 63

sed基本用法. 63

sed从文件中读取编辑器命令. 63

gawk基本用法. 64

取一行中的某个字段. 64

指定字段分隔符. 65

echo和gawk合用. 65

gawk从文件中读取编辑器命令. 65

在处理数据前执行gawk脚本(比如为报告创建标题). 66

实例. 66

一,    创建多个用户(使用.csv文件). 66

二,    查找可执行文件. 67

进程相关

Ps命令

参数

描述

-e

显示所有进程

-U userlist

显示属主的用户ID在userlist列表中的进程

-F

显示更多额外输出(相对-f参数而言)

-l

显示长列表

基本输出显示列

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

加-l增加列

  • F:内核分配给进程的系统标记。
  • S:进程的状态(O代表正在运行;S代表在休眠;R代表可运行,正等待运行;Z代表僵化,进程已结束但父进程已不存在;T代表停止)。
  • PRI:进程的优先级(越大的数字代表越低的优先级)。
  • NI:谦让度值用来参与决定优先级。
  • ADDR:进程的内存地址。
  • SZ:假如进程被换出,所需交换空间的大致大小。
  • WCHAN:进程休眠的内核函数的地址。

TOP命令

第一行显示了当前时间、系统的运行时间、登录的用

户数以及系统的平均负载。

平均负载有3个值:最近1分钟的、最近5分钟的和最近15分钟的平均负载。值越大说明系统

的负载越高。由于进程短期的突发性活动,出现最近1分钟的高负载值也很常见,但如果近15分

钟内的平均负载都很高,就说明系统可能有问题。通常,如果系统的负载值超过了2,就说明系统比较繁忙了。

第二行显示了进程概要信息——top命令的输出中将进程叫作任务(task):有多少进程处在

运行、休眠、停止或是僵化状态(僵化状态是指进程完成了,但父进程没有响应)。

下一行显示了CPU的概要信息。top根据进程的属主(用户还是系统)和进程的状态(运行、

空闲还是等待)将CPU利用率分成几类输出。

最后一部分显示了当前运行中的进程的详细列表,有些列跟ps命令的输出类似。

  • PID:进程的ID。
  • USER:进程属主的名字。
  • PR:进程的优先级。
  • NI:进程的谦让度值。
  • VIRT:进程占用的虚拟内存总量。
  • RES:进程占用的物理内存总量。
  • SHR:进程和其他进程共享的内存总量。
  • S:进程的状态(D代表可中断的休眠状态,R代表在运行状态,S代表休眠状态,T代表跟踪状态或停止状态,Z代表僵化状态)。
  • %CPU:进程使用的CPU时间比例。
  • %MEM:进程使用的内存占可用内存的比例。
  • TIME+:自进程启动到目前为止的CPU时间总量。
  • COMMAND:进程所对应的命令行名称,也就是启动的程序名。

键入f允许你选择对输出进行排序的字段,键入d允许你修改轮询间隔。键入q可以退出top。

Kill命令

Kill命令可以终结进程

使用方法:

Kill PID

还可以加-s 参数指定其他信号。

Killall它支持通过进程名,也支持通配符

使用方法:

Killall http*

会终结所有以http开头的进程

Type(查看命令的命令,which)

# type cd

cd is a shell builtin

#

# type exit

exit is a shell builtin

要注意,有些命令有多种实现。例如echo和pwd既有内建命令也有外部命令。两种实现略有

不同。要查看命令的不同实现,使用type命令的-a选项。

$ type -a echo

echo is a shell builtin

echo is /bin/echo

$

$ which echo

/bin/echo

$

$ type -a pwd

pwd is a shell builtin

pwd is /bin/pwd

$

$ which pwd

/bin/pwd

$

命令type -a显示出了每个命令的两种实现。注意,which命令只显示出了外部命令文件。

History历史命令

想强制重新读取.bash_history文件,更新终端会话的历史记录,可以使用history -n命令

监测磁盘

Mount挂载

默认情况下,mount命令会输出当前系统上挂载的设备列表。

mount命令提供如下四部分信息:

  • 媒体的设备文件名
  • 媒体挂载到虚拟目录的挂载点
  • 文件系统类型
  • 已挂载媒体的访问状态

手动挂载媒体设备的基本命令:

mount -t type device directory

type参数指定了磁盘被格式化的文件系统类型。

  • vfat:Windows长文件系统。
  • ntfs:Windows NT、XP、Vista以及Windows 7中广泛使用的高级文件系统。
  • iso9660:标准CD-ROM文件系统

参数

描述

-f

使mount命令模拟挂载设备,但并不真的挂载

Umount卸载

umount命令支持通过设备文件或者是挂载点来指定要卸载的设备。如果有任何程序正在使

用设备上的文件,系统就不会允许你卸载它

Df命令

df命令会显示每个有数据的已挂载文件系统。如你在前例中看到的,有些已挂载设备仅限系

统内部使用。命令输出如下:

  • 设备的设备文件位置;
  • 能容纳多少个1024字节大小的块;
  • 已用了多少个1024字节大小的块;
  • 还有多少个1024字节大小的块可用;
  • 已用空间所占的比例;
  • 设备挂载到了哪个挂载点上。

参数

描述

-h

会把输出中的磁盘空间按照用户易读的形式显示,通常用M来替代兆字节,用G替代吉字节

Du命令

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

参数

描述

-h

会把输出中的磁盘空间按照用户易读的形式显示,通常用M来替代兆字节,用G替代吉字节

-c

显示所有已列出文件总的大小。

文件内容处理命令

Sort排序

Sort不加参数排序会把所有当作字符串排序。

参数

描述

-n

按字符串数值来排序(并不转换为浮点数)

-M

按月排序, sort命令就能识别三字符的月份名,并相应地排序。

-b

排序时忽略起始的空白

-r

反序排序(升序变成降序)

-z

用NULL字符作为行尾,而不是用换行符

-d

仅考虑空白和字母,不考虑特殊字符

-t

指定一个用来区分键位置的字符

-k POS1[,POS2]

排序从POS1位置开始;如果指定了POS2的话,到POS2位置结

-k和-t参数在对按字段分隔的数据进行排序时非常有用,

sort -t ':' -k 3 -n /etc/passwd

du -sh * | sort –nr

grep搜索

参数

描述

-v

反向搜索,(输出不匹配该模式的行)

-n

显示匹配模式的行所在的行号

-c

要知道有多少行含有匹配的模式

-e

可以使用正则表达式进行匹配搜索

Tar归档(压缩和解压)

参数

描述

-c

创建一个新的归档文件

-A

将一个已有tar归档文件追加到另一个已有tar归档文件,嵌套tar文件

-r

追加文件到已有tar归档文件末尾

-x

从已有tar归档文件中提取文件

-f

输出结果到文件或设备file,

-v

在处理文件时显示文件

wc命令

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

  • 文本的行数
  • 文本的词数
  • 文本的字节数

Wc –l 只取行数

把两个单列文件合并成一个2列的文件

方法1、paste -d "\t" eng.txt chi.txt 
semicolon       分号
comma   逗号
delimiter       定界符
spacebar        空格键
hyphen  连字符号
single quote    单引号
double quote    双引号

方法2、或者使用awk来处理

awk 'NR==FNR{a[i]=$0;i++}NR>FNR{print a[j]" "$0;j++}' eng.txt chi.txt 
semicolon 分号
comma 逗号
delimiter 定界符
spacebar 空格键
hyphen 连字符号
single quote 单引号
double quote 双引号
hash 井号

系统命令

Alias命名其他命令别名

命令别名允许你为常用的命令(及其参数)创建另一个名称

用法:alias li='ls -li'

通过alisa –p查看系统中所有的别名

环境变量相关命令

Export导出环境变量

设置全局环境变量先创建一个局部环境变量,然后再把它导出到全局环境中

这个过程通过export命令来完成,变量名前面不需要加$

例如export my_variable

注意:修改子shell中全局环境变量并不会影响到父shell中该变量的值

Unset删除环境变量

在unset命令中引用环境变量时,记住不要使用$。

例如 Unset my_variable

系统默认变量(例UID)

变 量          描 述

CDPATH        冒号分隔的目录列表,作为cd命令的搜索路径

HOME          当前用户的主目录

IFS           shell用来将文本字符串分割成字段的一系列字符

MAIL          当前用户收件箱的文件名(bash shell会检查这个文件,看看有没有新邮件)

MAILPATH       冒号分隔的当前用户收件箱的文件名列表(bash shell会检查列表中的每个文件,看看有没有新邮件)

OPTARG        getopts命令处理的最后一个选项参数值

OPTIND        getopts命令处理的最后一个选项参数的索引号

PATH          shell查找命令的目录列表,由冒号分隔

PS1           shell命令行界面的主提示符

PS2           shell命令行界面的次提示符

BASH          当前shell实例的全路径名

BASH_ALIASES 含有当前已设置别名的关联数组

BASH_ARGC     含有传入子函数或shell脚本的参数总数的数组变量

BASH_ARCV     含有传入子函数或shell脚本的参数的数组变量

BASH_CMDS     关联数组,包含shell执行过的命令的所在位置

BASH_COMMAND shell正在执行的命令或马上就执行的命令

BASH_ENV      设置了的话,每个bash脚本会在运行前先尝试运行该变量定义的启动文件

BASH_EXECUTION_STRING 使用bash -c选项传递过来的命令

BASH_LINENO   含有当前执行的shell函数的源代码行号的数组变量

BASH_REMATCH 只读数组,在使用正则表达式的比较运算符=~进行肯定匹配(positive match)时,包含了匹配到的模式和子模式

BASH_SOURCE   含有当前正在执行的shell函数所在源文件名的数组变量

BASH_SUBSHELL 当前子shell环境的嵌套级别(初始值是0)

BASH_VERSINFO 含有当前运行的bash shell的主版本号和次版本号的数组变量

BASH_VERSION 当前运行的bash shell的版本号

BASH_XTRACEFD 若设置成了有效的文件描述符(0、1、2),则'set -x'调试选项生成的跟踪输出可被重定向。通常用来将跟踪输出到一个文件中

BASHOPTS 当前启用的bash shell选项的列表

BASHPID 当前bash进程的PID

COLUMNS 当前bash shell实例所用终端的宽度

COMP_CWORD COMP_WORDS变量的索引值,后者含有当前光标的位置

COMP_LINE 当前命令行

COMP_POINT 当前光标位置相对于当前命令起始的索引

COMP_KEY 用来调用shell函数补全功能的最后一个键

COMP_TYPE 一个整数值,表示所尝试的补全类型,用以完成shell函数补全

COMP_WORDBREAKS Readline库中用于单词补全的词分隔字符

COMP_WORDS 含有当前命令行所有单词的数组变量

COMPREPLY 含有由shell函数生成的可能填充代码的数组变量

COPROC 占用未命名的协进程的I/O文件描述符的数组变量

DIRSTACK 含有目录栈当前内容的数组变量

EMACS 设置为't'时,表明emacs shell缓冲区正在工作,而行编辑功能被禁止

ENV 如果设置了该环境变量,在bash shell脚本运行之前会先执行已定义的启动文件(仅

用于当bash shell以POSIX模式被调用时)

EUID 当前用户的有效用户ID(数字形式)

FCEDIT 供fc命令使用的默认编辑器

FIGNORE 在进行文件名补全时可以忽略后缀名列表,由冒号分隔

FUNCNAME 当前执行的shell函数的名称

FUNCNEST 当设置成非零值时,表示所允许的最大函数嵌套级数(一旦超出,当前命令即被终止)

GLOBIGNORE 冒号分隔的模式列表,定义了在进行文件名扩展时可以忽略的一组文件名

GROUPS 含有当前用户属组列表的数组变量

histchars 控制历史记录扩展,最多可有3个字符

HISTCMD 当前命令在历史记录中的编号

HISTCONTROL 控制哪些命令留在历史记录列表中

HISTFILE 保存shell历史记录列表的文件名(默认是.bash_history)

HISTFILESIZE 最多在历史文件中存多少行

HISTTIMEFORMAT 如果设置了且非空,就用作格式化字符串,以显示bash历史中每条命令的时间戳

HISTIGNORE 由冒号分隔的模式列表,用来决定历史文件中哪些命令会被忽略

HISTSIZE 最多在历史文件中存多少条命令

HOSTFILE shell在补全主机名时读取的文件名称

HOSTNAME 当前主机的名称

HOSTTYPE 当前运行bash shell的机器

IGNOREEOF shell在退出前必须收到连续的EOF字符的数量(如果这个值不存在,默认是1)

INPUTRC Readline初始化文件名(默认是.inputrc)

LANG shell的语言环境类别

LC_ALL 定义了一个语言环境类别,能够覆盖LANG变量

LC_COLLATE 设置对字符串排序时用的排序规则

LC_CTYPE 决定如何解释出现在文件名扩展和模式匹配中的字符

LC_MESSAGES 在解释前面带有$的双引号字符串时,该环境变量决定了所采用的语言环境设置

LC_NUMERIC 决定着格式化数字时采用的语言环境设置

LINENO 当前执行的脚本的行号

LINES 定义了终端上可见的行数

MACHTYPE 用“CPU公司系统”(CPU-company-system)格式定义的系统类型

MAPFILE 一个数组变量,当mapfile命令未指定数组变量作为参数时,它存储了mapfile所读入的文本

MAILCHECK shell查看新邮件的频率(以秒为单位,默认值是60)

OLDPWD shell之前的工作目录

OPTERR 设置为1时,bash shell会显示getopts命令产生的错误

OSTYPE 定义了shell所在的操作系统

PIPESTATUS 含有前台进程的退出状态列表的数组变量

POSIXLY_CORRECT 设置了的话,bash会以POSIX模式启动

PPID bash shell父进程的PID

PROMPT_COMMAND 设置了的话,在命令行主提示符显示之前会执行这条命令

PROMPT_DIRTRIM 用来定义当启用了\w或\W提示符字符串转义时显示的尾部目录名的数量。被删除的目录名会用一组英文句点替换

PS3 select命令的提示符

系统用户相关

useradd添加用户

参数

描述

-c

给用户添加新备注

-d

为主目录指定一个名字(如果不想用登录名作为主目录名的话)

-e

用YYYY-MM-DD格式指定一个账户过期的日期

-f

指定这个账户密码过期后多少天这个账户被禁用;0表示密码一过期就立即禁用,1表示禁用这个功能

-r

创建系统账户

-p

为用户账户指定默认密码

-m

创建用户的HOME目录

例如useradd oldboy 添加一个oldboy用户

useradd -D -e 2018-09-10 -f 0 oldboy

userdel删除用户

默认情况下,userdel命令会只删除/etc/passwd文件中的用户信息,而不会删除系统中属于该账户的任何文件。

如果加上-r参数,userdel会删除用户的HOME目录以及邮件目录。然而,系统上仍可能存

有已删除用户的其他文件。这在有些环境中会造成问题。

userdel -r oldboy

chpasswd修改用户密码

chpasswd命令能从标准输入自动读取登录名和密码对(由冒号分割)列表,给密码加密,然后为用户账户设置。你也可以用重定向命令来将含有userid:passwd对的文件重定向给该命令。

# chpasswd < users.txt

passwd命令用法:

passwd 用户名

文件权限

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命令可以显示和设置这个默认权限。

$ umask

0022

$

要把umask值从对象的全权限值中减掉。对文件来说,全权限的值是666(所有用户都有读和写的权限);而对目录来说,则是777(所有用户都有读、写、执行权限)。

可以用umask命令为默认umask设置指定一个新值。

$ umask 026

$ touch newfile2

$ ls -l newfile2

-rw-r----- 1 rich rich 0 Sep 20 19:46 newfile2

$

改变权限chmod

命令的格式如下:

chmod options mode file

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

  • u代表用户
  • g代表组
  • o代表其他
  • a代表上述所有

后面跟着的符号表示你是想在现有权限基础上增加权限(+),还是在现有权限基础上移除权限(),或是将权限设置成后面的值(=)。

  • X:如果对象是目录或者它已有执行权限,赋予执行权限。
  • s:运行时重新设置UID或GID。
  • t:保留文件或目录。
  • u:将权限设置为跟属主一样。
  • g:将权限设置为跟属组一样。
  • o:将权限设置为跟其他用户一样。

例如:

$ chmod o+r newfile

$ ls -lF newfile

-rwxrw-r-- 1 rich rich 0 Sep 20 19:16 newfile*

$

不管其他用户在这一安全级别之前都有什么权限,o+r都给这一级别添加读取权限。

$ chmod u-x newfile

$ ls -lF newfile

-rw-rw-r-- 1 rich rich 0 Sep 20 19:16 newfile

$

u-x移除了属主已有的执行权限。注意ls命令的-F选项,它能够在具有执行权限的文件名后加一个星号

Readlink命令

使用readlink –f命令能够立刻找出链接文件的最后一环。

软件包管理

基于Red Hat 的系统

要找出系统上已安装的包,可在shell提示符下输入如下命令:

yum list installed

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

版 本              前端工具        命 令

Mandriva         urpm          rpm -qa > installed_software

openSUSE          zypper        zypper search -I > installed_software

yum擅长找出某个特定软件包的详细信息。它能给出关于包的非常详尽的描述,另外你还可

以通过一条简单的命令查看包是否已安装。

# yum list xterm

Loaded plugins: langpacks, presto, refresh-packagekit

Adding en_US to language list

Available Packages

xterm.i686 253-1.el6

#

# yum list installed xterm

Loaded plugins: refresh-packagekit

Error: No matching Packages to list

#

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

信息类型        前端工具        命 令

包信息      urpm          urpmq -i package_name

是否安装        urpm          rpm -q package_name

包信息      zypper        zypper search -s package_name

是否安装        zypper        同样的命令,注意在Status列查找i

Yum安装软件包

Yum安装软件包基本命令

yum install package_name

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

yum localinstall package_name.rpm

yum更新软件包

要列出所有已安装包的可用更新,输入如下命令:

yum list updates

如果这个命令没有输出就太好了,因为它说明你没有任何需要更新的!但如果发现某个特定

软件包需要更新,输入如下命令:

yum update package_name

如果想对更新列表中的所有包进行更新,只要输入如下命令:

yum update

yum卸载软件包

只删除软件包而保留配置文件和数据文件,就用如下命令:

yum remove package_name

要删除软件和它所有的文件,就用erase选项:

yum erase package_name

处理损坏的包依赖关系

有时在安装多个软件包时,某个包的软件依赖关系可能会被另一个包的安装覆盖掉。这叫作损坏的包依赖关系(broken dependency)。

如果系统出现了这个问题,先试试下面的命令:

yum clean all

然后试着用yum命令的update选项。有时,只要清理了放错位置的文件就可以了。

如果这还解决不了问题,试试下面的命令:

yum deplist package_name

如果这样仍未解决问题,还有最后一招:

yum update --skip-broken

--skip-broken选项允许你忽略依赖关系损坏的那个包,继续去更新其他软件包。

Yum仓库

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

yum repolist

如果仓库中没有需要的软件,你可以编辑一下配置文件。yum的仓库定义文件位于

/etc/yum.repos.d。你需要添加正确的URL,并获得必要的加密密钥。

make命令来构建各种二进制文件。make命令会编译源码

vim编辑器命令

编辑命令

在大的文本文件中一行一行地来回移动会特别麻烦,幸而vim提供了一些能够提高移动速度

的命令。

  • PageDown(或Ctrl+F):下翻一屏。
  • PageUp(或Ctrl+B):上翻一屏。
  • G:移到缓冲区的最后一行。
  • num G:移动到缓冲区中的第num行。
  • gg:移到缓冲区的第一行。

普通模式下vim编辑器命令

命 令       描 述

x          删除当前光标所在位置的字符

dd         删除当前光标所在行

dw         删除当前光标所在位置的单词

d$         删除当前光标所在位置至行尾的内容

J          删除当前光标所在行行尾的换行符(拼接行)

u          撤销前一编辑命令

a          在当前光标后追加数据

A          在当前光标所在行行尾追加数据

r char     用char替换当前光标所在位置的单个字符

R text     用text覆盖当前光标所在位置的数据,直到按下ESC键

查找和替换

替换命令的格式是:

:s/old/new/

vim编辑器会跳到old第一次出现的地方,并用new来替换。可以对替换命令作一些修改来替

换多处文本。

  • :s/old/new/g:一行命令替换所有old。
  • :n,ms/old/new/g:替换行号n和m之间所有old。高亮显示,不替换
  • :%s/old/new/g:替换整个文件中的所有old。直接替换
  • :%s/old/new/gc:替换整个文件中的所有old,但在每次出现时提示。

Shell脚本常用命令

变量赋值

在命令行中只能给变量赋一个值。

[root@localhost testing]# a=1

如果给多个会提示错误

[root@localhost testing]# age=1 2 3 3

-bash: 2: command not found

如果想给多个,可以通过命令结果赋值给变量的方式

[root@localhost testing]# f=(1 2 3 4 5)

[root@localhost testing]# arg=$(echo ${f[@]})

[root@localhost testing]# echo $arg

1 2 3 4 5

变量子串的常用操作(提取。。。)

表达式

说明

${#string}

返回$string的长度

${string:position}

在$string中,从位置$position之后开始提取子串,不包含$psotion位置的字符

${string:position:length}

在$string中,从位置$position之后开始提取长度为$length的子串

子串删除

${string#substring}

从变量$string开头开始删除最短匹配$substring子串

${string##substring}

从变量$string开头开始删除最长匹配$substring子串

${string%substring}

从变量$string结尾开始删除最短匹配$substring子串

${string%%substring}

从变量$string结尾开始删除最长匹配$substring子串

子串替换

${string/substring/replace}

使用$replace,来替换第一个匹配的$substring

${string/#substring/replace}

如果$string前缀匹配$substring,就用$replace来代替匹配$substring

${string/%substring/replace}

如果$stringho后缀匹配$substring,就用$replace来代替匹配$substring

如何使用子串替换批量修改文件后缀名。

方法:

for file in `ls *.jpg`;do mv ${file/%jpg/JPG};done

如何使用子串替换批量修改文件名。

for file in `ls *.jpg`:do mv $file `echo ${file%.jpg}|tr “[a-z]” “[A-Z]”`:done

ls *.jpg|awk -F “finished” ‘{print “mv” $0,$1””$2}’|bash(使用awk语言)

rename “finnshed” “” *.Jpg

rename语法:

rename “from” “to” “file”

rename “要改的” “改成的” 对哪些文件操作

例如:rename “.HTML” “.html” *.HTML

变量替换表(空值给默认值)

预算符号

替换

${value:-word}

如果变量名存在且非null,则返回变量的值。否则,返回word字符串。

用途:如果变量未定义,则返回默认值

例:${value:-word},如果value没有定义,则表达式的值为word,

即echo $value

word

${value:=word}

如果变量名存在且非null,则返回变量的值。否则,设置这个变量值为word,并返回其值。

用途:如果变量未定义,则设置变量为默认值,并返回默认值。

例:${value:-word},如果value没有定义,则设置value值为word,返回表达式的值也为word。

即echo $value

word

${value:?”not define”}

如果变量名存在且非null,则返回变量的值。否则,显示变量名:message,并退出当前的命令或者脚本。

用途:用于捕捉由于变量未定义而导致的错误,并退出程序。

例:${value:?”not define”}如果value未定义,则显示-bash:value:not define并退出。

${value:+word}

如果变量名存在且非null,则返回word。否则返回null。

用途:测试变量是否存在。

例:${value:+word}如果value已经定义,返回word(也就是真)

Echo命令

如果想把文本字符串和命令输出显示在同一行中,可以用echo语句的-n参数。命令输出将会在紧接着字符串结束的地方出现。

#vim test1

#!/bin/bash

echo -n "The time and date are: "

date

# ./test1

The time and date are: Mon Feb 21 15:42:23 EST 2014

数学运算命令

Expr命令

使用方法:

$ expr 1 + 5

6

$ expr 5 \* 2     #注意接转义符

10

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的值

bash shell数学运算符只支持整数运算。

方括号计算

使用方法:$[ operation ]

$ var1=$[1 + 5]

$ echo $var1

6

$ var2=$[$var1 * 2]

$ echo $var2

12

$

在使用方括号来计算公式时,不用担心shell会误解乘号或其他符号。shell知道它不是通配符,因为它在方括号内。

bash shell数学运算符只支持整数运算。

bc浮点数计算

浮点运算是由内建变量scale控制的。必须将这个值设置为你希望在计算结果中保留的小数

位数,否则无法得到期望的结果。

$ bc -q

3.44 / 5

0

scale=4

3.44 / 5

.6880

quit

$

scale变量的默认值是0。在scale值被设置前,bash计算器的计算结果不包含小数位。在将

其值设置成4后,bash计算器显示的结果包含四位小数。-q命令行选项可以不显示bash计算器冗

长的欢迎信息。

在脚本中使用bc基本格式如下:

variable=$(echo "options; expression" | bc)

$ cat test9

#!/bin/bash

var1=$(echo "scale=4; 3.44 / 5" | bc)

echo The answer is $var1

$

脚本中也可以使用变量进行bc计算,

$ cat test11

#!/bin/bash

var1=20

var2=3.14159

var3=$(echo "scale=4; $var1 * $var1" | bc)

var4=$(echo "scale=4; $var3 * $var2" | bc)

echo The final result is $var4

$

如果设计大量的数字计算,bc命令能识别输入重定向,使用内联输入重定向,它允许你直接在命令行中重定向数据。在shell脚本中,

你可以将输出赋给一个变量。

variable=$(bc << EOF

options

statements

expressions

EOF

)

EOF文本字符串标识了内联重定向数据的起止。记住,仍然需要命令替换符号将bc命令的输出赋给变量。

Linux退出状态码

Linux退出状态码

状 态 码        描 述

0            命令成功结束

1             一般性未知错误

2             不适合的shell命令

126           命令不可执行

127           没找到命令

128           无效的退出参数

128+x         与Linux信号x相关的严重错误

130           通过Ctrl+C终止的命令

255           正常范围之外的退出状态码

If语句

记住,在elif语句中,紧跟其后的else语句属于elif代码块。它们并不属于之前的

if-then代码块。但是如果之前的条件都不满足,还是会执行最后的else代码块中的命令。

If-then语句根据command1返回的退出状态码是否执行if-then中的代码块。

用法一:(不常用)

if command1

then

command set 1

elif command2

then

command set 2

else

command

fi

test命令可以判断三类条件:

  • 数值比较
  • 字符串比较
  • 文件比较

数值比较

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

方法二常用推荐(包含测试条件)

if [ conidition ]

then

commands1

elif [ conidition ]

then

commands2

else

commands3

fi

字符串比较

test命令的字符串比较功能

比 较       描 述

str1 = str2   检查str1是否和str2相同

str1 != str2 检查str1是否和str2不同

str1 < str2   检查str1是否比str2小

str1 > str2   检查str1是否比str2大

-n str1       检查str1的长度是否非0

-z str1       检查str1的长度是否为0

标准的数学比较符号来表示字符串比较,而用文本代码来表示数值比较。

文件比较

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旧

if语句的其他使用

双括号

双括号命令的格式如下:

(( expression ))

双括号命令符号

符 号

描 述

val++

后增

val--

后减

++val

先增

--val

先减

!

逻辑求反

~

位求反

**

幂运算

<<

左位移

>>

右位移

&

位布尔和

|

位布尔或

&&

逻辑和

||

逻辑或

双方括号

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

[[ expression ]]

Case语句

命令格式如下:

case variable in

pattern1 | pattern2) commands1;;

pattern3) commands2;;

*) default commands;;

esac

例如:

$ cat test26.sh

#!/bin/bash

# using the case command

#

case $USER in

rich | barbara)

echo "Welcome, $USER"

echo "Please enjoy your visit";;

testing)

echo "Special testing account";;

jessica)

echo "Do not forget to log off when you're done";;

*)

echo "Sorry, you are not allowed here";;

esac

$

$ ./test26.sh

Welcome, rich

Please enjoy your visit

$

for循环语句

命令的基本格式。

for var in list

do

commands

done

for无限循环写法

for ((;;))

do

done

分隔符,默认list使用空格为分割符,确定每一个值,如果值中有单引号可以使用转义符号\,使单引号脱义。也可以使用双引号来标识整个值的范围,如果值中有空格,也可以使用双引号标识。

例如:

$ cat test2

#!/bin/bash

# another example of how not to use the for command

for test in I don\'t know if "this'll" "wo rk"

do

echo "word:$test"

done

$ ./test2

word:I

word:don't

word:know

word:if

word:this'll

word:wo rk

$

更改字段分隔符

默认情况下,bash shell会将下列字符当作字段分隔符:

  • 空格
  • 制表符
  • 换行符

如果bash shell在数据中看到了这些字符中的任意一个,它就会假定这表明了列表中一个新数据字段的开始。

如果你想修改IFS的值,使其只能识别换行符,那就必须这么做:IFS=$'\n' 将这个语句加入到脚本中,告诉bash shell在数据值中忽略空格和制表符。

$ cat test5b

#!/bin/bash

# reading values from a file

file="states"

IFS=$'\n'

for state in $(cat $file)

do

echo "Visit beautiful $state"

done

$ ./test5b

Visit beautiful Alabama

Visit beautiful Alaska

Visit beautiful Arizona

Visit beautiful Arkansas

Visit beautiful Colorado

Visit beautiful Connecticut

$

警告 在处理代码量较大的脚本时,可能在一个地方需要修改IFS的值,然后忽略这次修改,在脚本的其他地方继续沿用IFS的默认值。一个可参考的安全实践是在改变IFS之前保存原来的IFS值,之后再恢复它。

这种技术可以这样实现:

IFS.OLD=$IFS

IFS=$'\n'

<在代码中使用新的IFS值>

IFS=$IFS.OLD

这就保证了在脚本的后续操作中使用的是IFS的默认值。

还可以指定IFS的值,例如需要使用:作为分割单位,可以像下面这样设定

IFS=:

如果你需要多个符号来分割,那么IFS也支持指定多个分割符,就像下面这样

IFS=$'\n':;"

这个赋值会将换行符、冒号、分号和双引号作为字段分隔符。如何使用IFS字符解析数据没有任何限制。

注意:

if [ -d "$file" ]

在Linux中,目录名和文件名中包含空格当然是合法的。要适应这种情况,应该将$file变

量用双引号圈起来。如果不这么做,遇到含有空格的目录名或文件名时就会有错误产生。

还可以在for数据列表中使用任意多个目录通配符,例如下面这样

for file in /home/rich/.b* /home/rich/badtest

do  #会先匹配/home/rich/.b*,然后匹配/home/rich/badtest

if [ -d "$file" ]

then

echo "$file is a directory"

elif [ -f "$file" ]

then

echo "$file is a file"

else

echo "$file doesn't exist"

fi

done

C 语言风格的for 命令

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

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

$ cat test8

#!/bin/bash

# testing the C-style for loop

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

do

echo "The next number is $i"

done

for循环中的do done也可以使用{}来代替,上面的例子也可以写成

$ cat test8

#!/bin/bash

# testing the C-style for loop

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

{

echo "The next number is $i"

}

使用多个变量

尽管可以使用多个变量,但你只能在for循环中定义一种条件。

$ cat test9

#!/bin/bash

# multiple variables

for (( a=1, b=10; a <= 10; a++, b-- ))

do

echo "$a - $b"

done

$ ./test9

1 - 10

2 - 9

3 - 8

4 - 7

5 - 6

6 - 5

7 - 4

8 - 3

9 - 2

10 - 1

$

while循环语句

while命令允许定义一个要测试的命令,然后循环执行一组命令,只要定义的测试命令返回的是退出状态码0。它会在每次迭代的一开始测试test命令。在test命令返回非零退出状态码时,while命令会停止执行那组命令。

while命令的格式是:

while test command

do

other commands

done

while无限循环有多种写法,这里举例2个

写法一

while true

do

done

写法二

while [ 1 ]

do

done

使用多个测试命令

只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环。

cat test11

#!/bin/bash

# testing a multicommand while loop

var1=10

while echo $var1

[ $var1 -ge 0 ]

do

echo "This is inside the loop"

var1=$[ $var1 - 1 ]

done

$ ./test11

10

This is inside the loop

9

This is inside the loop

8

This is inside the loop

7

This is inside the loop

6

This is inside the loop

5

This is inside the loop

4

This is inside the loop

3

This is inside the loop

2

This is inside the loop

1

This is inside the loop

0

This is inside the loop

-1

$

请仔细观察本例中做了什么。while语句中定义了两个测试命令。

while echo $var1

[ $var1 -ge 0 ]

第一个测试简单地显示了var1变量的当前值。第二个测试用方括号来判断var1变量的值。

在循环内部,echo语句会显示一条简单的消息,说明循环被执行了。注意当你运行本例时输出是如何结束的。

This is inside the loop

-1

$

while循环会在var1变量等于0时执行echo语句,然后将var1变量的值减一。接下来再次执行测试命令,用于下一次迭代。echo测试命令被执行并显示了var变量的值(现在小于0了)。直到shell执行test测试命令,whle循环才会停止。

这说明在含有多个命令的while语句中,在每次迭代中所有的测试命令都会被执行,包括测

试命令失败的最后一次迭代。要留心这种用法。另一处要留意的是该如何指定多个测试命令。注意,每个测试命令都出现在单独的一行上。

until 命令

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

出状态码的测试命令。只有测试命令的退出状态码不为0bash shell才会执行循环中列出的命令。

一旦测试命令返回了退出状态码0,循环就结束了。

until命令的格式如下。

until test commands

do

other commands

done

循环处理文件数据

通常必须遍历存储在文件中的数据。这要求结合已经讲过的两种技术:

  • 使用嵌套循环
  • 修改IFS环境变量

通过修改IFS环境变量,就能强制for命令将文件中的每行都当成单独的一个条目来处理,

即便数据中有空格也是如此。一旦从文件中提取出了单独的行,可能需要再次利用循环来提取行中的数据。

典型的例子是处理/etc/passwd文件中的数据。这要求你逐行遍历/etc/passwd文件,并将IFS变量的值改成冒号,这样就能分隔开每行中的各个数据段了。

#!/bin/bash

# changing the IFS value

IFS.OLD=$IFS

IFS=$'\n'

for entry in $(cat /etc/passwd)

do

echo "Values in $entry –"

IFS=:

for value in $entry

do

echo " $value"

done

done

$

这个脚本使用了两个不同的IFS值来解析数据。第一个IFS值解析出/etc/passwd文件中的单独的行。内部for循环接着将IFS的值修改为冒号,允许你从/etc/passwd的行中解析出单独的值。

在运行这个脚本时,你会得到如下输出。

Values in rich:x:501:501:Rich Blum:/home/rich:/bin/bash -

rich

x

501

501

Rich Blum

/home/rich

/bin/bash

Values in katie:x:502:502:Katie Blum:/home/katie:/bin/bash -

katie

x

506

509

Katie Blum

/home/katie

/bin/bash

内部循环会解析出/etc/passwd每行中的各个值。这种方法在处理外部导入电子表格所采用的

逗号分隔的数据时也很方便。

控制循环

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

break命令

continue命令

break 命令

跳出循环,默认跳出一层循环,可以在break后加参数,表示需要跳出几层循环。

例如

break 2

表示跳出2层循环。(if-then是判断,for,while,until才是循环)

continue 命令

continue命令可以提前中止某次循环中的命令,但并不会完全终止整个循环。

相当于给出条件,把符合这个条件东西过滤出去,不提供显示或者输出

也可以在while和until循环中使用continue命令,但要特别小心。记住,当shell执行

continue命令时,它会跳过剩余的命令。

处理循环的输出

你可以对循环的输出使用管道或进行重定向。这可以通过在done命令之后添加一个处理命令来实现。

for file in /home/rich/*

do

if [ -d "$file" ]

then

echo "$file is a directory"

elif

echo "$file is a file"

fi

done > output.txt

处理用户输入($0,$1-$9)

位置参数变量是标准的数字:$0是程序名,$1是第一个参数,$2是第二个参数,依次类推,直到第九个参数$9。在第9个变量之后,你必须在变量数字周围加上花括号,比如${10}。

$ cat test4.sh

#!/bin/bash

# handling lots of parameters

#

total=$[ ${10} * ${11} ]

echo The tenth parameter is ${10}

echo The eleventh parameter is ${11}

echo The total is $total

$

$ ./test4.sh 1 2 3 4 5 6 7 8 9 10 11 12

The tenth parameter is 10

The eleventh parameter is 11

The total is 110

$

传递命令行参数时,如果参数中间有空格,需要使用引号(单引号,双引号均可)。

$ cat test3.sh

#!/bin/bash

# testing string parameters

#

echo Hello $1, glad to meet you.

$

$ ./test3.sh 'Rich Blum'

Hello Rich Blum, glad to meet you.

$

$ ./test3.sh "Rich Blum"

Hello Rich Blum, glad to meet you.

$

读取脚本名($0)basename用法

可以用$0参数获取shell在命令行启动的脚本名。使用什么方式很重要,如果使用另一个命令来运行shell脚本,命令会和脚本名混在一起,出现在$0参数中。

$ ./test5.sh

The zero parameter is set to: ./test5.sh

$

当传给$0变量的实际字符串不仅仅是脚本名,而是完整的脚本路径时,变量$0就会使用整个路径。

$ bash /home/Christine/test5.sh

The zero parameter is set to: /home/Christine/test5.sh

$

basename命令会返回不包含路径的脚本名。注意basename的使用方法

$ cat test5b.sh

#!/bin/bash

# Using basename with the $0 parameter

#

name=$(basename $0)

echo

echo The script name is: $name

#

$ bash /home/Christine/test5b.sh

The script name is: test5b.sh

$

$ ./test5b.sh

The script name is: test5b.sh

$

参数统计($#)

特殊变量$#含有脚本运行时携带的命令行参数的个数。如果需要使用多个命令行参数时,可以在脚本运行时直接检查参数够不够,更加简单,不需要一个一个作对比。

这个变量还提供了一个简便方法来获取命令行中最后一个参数,完全不需要知道实际上到底

用了多少个参数。使用${!#}就可以知道命令行参数的最后一个参数。

$ cat test10.sh

#!/bin/bash

# Grabbing the last parameter

#

params=$#

echo

echo The last parameter is $params

echo The last parameter is ${!#}

echo

#

$

$ bash test10.sh 1 2 3 4 5

The last parameter is 5

The last parameter is 5

$

$ bash test10.sh

The last parameter is 0

The last parameter is test10.sh

$

抓取所有的数据($*和$@)

$*和$@变量可以用来轻松访问所有的参数。这两个变量都能够在单个变量中存储所有的命

令行参数。

$*变量会将命令行上提供的所有参数当作一个单词保存。这个单词包含了命令行中出现的每

一个参数值。基本上$*变量会将这些参数视为一个整体,而不是多个个体。

另一方面,$@变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。这样

你就能够遍历所有的参数值,得到每个参数。

$*变量会将所有参数当成单个参数,而$@变量会单独处理每个参数。

下面的例子给出了差异

$ cat test12.sh

#!/bin/bash

# testing $* and $@

#

echo

count=1

#

for param in "$*"

do

echo "\$* Parameter #$count = $param"

count=$[ $count + 1 ]

done

#

echo

count=1

#

for param in "$@"

do

echo "\$@ Parameter #$count = $param"

count=$[ $count + 1 ]

done

$

$ ./test12.sh rich barbara katie jessica

$* Parameter #1 = rich barbara katie jessica

$@ Parameter #1 = rich

$@ Parameter #2 = barbara

$@ Parameter #3 = katie

$@ Parameter #4 = jessica

$

移动变量(shift)

bash shell的shift命令能够用来操作命令行参数。跟字面上的意思一样,shift命令会根据它们的相对位置来移动命令行参数。

在使用shift命令时,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3

的值会移到$2中,变量$2的值会移到$1中,而变量$1的值则会被删除(注意,变量$0的值,也就是程序名,不会改变)。

这里有个例子来解释它是如何工作的。

$ cat test13.sh

#!/bin/bash

# demonstrating the shift command

echo

count=1

while [ -n "$1" ]

do

echo "Parameter #$count = $1"

count=$[ $count + 1 ]

shift

done

$

$ ./test13.sh rich barbara katie jessica

Parameter #1 = rich

Parameter #2 = barbara

Parameter #3 = katie

Parameter #4 = jessica

$

注意:使用shift命令的时候要小心。如果某个参数被移出,它的值就被丢弃了,无法再恢复。

你也可以一次性移动多个位置,只需要给shift命令提供一个参数,指明要移动的位置数就行了。

$ cat test14.sh

#!/bin/bash

# demonstrating a multi-position shift

#

echo

echo "The original parameters: $*"

shift 2

echo "Here's the new first parameter: $1"

$

$ ./test14.sh 1 2 3 4 5

The original parameters: 1 2 3 4 5

Here's the new first parameter: 3

$

通过使用shift命令的参数,就可以轻松地跳过不需要的参数。

处理选项

处理简单选项

在前面的test13.sh脚本中,你看到了如何使用shift命令来依次处理脚本程序携带的命令

行参数。你也可以用同样的方法来处理命令行选项。

在提取每个单独参数时,用case语句(参见第12章)来判断某个参数是否为选项。

$ 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

$

case语句会检查每个参数是不是有效选项。如果是的话,就运行对应case语句中的命令。

不管选项按什么顺序出现在命令行上,这种方法都适用。

$ ./test15.sh -d -c -a

-d is not an option

Found the -c option

Found the -a option

$

case语句在命令行参数中找到一个选项,就处理一个选项。如果命令行上还提供了其他参

数,你可以在case语句的通用情况处理部分中处理。

分离参数和选项(--)

你会经常遇到想在shell脚本中同时使用选项和参数的情况。Linux中处理这个问题的标准方

式是用特殊字符来将二者分开,该字符会告诉脚本何时选项结束以及普通参数何时开始。

对Linux来说,这个特殊字符是双破折线(--)。shell会用双破折线来表明选项列表结束。在

双破折线之后,脚本就可以放心地将剩下的命令行参数当作参数,而不是选项来处理了。

要检查双破折线,只要在case语句中加一项就行了。

$ cat test16.sh

#!/bin/bash

# extracting options and 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" ;;

--) shift

break ;;

*) echo "$1 is not an option";;

esac

shift

done

#

count=1

for param in $@

do

echo "Parameter #$count: $param"

count=$[ $count + 1 ]

done

$

在遇到双破折线时,脚本用break命令来跳出while循环。由于过早地跳出了循环,我们需

要再加一条shift命令来将双破折线移出参数变量。(这个shift是break上边的shift)

对于第一个测试,试试用一组普通的选项和参数来运行这个脚本。

$ ./test16.sh -c -a -b test1 test2 test3

Found the -c option

Found the -a option

Found the -b option

test1 is not an option

test2 is not an option

test3 is not an option

$

处理带值的选项

有些选项会带上一个额外的参数值。在这种情况下,命令行看起来像下面这样。

$ ./testing.sh -a test1 -b -c -d test2

当命令行选项要求额外的参数时,脚本必须能检测到并正确处理。下面是如何处理的

例子。

$ cat test17.sh

#!/bin/bash

# extracting command line options and values

echo

while [ -n "$1" ]

do

case "$1" in

-a) echo "Found the -a option";;

-b) param="$2"

echo "Found the -b option, with parameter value $param"

shift ;;

-c) echo "Found the -c option";;

--) shift

break ;;

*) echo "$1 is not an option";;

esac

shift

done

#

count=1

for param in "$@"

do

echo "Parameter #$count: $param"

count=$[ $count + 1 ]

done

$

$ ./test17.sh -a -b test1 -d

Found the -a option

Found the -b option, with parameter value test1

-d is not an option

$

在这个例子中,case语句定义了三个它要处理的选项。-b选项还需要一个额外的参数值。

由于要处理的参数是$1,额外的参数值就应该位于$2(因为所有的参数在处理完之后都会被移

出)。只要将参数值从$2变量中提取出来就可以了。当然,因为这个选项占用了两个参数位,所

以你还需要使用shift命令多移动一个位置。

只用这些基本的特性,整个过程就能正常工作,不管按什么顺序放置选项(但要记住包含每个选项相应的选项参数)。

$ ./test17.sh -b test1 -a -d

Found the -b option, with parameter value test1

Found the -a option

-d is not an option

$

现在shell脚本中已经有了处理命令行选项的基本能力,但还有一些限制。比如,如果你想将

多个选项放进一个参数中时,它就不能工作了。

$ ./test17.sh -ac

-ac is not an option

$

在Linux中,合并选项是一个很常见的用法,而且如果脚本想要对用户更友好一些,也要给

用户提供这种特性。幸好,有另外一种处理选项的方法能够帮忙。

使用getopts 命令

每次调用它时,它一次只处理命令行上检测到的一个参数。处理完所有的参数后,它会退出

并返回一个大于0的退出状态码。这让它非常适合用解析命令行所有参数的循环中。

getopts命令的格式如下:

getopts optstring variable

optstring值类似于getopt命令中的那个。有效的选项字母都会列在optstring中,如果

选项字母要求有个参数值,就在该字母后加一个冒号。要去掉错误消息的话,可以在optstring之前加一个冒号。getopts命令将当前参数保存在命令行中定义的variable中。

getopts命令会用到两个环境变量。如果选项需要跟一个参数值,OPTARG环境变量就会保

存这个值。OPTIND环境变量保存了参数列表中getopts正在处理的参数位置。这样你就能在处

理完选项之后继续处理其他命令行参数了。

让我们看个使用getopts命令的简单例子。

$ cat test19.sh

#!/bin/bash

# simple demonstration of the getopts command

#

echo

while getopts :ab:c opt

do

case "$opt" in

a) echo "Found the -a option" ;;

b) echo "Found the -b option, with value $OPTARG";;

c) echo "Found the -c option" ;;

*) echo "Unknown option: $opt";;

esac

done

$

$ ./test19.sh -ab test1 -c

Found the -a option

Found the -b option, with value test1

Found the -c option

$

getopts命令有几个好用的功能。对新手来说,可以在参数值中包含空格。

$ ./test19.sh -b "test1 test2" -a

Found the -b option, with value test1 test2

Found the -a option

$

另一个好用的功能是将选项字母和参数值放在一起使用,而不用加空格。

$ ./test19.sh -abtest1

Found the -a option

Found the -b option, with value test1

$

getopts命令能够从-b选项中正确解析出test1值。除此之外,getopts还能够将命令行上

找到的所有未定义的选项统一输出成问号。

$ ./test19.sh -acde

Found the -a option

Found the -c option

Unknown option: ?

Unknown option: ?

$

getopts命令知道何时停止处理选项,并将参数留给你处理。在getopts处理每个选项时,它会将OPTIND环境变量值增一。在getopts完成处理时,你可以使用shift命令和OPTIND值来移动参数。

$ cat test20.sh

#!/bin/bash

# Processing options & parameters with getopts

#

echo

while getopts :ab:cd opt

do

case "$opt" in

a) echo "Found the -a option" ;;

b) echo "Found the -b option, with value $OPTARG" ;;

c) echo "Found the -c option" ;;

d) echo "Found the -d option" ;;

*) echo "Unknown option: $opt" ;;

esac

done

#

shift $[ $OPTIND - 1 ]

#

echo

count=1

for param in "$@"

do

echo "Parameter $count: $param"

count=$[ $count + 1 ]

done

#

$

$ ./test20.sh -a -b test1 -d test2 test3 test4

Found the -a option

Found the -b option, with value test1

Found the -d option

Parameter 1: test2

Parameter 2: test3

Parameter 3: test4

$

选项标准化

这些字母选项在Linux世界里已经拥有了某种程度的标准含义。能支持对用户会很友好

常用的Linux命令选项

选 项    描 述

-a 显示所有对象

-c 生成一个计数

-d 指定一个目录

-e 扩展一个对象

-f 指定读入数据的文件

-h 显示命令的帮助信息

-i 忽略文本大小写

-l 产生输出的长格式版本

-n 使用非交互模式(批处理)

-o 将所有输出重定向到的指定的输出文件

-q 以安静模式运行

-r 递归地处理目录和文件

-s 以安静模式运行

-v 生成详细输出

-x 排除某个对象

-y 对所有问题回答yes

获得用户输入read

read命令包含了-p选项,允许你直接在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命令会将提示符后输入的所有数据分配给单个变量,要么你就指定多个变量。输入的每个数据值都会分配给变量列表中的下一个变量。如果变量数量不够,剩下的数据就全部分配给最后一个变量。

在read命令行中也可以不指定变量。如果是这样,read命令会将它收到的任何数据都放进

特殊环境变量REPLY中。

$ cat test24.sh

#!/bin/bash

# Testing the REPLY Environment variable

#

read -p "Enter your name: "

echo

echo Hello $REPLY, welcome to my program.

#

$

$ ./test24.sh

Enter your name: Christine

Hello Christine, welcome to my program.

$

REPLY环境变量会保存输入的所有数据,可以在shell脚本中像其他变量一样使用。

read超时

可以用-t选项来指定一个计时器。-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

$

$ ./test25.sh

Please enter your name: Rich

Hello Rich, welcome to my script

$

$ ./test25.sh

Please enter your name:

Sorry, too slow!

$

在本例中,计时器过期时,if语句不成立,shell会执行else部分的命令。

限制read输入字符

也可以不对输入过程计时,而是让read命令来统计输入的字符数。当输入的字符达到预设

的字符数时,就自动退出,将输入的数据赋给变量。

$ cat test26.sh

#!/bin/bash

# getting just one character of input

#

read -n1 -p "Do you want to continue [Y/N]? " answer

case $answer in

Y | y) echo

echo "fine, continue on…";;

N | n) echo

echo OK, goodbye

exit;;

esac

echo "This is the end of the script"

$

$ ./test26.sh

Do you want to continue [Y/N]? Y

fine, continue on…

This is the end of the script

$

$ ./test26.sh

Do you want to continue [Y/N]? n

OK, goodbye

$

本例中将-n选项和值1一起使用,告诉read命令在接受单个字符后退出。只要按下单个字符

回答后,read命令就会接受输入并将它传给变量,无需按回车键。

read从文件中读取

用read命令来读取Linux系统上文件里保存的数据。每次调用read命令,它都会从文件中读取一行文本。当文件中再没有内容时,read命令会退出并返回非零退出状态码。

最常见的方法是对文件使用cat命令,将

结果通过管道直接传给含有read命令的while命令。下面的例子说明怎么处理。

$ 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

$

呈现数据

理解输入和输出

标准的文件描述符有3个。0、1、2,分别对应输入,输出,错误输出

Linux的标准文件描述符

文件描述符     缩 写      描 述

0             STDIN      标准输入

1             STDOUT     标准输出

2             STDERR     标准错误

重定向

重定向> 和追加重定向 >> 重定向后不再提供屏幕显示

如果需要重定向和屏幕显示同时存在,可以使用tee(重定向),tee –a(追加重定向)

临时重定向的使用方法:直接在命令后跟 > 或 >> 重定向文件。  cat file >t.log

永久重定向的使用方法:使用exec命令。     exec 1>testout

注意,文件描述符,重定向符和重定向文件之间没有空格

文件描述符

在shell中最多可以有9个打开的文件描述符。其他6个从3~8的文件描述符均可用作输入或输出重定向。

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

echo "This is an error message" >&2

使用和恢复文件描述符

下面这个例子举例了标准输出的恢复。

$ cat test14

#!/bin/bash

# storing STDOUT, then coming back to it

exec 3>&1

exec 1>test14out

echo "This should store in the output file"

echo "along with this line."

exec 1>&3

echo "Now things should be back to normal"

$

$ ./test14

Now things should be back to normal

$ cat test14out

This should store in the output file

along with this line.

$

首先,脚本将文件描述符3重定向到文件描述符1的当前位置,也就是STDOUT。这意味着任何发送给文件描述符3的输出都将出现在显示器上。

第二个exec命令将STDOUT重定向到文件,shell现在会将发送给STDOUT的输出直接重定向到输出文件中。但是,文件描述符3仍然指向STDOUT原来的位置,也就是显示器。如果此时将输出数据发送给文件描述符3,它仍然会出现在显示器上,尽管STDOUT已经被重定向了。在向STDOUT(现在指向一个文件)发送一些输出之后,脚本将STDOUT重定向到文件描述符3的当前位置(现在仍然是显示器)。这意味着现在STDOUT又指向了它原来的位置:显示器。

这个方法可能有点叫人困惑,但这是一种在脚本中临时重定向输出,然后恢复默认输出设置

的常用方法

下面这个例子举例了标准输入的恢复

$ cat test15

#!/bin/bash

# redirecting input file descriptors

exec 6<&0

exec 0< testfile

count=1

while read line

do

echo "Line #$count: $line"

count=$[ $count + 1 ]

done

exec 0<&6

read -p "Are you done now? " answer

case $answer in

Y|y) echo "Goodbye";;

N|n) echo "Sorry, this is the end.";;

esac

$ ./test15

Line #1: This is the first line.

Line #2: This is the second line.

Line #3: This is the third line.

Are you done now? y

Goodbye

$

在这个例子中,文件描述符6用来保存STDIN的位置。然后脚本将STDIN重定向到一个文件。

read命令的所有输入都来自重定向后的STDIN(也就是输入文件)。

在读取了所有行之后,脚本会将STDIN重定向到文件描述符6,从而将STDIN恢复到原先的

位置。该脚本用了另外一个read命令来测试STDIN是否恢复正常了。这次它会等待键盘的输入。

关闭文件描述符

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

exec 3>&-

列出打开的文件描述符

lsof命令会列出整个Linux系统打开的所有文件描述符。它会向非系统管理员用户提供Linux系统的信息。在很多Linux系统中(如Fedora),lsof命令位于/usr/sbin目录。要想以普通用户账户来运行它,必须通过全路径名来引用:

$ /usr/sbin/lsof

常用的参数有-p和-d,前者允许指定进程ID(PID),后者允许指定要显示的文件描述符编号

要想知道进程的当前PID,可以用特殊环境变量$$(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的默认输出中有7列信息。

lsof的默认输出

列         描 述

COMMAND    正在运行的命令名的前9个字符

PID        进程的PID

USER       进程属主的登录名

FD         文件描述符号以及访问类型(r代表读,w代表写,u代表读写)

TYPE       文件的类型(CHR代表字符型,BLK代表块型,DIR代表目录,REG代表常规文件)

DEVICE     设备的设备号(主设备号和从设备号)

SIZE       如果有的话,表示文件的大小

NODE       本地文件的节点号

NAME       文件名

创建临时文件和文件夹

Linux使用/tmp目录来存放不需要永久保留的文件。大多数Linux发行版配置了系统在启动时自动删除/tmp目录的所有文件。

mktemp会在本地目录中创建一个文件。要用mktemp命令在本地目录中创建一

个临时文件,你只要指定一个文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾加上6个X就行了。

$ mktemp testing.XXXXXX

$ ls -al testing*

-rw------- 1 rich rich 0 Oct 17 21:30 testing.UfIi13

$

mktemp命令会用6个字符码替换这6个X,从而保证文件名在目录中是唯一的。你可以创建多个临时文件,它可以保证每个文件都是唯一的。

-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

$

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

$ cat test21

#!/bin/bash

# using a temporary directory

tempdir=$(mktemp -d dir.XXXXXX)

cd $tempdir

tempfile1=$(mktemp temp.XXXXXX)

tempfile2=$(mktemp temp.XXXXXX)

exec 7> $tempfile1

exec 8> $tempfile2

echo "Sending data to directory $tempdir"

echo "This is a test line of data for $tempfile1" >&7

echo "This is a test line of data for $tempfile2" >&8

$ ./test21

Sending data to directory dir.ouT8S8

$ ls –al

total 72

drwxr-xr-x 3 rich rich 4096 Oct 17 22:20 ./

drwxr-xr-x 9 rich rich 4096 Oct 17 09:44 ../

drwx------ 2 rich rich 4096 Oct 17 22:20 dir.ouT8S8/

-rwxr--r-- 1 rich rich 338 Oct 17 22:20 test21*

$ cd dir.ouT8S8

[dir.ouT8S8]$ ls -al

total 16

drwx------ 2 rich rich 4096 Oct 17 22:20 ./

drwxr-xr-x 3 rich rich 4096 Oct 17 22:20 ../

-rw------- 1 rich rich 44 Oct 17 22:20 temp.N5F3O6

-rw------- 1 rich rich 44 Oct 17 22:20 temp.SQslb7

[dir.ouT8S8]$ cat temp.N5F3O6

This is a test line of data for temp.N5F3O6

[dir.ouT8S8]$ cat temp.SQslb7

This is a test line of data for temp.SQslb7

[dir.ouT8S8]$

这段脚本在当前目录创建了一个目录,然后它用cd命令进入该目录,并创建了两个临时文件。

之后这两个临时文件被分配给文件描述符,用来存储脚本的输出。

信号

捕获信号

trap命令允许你来指定shell

脚本要监看并从shell中拦截的Linux信号。如果脚本收到了trap命令中列出的信号,该信号不再

由shell处理,而是交由本地处理。

trap命令的格式是:

trap commands signals

这里有个简单例子,展示了如何使用trap命令来忽略SIGINT信号,并控制脚本的行为。

$ cat test1.sh

#!/bin/bash

# Testing signal trapping

#

trap "echo ' Sorry! I have trapped Ctrl-C'" SIGINT

#

echo This is a test script

#

count=1

while [ $count -le 10 ]

do

echo "Loop #$count"

sleep 1

count=$[ $count + 1 ]

done

#

echo "This is the end of the test script"

#

本例中用到的trap命令会在每次检测到SIGINT信号时显示一行简单的文本消息。捕获这些

信号会阻止用户用bash shell组合键Ctrl+C来停止程序。

$ ./test1.sh

This is a test script

Loop #1

Loop #2

Loop #3

Loop #4

Loop #5

^C Sorry! I have trapped Ctrl-C

Loop #6

Loop #7

Loop #8

^C Sorry! I have trapped Ctrl-C

Loop #9

Loop #10

This is the end of the test script

$

每次使用Ctrl+C组合键,脚本都会执行trap命令中指定的echo语句,而不是处理该信号并

允许shell停止该脚本。

捕获脚本退出

除了在shell脚本中捕获信号,你也可以在shell脚本退出时进行捕获。这是在shell完成任务时执行命令的一种简便方法。

要捕获shell脚本的退出,只要在trap命令后加上EXIT信号就行。

$ cat test2.sh

#!/bin/bash

# Trapping the script exit

#

trap "echo Goodbye..." EXIT

#

count=1

while [ $count -le 5 ]

do

echo "Loop #$count"

sleep 1

count=$[ $count + 1 ]

done

#

$

$ ./test2.sh

Loop #1

Loop #2

Loop #3

Loop #4

Loop #5

Goodbye...

$

当脚本运行到正常的退出位置时,捕获就被触发了,shell会执行在trap命令行指定的命令。

如果提前退出脚本,同样能够捕获到EXIT。

$ ./test2.sh

Loop #1

Loop #2

Loop #3

^CGoodbye...

$

因为SIGINT信号并没有出现在trap命令的捕获列表中,当按下Ctrl+C组合键发送SIGINT信

号时,脚本就退出了。但在脚本退出前捕获到了EXIT,于是shell执行了trap命令。

修改或移除捕获

要想在脚本中的不同位置进行不同的捕获处理,只需重新使用带有新选项的trap命令。

$ cat test3.sh

#!/bin/bash

# Modifying a set trap

#

trap "echo ' Sorry... Ctrl-C is trapped.'" SIGINT

#

count=1

while [ $count -le 5 ]

do

echo "Loop #$count"

sleep 1

count=$[ $count + 1 ]

done

#

trap "echo ' I modified the trap!'" SIGINT

#

count=1

while [ $count -le 5 ]

do

echo "Second Loop #$count"

sleep 1

count=$[ $count + 1 ]

done

#

$

修改了信号捕获之后,脚本处理信号的方式就会发生变化。但如果一个信号是在捕获被修改

前接收到的,那么脚本仍然会根据最初的trap命令进行处理。

$ ./test3.sh

Loop #1

Loop #2

Loop #3

^C Sorry... Ctrl-C is trapped.

Loop #4

Loop #5

Second Loop #1

Second Loop #2

^C I modified the trap!

Second Loop #3

Second Loop #4

Second Loop #5

$

也可以删除已设置好的捕获。

删除捕获

$ cat test3b.sh

#!/bin/bash

# Removing a set trap

#

trap "echo ' Sorry... Ctrl-C is trapped.'" SIGINT

#

count=1

while [ $count -le 5 ]

do

echo "Loop #$count"

sleep 1

count=$[ $count + 1 ]

done

#

# Remove the trap

trap -- SIGINT

echo "I just removed the trap"

#

count=1

while [ $count -le 5 ]

do

echo "Second Loop #$count"

sleep 1

count=$[ $count + 1 ]

done

#

$ ./test3b.sh

Loop #1

Loop #2

Loop #3

Loop #4

Loop #5

I just removed the trap

Second Loop #1

Second Loop #2

Second Loop #3

^C

$

窍门 也可以在trap命令后使用单破折号来恢复信号的默认行为。单破折号和双破折号都可以

正常发挥作用。

nohup命令(脱离会话终端)

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

话时阻止进程退出。

nohup命令的格式如下:

$ nohup ./test1.sh &

[1] 3856

$ nohup: ignoring input and appending output to 'nohup.out'

$

和普通后台进程一样,shell会给命令分配一个作业号,Linux系统会为其分配一个PID号。区

别在于,当你使用nohup命令时,如果关闭该会话,脚本会忽略终端会话发过来的SIGHUP信号。

由于nohup命令会解除终端与进程的关联,进程也就不再同STDOUT和STDERR联系在一起。

为了保存该命令产生的输出,nohup命令会自动将STDOUT和STDERR的消息重定向到一个名为

nohup.out的文件中。

注意:如果在运行位于同一个目录中的多个命令时一定要当心,因为所有的输出都会被发送到同一个nohup.out文件中。

查看作业号(jobs)

通过jobs可以查看当前所有的作业情况,如果想看作业的PID,通过jobs -l便可以查看。(小写的L)带加号的作业会被当做默认作业。带减号的作业成为下一个默认作业。在任何情况下,只有一个加号和一个减号,不管多少正在运行的作业。

$ jobs -l

[1]+ 1897 Stopped ./test10.sh

[2]- 1917 Running ./test10.sh > test10.out &

$

jobs命令参数

参 数       描 述

-l         列出进程的PID以及作业号

-n         只列出上次shell发出的通知后改变了状态的作业

-p         只列出作业的PID

-r         只列出运行中的作业

-s         只列出已停止的作业

重启被暂停的作业

bg(以后台方式运行)+作业号

$ bg 2

fg(以前台方式运行)+作业号

$ fg 2

调整谦让度

调度优先级是个整数值,从20(最高优先级)到+19(最低优先级)。默认情况下,bash shell

以优先级0来启动所有进程。

只要记住那句俗语“好人难做”就行了。越是“好”或高的值,获得CPU时间的机会越低。

nice 命令

nice命令允许你设置命令启动时的调度优先级。要让命令以更低的优先级运行,只要用nice

的-n命令行来指定新的优先级级别。

$ 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

nice命令的-n选项并不是必须的,只需要在破折号后面跟上优先级就行了。

$ nice -10 ./test4.sh > test4.out &

[1] 4993

$

$ ps -p 4993 -o pid,ppid,ni,cmd

PID PPID NI CMD

4993 4721 10 /bin/bash ./test4.sh

$

renice命令

有时你想改变系统上已运行命令的优先级。这正是renice命令可以做到的。它允许你指定

运行进程的PID来改变它的优先级。

$ ./test11.sh &

[1] 5055

$

$ ps -p 5055 -o pid,ppid,ni,cmd

PID PPID NI CMD

5055 4721 0 /bin/bash ./test11.sh

$

$ 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来任意调整进程的优先级。

如果想完全控制运行进程,必须以root账户身份登录或使用sudo命令。

定时任务

cron时间表

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

min hour dayofmonth month dayofweek command

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

如,如果想在每天的10:15运行一个命令,可以用cron时间表条目:

15 10 * * * command

如何在每个月最后一天执行,常用的方法是加一条使用date命令的if-then语句来检查明天的日期是不是01:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command

它会在每天中午12点来检查是不是当月的最后一天,如果是,cron将会运行该命令。

构建cron时间表

Linux提供

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

$ crontab -l

no crontab for rich

$

要为cron时间表添加条目,可以用-e选项。

函数

return

可以指定一个整数值来定义函数的退出状态码,但当用这种方法从函数中返回值时,要小心了。记住下面两条技巧来避免问题:

 记住,函数一结束就取返回值;

 记住,退出状态码必须是0~255。

$ cat test5

#!/bin/bash

# using the return command in a function

function dbl {

read -p "Enter a value: " value

echo "doubling the value"

return $[ $value * 2 ]

}

dbl

echo "The new value is $?"

$

$ ./test5

Enter a value: 2

doubling the value

The new value is 4

$

使用函数输出

正如可以将命令的输出保存到shell变量中一样,你也可以对函数的输出采用同样的处理办

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

result='dbl'

这个命令会将dbl函数的输出赋给$result变量。下面是在脚本中使用这种方法的例子。

$ cat test5b

#!/bin/bash

# using the echo to return a value

function dbl {

read -p "Enter a value: " value

echo $[ $value * 2 ]

}

result=$(dbl)

echo "The new value is $result"

$

$ ./test5b

Enter a value: 200

The new value is 400

$

$ ./test5b

Enter a value: 1000

The new value is 2000

$

新函数会用echo语句来显示计算的结果。该脚本会获取dbl函数的输出,而不是查看退出状

态码。

这个例子中演示了一个不易察觉的技巧。你会注意到dbl函数实际上输出了两条消息。read

命令输出了一条简短的消息来向用户询问输入值。bash shell脚本非常聪明,并不将其作为STDOUT输出的一部分,并且忽略掉它。如果你用echo语句生成这条消息来向用户查询,那么它会与输出值一起被读进shell变量中。

说明 通过这种技术,你还可以返回浮点值和字符串值。这使它成为一种获取函数返回值的强

大方法。

在函数中处理变量

全局变量

默认情况下,你在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正

常访问。

$ cat test8

#!/bin/bash

# using a global variable to pass a value

function dbl {

value=$[ $value * 2 ]

}

read -p "Enter a value: " value

dbl

echo "The new value is: $value"

$

$ ./test8

Enter a value: 450

The new value is: 900

$

$value变量在函数外定义并被赋值。当dbl函数被调用时,该变量及其值在函数中都依然有

效。如果变量在函数内被赋予了新值,那么在脚本中引用该变量时,新值也依然有效。

局部变量

函数内部使用的任何变量都可以被声明成局部变量。要实现这一点,只要在变量声明的前面加上local关键字就可以了。temp为变量

local temp

也可以在变量赋值语句中使用local关键字:

local temp=$[ $value + 5 ]

local关键字保证了变量只局限在该函数中。如果脚本中在该函数之外有同样名字的变量,

那么shell将会保持这两个变量的值是分离的。现在你就能很轻松地将函数变量和脚本变量隔离开

了,只共享需要共享的变量。

$ cat test9

#!/bin/bash

# demonstrating the local keyword

function func1 {

local temp=$[ $value + 5 ]

result=$[ $temp * 2 ]

}

temp=4

value=6

func1

echo "The result is $result"

if [ $temp -gt $value ]

then

echo "temp is larger"

else

echo "temp is smaller"

fi

$

$ ./test9

The result is 22

temp is smaller

$

现在,在func1函数中使用$temp变量时,并不会影响在脚本主体中赋给$temp变量的值。

向函数传数组参数

你必须将该数组变量的值分解成单个的值,然后将这些值作为函数参数使

用。在函数内部,可以将所有的参数重新组合成一个新的变量。下面是个具体的例子。

$ 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变量来保存所有的数组元素,然后将它们都放在函数的命令行上。该函

数随后从命令行参数中重建数组变量。在函数内部,数组仍然可以像其他数组一样使用。

$ cat test11

#!/bin/bash

# adding values in an array

function addarray {

local sum=0

local newarray

newarray=($(echo "$@"))

for value in ${newarray[*]}

do

sum=$[ $sum + $value ]

done

echo $sum

}

myarray=(1 2 3 4 5)

echo "The original array is: ${myarray[*]}"

arg1=$(echo ${myarray[*]})

result=$(addarray $arg1)

echo "The result is $result"

$

$ ./test11

The original array is: 1 2 3 4 5

The result is 15

$

addarray函数会遍历所有的数组元素,将它们累加在一起。你可以在myarray数组变量中

放置任意多的值,addarry函数会将它们都加起来。

创建函数库和调用

创建函数库就是吧一个函数写在一个固定的文件里,然后再脚本中使用时直接调用即可。

这里有个叫作myfuncs的库文件,它定义了3个简单的函数。

$ cat myfuncs

# my script functions

function addem {

echo $[ $1 + $2 ]

}

function multem {

echo $[ $1 * $2 ]

}

function divem {

if [ $2 -ne 0 ]

then

echo $[ $1 / $2 ]

else

echo -1

fi

}

$

使用函数库的关键在于source命令。source命令会在当前shell上下文中执行命令,而不是创建一个新shell。可以用source命令来在shell脚本中运行库文件脚本。这样脚本就可以使用库中的函数了。

source命令有个快捷的别名,称作点操作符(dot operator)。要在shell脚本中运行myfuncs库文件,只需添加下面这行:

. ./myfuncs

这个例子假定myfuncs库文件和shell脚本位于同一目录。如果不是,你需要使用相应路径访问该文件。这里有个用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

$

该脚本成功地使用了myfuncs库文件中定义的函数。

在命令行上创建函数

可以在命令行上直接定义一个函数。有两种方法。

一种方法是采用单行方式定义函数。

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

$ divem 100 5

20

$

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

$ function multem {

> echo $[ $1 * $2 ]

> }

$ multem 2 5

10

$

注意在命令行定义函数时不要和系统的内建命令重复,否则函数会覆盖之前的内建命令。

.bashrc文件

bash shell在每次启动时都会在主目录下查找这个文件,不管是交互式shell还是从现有shell中启动的新shell。所以把函数定义在这个文件结尾,就可以在命令行和脚本中直接使用,也不需要在脚本中使用source或者点操作符操作库文件。

函数库实例

可以在网上找别人写好的函数库拿来直接用,这样更节省时间

下载库shtool软件包的下载地址是:

ftp://ftp.gnu.org/gnu/shtool/shtool-2.0.8.tar.gz

完成复制操作后,使用tar命令提取文件。

tar -zxvf shtool-2.0.8.tar.gz

该命令会将打包文件中的内容提取到shtool-2.0.8目录中。接下来就可以构建shell脚本库文件。配置工作必须使用标准的configure和make命令

$ ./confifgure

$ make

configure命令会检查构建shtool库文件所必需的软件。一旦发现了所需的工具,它会使用

工具路径修改配置文件。

make命令负责构建shtool库文件。最终的结果(shtool)是一个完整的库软件包。你也可

以使用make命令测试这个库文件。

$ make test

Running test suite:

echo...........ok

mdate..........ok

table..........ok

prop...........ok

。。。。

$

如果全部通过测试,就可以将库安装到Linux系统中

的公用位置,这样所有的脚本就都能够使用这个库了。要完成安装,需要使用make命令的

install选项。

菜单

创建菜单布局

clear命令用当前终端会话的terminfo数据来清理出现在屏幕上的文本。运行

clear命令之后,可以用echo命令来显示菜单元素。

echo用-e选项可以显示非可打印字符,例如制表符和换行符。

echo -e "1.\tDisplay disk space"

会生成如下输出行:

  1. Display disk space

最后一行的-en选项会可以掉末尾的换行符。

clear

echo

echo -e "\t\t\tSys Admin Menu\n"

echo -e "\t1. Display disk space"

echo -e "\t2. Display logged on users"

echo -e "\t3. Display memory usage"

echo -e "\t0. Exit menu\n\n"

echo -en "\t\tEnter option: "

read -n 1 option

在read命令中用了-n选项来限制只读取一个字符。这样用户只需要输入一个

数字,也不用按回车键:

read -n 1 option

接下来,你需要创建自己的菜单函数。

还有一点有助于制作shell脚本菜单,那就是将菜单布局本身作为一个函数来创建。

function menu {

clear

echo

echo -e "\t\t\tSys Admin Menu\n"

echo -e "\t1. Display disk space"

echo -e "\t2. Display logged on users"

echo -e "\t3. Display memory usage"

echo -e "\t0. Exit program\n\n"

echo -en "\t\tEnter option: "

read -n 1 option

}

这样一来,任何时候你都能调用menu函数来重现菜单。

创建菜单图像化界面工具有很多,

有dialog程序,

对KDE桌面来说,kdialog程序,

对GNOME桌面来说,有gdialog和zenity程序

sed和gawk

sed基本用法

记住,sed编辑器不会修改原始文件。所做的任何修改只是从sed编辑器的输出中修改,并不会修改源文件

sed命令的格式如下。

sed options script file

选项允许你修改sed命令的行为,可以使用的选项已在下面列出。

sed命令选项

选 项   描 述

-e     script 在处理输入时,将script中指定的命令添加到已有的命令中

-f      file 在处理输入时,将file中指定的命令添加到已有的命令中

-n     不产生命令输出,使用print命令来完成输出

常用的使用方式

sed 's/dog/cat/' data1.txt  #修改文件中所有的dog为cat。但不会修改源文件

在sed命令行上执行多个命令时,只要用-e选项

$ sed -e 's/brown/green/; s/dog/cat/' data1.txt

两个命令都作用到文件中的每行数据上。命令之间必须用分号隔开,并且在命令末尾和分号之间不能有空格。

sed从文件中读取编辑器命令

文件结尾使用.sed便于查看是什么文件。

每行都是一条单独的命令

在sed命令中用-f选项来指定文件。

$ cat script1.sed

s/brown/green/

s/fox/elephant/

s/dog/cat/

$

$ sed -f script1.sed data1.txt

The quick green elephant jumps over the lazy cat.

The quick green elephant jumps over the lazy cat.

The quick green elephant jumps over the lazy cat.

The quick green elephant jumps over the lazy cat.

$

在这种情况下,不用在每条命令后面放一个分号。sed编辑器知道每行都是一条单独的命令。跟在命令行输入命令一样,sed编辑器会从指定文件中读取命令,并将它们应用到数据文件中的每一行上。

如果一行存在多个匹配的词,默认只会替换第一个,本行其余的匹配词不做修改。

替换标记(数字,g,p,w)

基本命令格式

s/pattern/replacement/flags

命令中的/可以用任意符号替代。

s#pattern#replacement#flags

有4种可用的替换标记:

 数字,表明新文本将替换第几处模式匹配的地方;

 g,表明新文本将会替换所有匹配的文本;

 p,表明原先行的内容要打印出来;

 w file,将替换的结果写到文件中。

数字的使用方法

$ sed 's/test/trial/2' data4.txt

This is a test of the trial script.

This is the second test of the trial script.

$

g的使用方法

$ sed 's/test/trial/g' data4.txt

This is a trial of the trial script.

This is the second trial of the trial script.

$

p的使用方法,和-n选项一同使用,n选项是禁止sed编辑器输出,P表示只打印修改那行

$ cat data5.txt

This is a test line.

This is a different line.

$

$ sed -n 's/test/trial/p' data5.txt

This is a trial line.

$

w替换标记会产生同样的输出,不过会将输出保存到指定文件中。

$ sed 's/test/trial/w test.txt' data5.txt

This is a trial line.

This is a different line.

$

$ cat test.txt

This is a trial line.

$

会把替换的行保存到test.txt中

匹配某些行做替换(s###)

在sed编辑器中有两种形式的行寻址:

 以数字形式表示行区间

 用文本模式来过滤出行

两种形式都使用相同的格式来指定地址:

[address]command

也可以将特定地址的多个命令分组:

address {

command1

command2

command3

}

数字形式的行寻址(ns###)

指定行号的例子。

$ sed '2s/dog/cat/' data1.txt

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy cat

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

$

指定行地址区间的例子(n,ns###)

$ sed '2,3s/dog/cat/' data1.txt

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy cat

The quick brown fox jumps over the lazy cat

The quick brown fox jumps over the lazy dog

$

从某行开始的所有行,可以用特殊地址——美元符(n,$s###)

$ sed '2,$s/dog/cat/' data1.txt

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy cat

The quick brown fox jumps over the lazy cat

The quick brown fox jumps over the lazy cat

$

使用文本模式过滤器

格式如下:

/pattern/command

必须用正斜线将要指定的pattern封起来

只修改用户Samantha的默认shell,可以使用sed命令。

$ grep Samantha /etc/passwd

Samantha:x:502:502::/home/Samantha:/bin/bash

$

$ sed '/Samantha/s/bash/csh/' /etc/passwd

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

[...]

Christine:x:501:501:Christine B:/home/Christine:/bin/bash

Samantha:x:502:502::/home/Samantha:/bin/csh

Timothy:x:503:503::/home/Timothy:/bin/bash

$

多条命令合并使用

这里举例了一个指定行修改的例子,其他使用方法和数字形式行寻址一样。

$sed '2{

> s/fox/elephant/

> s/dog/cat/

> }' data1.txt

The quick brown fox jumps over the lazy dog.

The quick brown elephant jumps over the lazy cat.

The quick brown fox jumps over the lazy dog.

The quick brown fox jumps over the lazy dog.

$

指定地址区间

$ sed '3,${

> s/brown/green/

> s/lazy/active/

> }' data1.txt

删除(d)

使用匹配模式时,如果文本中包含多个匹配文字,会多次打开编辑,并不是编辑一次,

使用d命令,如果不加入寻址模式的话,会把sed流编辑器中的所有文本删除。

$ cat data1.txt

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

$

$ sed 'd' data1.txt

$

从数据流中删除特定的文本行,通过行号指定:

$ cat data6.txt

This is line number 1.

This is line number 2.

This is line number 3.

This is line number 4.

$

$ sed '3d' data6.txt

This is line number 1.

This is line number 2.

This is line number 4.

$

指定行区间

$ sed '2,3d' data6.txt

指定某行到文件结尾

$ sed '3,$d' data6.txt

sed编辑器的文本模式匹配特性也适用于删除命令。

$ sed '/number 1/d' data6.txt

This is line number 2.

This is line number 3.

This is line number 4.

$

插入和附加行(i,a)

格式如下:

sed '[address]command\new line'

插入(insert)命令(i)会在指定行前增加一个新行;

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

Test Line 1

Test Line 2

$

附加(append)命令(a)会在指定行后增加一个新行。

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

Test Line 2

Test Line 1

$

指定行插入

$ sed '3i\This is an inserted line.' data6.txt

指定行附加

$ sed ‘3a\This is an append line.’ data6.txt

添加到行尾使用$符号

$ sed '$a\This is a new line of text.' data6.txt

修改行(c)

指定第三行修改

$ 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.

$

使用文本匹配模式

$ sed '/number 3/c\This is a changed line of text.' data6.txt

转换命令(y)

转换命令格式如下。

[address]y/inchars/outchars/

使用转换命令的简单例子。

$ sed 'y/123/789/' data8.txt

This is line number 7.

This is line number 8.

This is line number 9.

This is line number 4.

This is line number 7 again.

This is yet another line.

This is the last line in the file.

$

sed编辑器转换了在文本行中匹配到的字符1的两个实例。你无法限定只转换在特定地方出现

的字符。

# echo "this is a 1 or 2 or 1" |sed 'y#123#jkl#'

this is a j or k or j

p打印行命令(与p标记不同)

打印命令最常见的用法是打印包含匹配文本模式的行。

$ cat data6.txt

This is line number 1.

This is line number 2.

This is line number 3.

This is line number 4.

$

$ sed -n '/number 3/p' data6.txt

This is line number 3.

$

打印行号“=”

每次数据流中出现一个换行符,就认为一行文本结束

$ cat data1.txt

The quick brown fox jumps over the lazy dog.

The quick brown fox jumps over the lazy dog.

$ sed '=' data1.txt

1

The quick brown fox jumps over the lazy dog.

2

The quick brown fox jumps over the lazy dog.

多条命令一起使用

$ sed -n '/number 4/{

> =

> p

> }' data6.txt

4

This is line number 4.

$

列出行

命令(l)可以打印数据流中的文本和不可打印的ASCII字符。任何不可打印字符要么在其八进制值前加一个反斜线,要么使用标准C风格的命名法(用于常见的不可打印字符),比如\t,来代表制表符。

$ cat data9.txt

This line contains tabs.

$

$ sed -n 'l' data9.txt

This\tline\tcontains\ttabs.$

$

如果数据流包含了转义字符,列出命令会在必要时候用八进制码来显示。

$ cat data10.txt

This line contains an escape character.

$

$ sed -n 'l' data10.txt

This line contains an escape character. \a$

$

data10.txt文本文件包含了一个转义控制码来产生铃声。当用cat命令来显示文本文件时,你看不到转义控制码,只能听到声音(如果你的音箱打开的话)。但是,利用列出命令,你就能显示出所使用的转义控制码。

sed中(r)读取文件并插入另一个文件命令

sed会把data12.txt中的文本插入到data6.txt文本的第3行后面。

$ 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.

$

文本匹配模式的用法

sed ‘/number 3/r data12.txt’ data6.txt

添加到行尾的用法

sed ‘$r data12.txt’ data6.txt

和删除命令配合使用,能在匹配的行后面添加内容,再删除匹配的内容

$ cat notice.std

Would the following people:

LIST

please report to the ship's captain.

$

$ sed '/LIST/{

> r data11.txt

> d

> }' notice.std

Would the following people:

Blum, R Browncoat

McGuiness, A Alliance

Bresnahan, C Browncoat

Harken, C Alliance

please report to the ship's captain.

$

把notice.std中的LIST替换为data11.txt的内容并删除

sed高级用法

单行处理命令next

单行使用小写n。先做匹配,然后用n移动到下一行,对下一行做处理。

单行next命令的使用,下面的文件中包含了2行空白,只删除第一行空白,保留第二行

$ cat data1.txt

This is the header line.

This is a data line.

This is the last line.

$

使用header匹配第一行,再用n命令移动到文本的下一行,进行处理。

$ sed '/header/{n ; d}' data1.txt

This is the header line.

This is a data line.

This is the last line.

$

多行处理命令next

多行使用大写N,先匹配,然后再用N把下一行合并到这行,当作一行来处理。

$ 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 '/first/{ N ; s/\n/ / }' 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 '

> s/System Administrator/Desktop User/

> N

> s/System\nAdministrator/Desktop\nUser/

> ' data4.txt

如果删除上一行的空白

它会删除数据流中出现在第一行前的空白行。

$ cat data5.txt

This is the header line.

This is a data line.

This is the last line.

$

$ sed '/^$/{N ; /header/D}' data5.txt

This is the header line.

This is a data line.

This is the last line.

$

多行删除命令D

D,它只删除模式空间中的第一行。该命令会删除到换行符(含换行符)为止的所有字符。

用法:

sed 'N ; /System\nAdministrator/D' data4.txt

删除文本的前一个空白行

sed ‘/^$/{N;/System/D}’ data.txt

多行打印命令P

它只打印多行模式空间中的第一行。在模式空间中直到换行符为止的所有字符。

$ sed -n 'N ; /System\nAdministrator/P' data3.txt

On Tuesday, the Linux System

$

NDP命令组合在一起使用才能发挥奇效

保持空间(h、H、g、G、x)

sed编辑器的保持空间命令

命 令       描 述

h      将模式空间复制到保持空间

H      将模式空间附加到保持空间

g      将保持空间复制到模式空间

G      将保持空间附加到模式空间

x      交换模式空间和保持空间的内容

sed -n '/first/ {h ; p ; n ; p ; g ; p }' data2.txt

排除命令!

打印出不匹配header的行

sed -n '/header/!p' data2.txt

用!处理使用N时无法处理的最后一行数据

$ 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.

$

$ 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 Desktop Users should attend.

$

下面的命令可以反转打印文本内容,虽然tac也能做到。

sed -n '{1!G ; h ; $p }' data2.txt

分支b :label(循环)

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

[address]b [label]

address参数决定了哪些行的数据会触发分支命令。label参数定义了要跳转到的位置。如果没有加label参数,跳转命令会跳转到脚本的结尾。注意在命令行使用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行处跳过了两个替换命令。

$ sed '{/first/b jump1 ; s/This is the/No jump on/

> :jump1

> s/This is the/Jump here on/}' data2.txt

No jump on header line

Jump here on first data line

No jump on second data line

No jump on last line

$

上面的命令解释:

首先符合first的行会跳过s/This is the/No jump on/

跳转到jump1的命令块中,

:jump1后面就是jump1的命令块 即s/This is the/Jump here on/

还可以制作循环效果,下面的例子是每次删除一个逗号

$ echo "This, is, a, test, to, remove, commas." | sed -n '{

> :start

> s/,//1p

> b start

> }'

This is, a, test, to, remove, commas.

This is a, test, to, remove, commas.

This is a test, to, remove, commas.

This is a test to, remove, commas.

This is a test to remove, commas.

This is a test to remove commas.

^C

$

注意b前面没有匹配的模式,所以一直匹配成功,就一直持续跳到start标识的命令块。所以会一直循环,需要使用ctrl+c停止。

如果在b钱前面添加一个匹配模式,如果没有匹配到,就跳到脚本结尾。

$ echo "This, is, a, test, to, remove, commas." | sed -n '{

> :start

> s/,//1p

> /,/b start

> }'

This is, a, test, to, remove, commas.

This is a, test, to, remove, commas.

This is a test, to, remove, commas.

This is a test to, remove, commas.

This is a test to remove, commas.

This is a test to remove commas.

$

另一种方式是使用t命令

$ echo "This, is, a, test, to, remove, commas. " | sed -n '{

> :start

> s/,//1p

> t start

> }'

测试命令t

测试命令会根据替换命令的结果跳转到某个标签,如果替换命令成功匹配并替换了一个模式,测试命令就会跳转到指定的标签。如果未能匹配,测试命令就不会跳转

测试命令的使用格式。

[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如果成功替换了matched,就执行s/This is the/No match on/

替换命令&

可以使用&符号来替换刚才匹配到的单词。

给想匹配的单词增加双引号。

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

The "cat" sleeps in his "hat".

$

使用.通配符匹配了cat和hat,然后用&符号代替了cat和hat,并增加了双引号。

替换单独的单词

sed编辑器用圆括号来定义替换模式中的子模式,在替代模式中使用特殊字符来引用每个子模式。替代字符由反斜线和数字组成。数字表明子模式的位置。sed编辑器会给第一个子模式分配字符\1,给第二个子模式分配字符\2,依此类推。

(System)替换成了.

# echo "The (System) Administrator manual"|sed 's#(System)#.#'

The . Administrator manual

用替换模式的子模式标记了System,并替换成了.

# echo "The (System) Administrator manual"|sed 's#\(System\)#.#'

The (.) Administrator manual

用替换模式的子模式标记了System,并替换成了添加了一个User,添加在括号中

# echo "The (System) Administrator manual"|sed 's#\(System\)#\1 User#'

The (System User) Administrator manual

用替换模式的子模式标记了System,并替换成了添加了一个User,添加在括号外

# echo "The (System) Administrator manual"|sed 's#\((System)\)#\1 User#'

The (System) User Administrator manual

下面这个脚本在数字中插入了逗号,使用了label参数,

$ echo "1234567" | sed '{

> :start

> s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/

> t start

> }'

1,234,567

$

个人修改了一下,可以在每个数字后都添加逗号,共有两种方式

# echo "123456789" |sed '{

:start

s#\(.*[0-9]\)\([0-9]\{1\}\)#\1,\2#

t start

}'

1,2,3,4,5,6,7,8,9

# echo "123456789" |sed '{

:start

s#\(.*[0-9]\)\([0-9]\+\)#\1,\2#

t start

}'

1,2,3,4,5,6,7,8,9

如果看不懂过程可以使用P打印出来,

s#\(.*[0-9]\)\([0-9]\{1\}\)#\1,\2#p

gawk基本用法

gawk程序的基本格式如下:

gawk options program file

gawk选项

选 项          描 述

-F fs         指定行中划分数据字段的字段分隔符

-f file       从指定的文件中读取程序

-v var=value 定义gawk程序中的一个变量及其默认值

-mf N         指定要处理的数据文件中的最大字段数

-mr N         指定数据文件中的最大数据行数

-W keyword    指定gawk的兼容模式或警告等级

下面的例子在命令行上指定了一个简单的gawk程序脚本:

$ gawk '{print "Hello World!"}'

但是回车后你会发现,并没有什么输出,如果你尝试的输入一些文字,再回车,会发现只打印Hello word!原因在于没有在命令行上指定文件名,所以gawk程序会从STDIN接收数据。在运行这个程序时,它会一直等待从STDIN输入的文本。

如果你输入一行文本并按下回车键,gawk会对这行文本运行一遍程序脚本。跟sed编辑器一样,gawk程序会针对数据流中的每行文本执行程序脚本。由于程序脚本被设为显示一行固定的文本字符串,因此不管你在数据流中输入什么文本,都会得到同样的文本输出。

要终止这个gawk程序,你必须表明数据流已经结束了。bash shell提供了一个组合键来生成

EOF(End-of-File)字符。Ctrl+D组合键会在bash中产生一个EOF字符。这个组合键能够终止该gawk程序并返回到命令行界面提示符下。

取一行中的某个字段

默认情况下,gawk会将如下变量分配给它在文本行中发现的数据字段:

 $0代表整个文本行;

 $1代表文本行中的第1个数据字段;

 $2代表文本行中的第2个数据字段;

 $n代表文本行中的第n个数据字段。

下面的例子只取了文件中每一行的第一个字段。

$ cat data2.txt

One line of test text.

Two lines of test text.

Three lines of test text.

$

$ gawk '{print $1}' data2.txt

One

Two

Three

指定字段分隔符

-F选项可以指定字段分隔符

$ gawk -F: '{print $1}' /etc/passwd

root

bin

daemon

echo和gawk合用

$ echo "My name is Rich" | gawk '{$4="Christine"; print $0}'

My name is Christine

$

gawk从文件中读取编辑器命令

文件结尾使用.gawk便于查看是什么文件

每行都是一条单独的命令

$ cat script2.gawk

{print $1 "'s home directory is " $6}

$

$ gawk -F: -f script2.gawk /etc/passwd

root's home directory is /root

bin's home directory is /bin

daemon's home directory is /sbin

adm's home directory is /var/adm

lp's home directory is /var/spool/lpd

[...]

Christine's home directory is /home/Christine

Samantha's home directory is /home/Samantha

Timothy's home directory is /home/Timothy

$

gawk程序中引用变量不需要加$符号。

{print $1 "'s home directory is " $6}

也可以写成

{

text = "'s home directory is "

print $1 text $6

}

在处理数据前\后执行gawk脚本(比如为报告创建标题)

BEGIN关键字字允许你指定一个程序脚本,gawk会在读完数据前执行它。

$ gawk 'BEGIN {print "The data3 File Contents:"}

> {print $0}' data3.txt

The data3 File Contents:

Line 1

Line 2

Line 3

$

END关键字允许你指定一个程序脚本,gawk会在读完数据后执行它。

$ gawk 'BEGIN {print "The data3 File Contents:"}

> {print $0}

> END {print "End of File"}' data3.txt

The data3 File Contents:

Line 1

Line 2

Line 3

End of File

$

下面是一个完整的例子,同时使用了BEGIN和END关键字

$ cat script4.gawk

BEGIN {

print "The latest list of users and shells"

print " UserID \t Shell"

print "-------- \t -------"

FS=":"

}

{

print $1 " \t " $7

}

END {

print "This concludes the listing"

}

$

按照指定符号输出文本内容

awk -v OFS='\t' '{$1=$1;print $3,$6,$7}' data.txt

分别打印出$3,$6,$7,并以制表符作为分隔符

# echo "1 2 3 4 5 6 7 8 9"|awk -v OFS='\t' '{$1=$1;print $3,$5,$7}'

3       5       7

正则表达式

正则表达式匹配时区分大小写空格也是一个字符,可以使用空格和数字

甚至可以创建匹配多个连续空格的正则表达式模式。

纯文本匹配时,需要注意表达式多余数据内容不能匹配

$ echo "The book is expensive" | sed -n '/books/p'

$

匹配的是books,数据中是book,所以没有结果

$ echo "The books are expensive" | sed -n '/book/p'

The books are expensive

$

匹配的是book,数据中是books,所以可以匹配

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

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

.表示必须任意一个字符,如果点号位置没有字符,则不能匹配。匹配的是.前面的字符

*表示匹配0次或多次

.*能匹配任意多个字符

匹配0次或1次

+匹配1次或多次,至少1次

^表示从数据流中文本行的行首开始的模式。如果模式出现在行首之外的位置,正则表达式模式则无法匹配。会在每个由换行符决定的新数据行的行首检查模式。

$ cat data3

This is a test line.

this is another test line.

A line that tests this feature.

Yet more testing of this

$ sed -n '/^this/p' data3

this is another test line.

$

$表示从行尾查找。将这个特殊字符放在文本模式之后来指明数据行必须以该文本模式结尾

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

This is a good book

注意要想匹配,文本模式必须是行的最后一部分

下面的例子,尽管book在数据中,但数据不是以book结尾的,所以无法匹配。

$ echo "There are a lot of good books" | sed -n '/book$/p'

$

^$可以组合使用,

^$中间加字符可以匹配指定的行

$ cat data4

this is a test of using both anchors

I said this is a test

this is a test

$ sed -n '/^this is a test$/p' data4

this is a test

$

^$中间什么都不加可匹配空行,

$ cat data5

This is one test line.

This is another test line.

$ sed '/^$/d' data5

This is one test line.

This is another test line.

$

[]表示字符组,匹配该字符组中的一个,如果在[]中,字符前加^表示排除这些字符。

[0-24-6]表示匹配0-2和4-6中的一个,其余数字不匹配。

[Aa]表示匹配A或a

[^a-k]表示排除a-k,匹配其余字母。

sed –n ‘/[0-24-6]/p’ data.txt

sed -n '/[Yy]es/p'

特殊字符组

组         描 述

[[: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

echo "abc" | sed -n '/[[:digit:]]/p'

实例

  • 创建多个用户(使用.csv文件)

你不用为每个需要创建的新用户账户手动输入useradd命令,而是可以将需要添加的新用户账户放在一个文本文件中,然后创建一个简单的脚本进行处理。这个文本文件的格式如下:

userid,user name

$ cat users.csv

rich,Richard Blum

christine,Christine Bresnahan

barbara,Barbara Blum

tim,Timothy Bresnahan

$

我们将IFS分隔符设置成逗号,并将其放入while语句的条件测试部分。然后使用read命令读取文件中的各行。实现代码如下:

while IFS=’,’ read –r userid name

read命令会自动读取.csv文本文件的下一行内容,所以不需要专门再写一个循环来处理。当

read命令返回FALSE时(也就是读取完整个文件时),while命令就会退出。要想把数据从文件中送入while命令,只需在while命令尾部使用一个重定向符就可以了。

将各部分处理过程写成脚本如下。

$ cat test26

#!/bin/bash

# process new user accounts

input="users.csv"

while IFS=',' read -r userid name

do

echo "adding $userid"

useradd -c "$name" -m $userid

done < "$input"

$

  • 查找可执行文件

#!/usr/bin/env bash

# ******************************************************

# Author       : haoyongnan

# Version      : 1.0

# Last modified: 2011-07-17 03:33

# Email        : 920206524@qq.com

# Telephone    : 17600186556

# Filename     : checkXfile.sh

# Description  :

# ******************************************************

IFS=:

for folder in $PATH

do

for file in $folder/*

do

if [ -x $file ];then

echo "$fiel is X fiel"

fi

done

done

linux命令与编程笔记相关推荐

  1. linux命令行大全 笔记,分享|4 个 Linux 下的命令行笔记程序

    这些工具可以让你在 Linux 命令行下简单而有效地记录笔记和保存信息. 当你需要保存代码段或 URL.想法或引用时,可能会启动文本编辑器或使用桌面或基于 Web 的笔记记录工具.但那些不是你唯一的选 ...

  2. Linux多进程多线程编程笔记

    文章目录 Linux多进程多线程编程 一.多进程编程 1.fork 函数 2.exec 系列函数 3.wait.waitpid 函数 4.pipe 管道 5.信号量 5.1.semget 5.2.se ...

  3. linux和网络编程笔记

    第一部分.章节目录 3.4.1.程序的开始和结束 3.4.2.进程环境 3.4.3.进程的正式引入 3.4.4.fork创建子进程 3.4.5.父子进程对文件的操作 3.4.6.进程的诞生和消亡 3. ...

  4. linux命令行大全 笔记,《Linux命令行大全》读书笔记

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? shell就是一个程序,它接受从键盘输入的命令,然后把命令传递给操作系统执行.当使用图像用户界面时,我们需要一个叫做终端 ...

  5. 快乐的Linux命令行--读书笔记1

    相信很多没有系统了解过Linux的小伙伴会和我一样,在按照教程进行某些环境配置类工作时,遇到一些看上去令人一脸懵逼看不出意思的英文缩写组成的linux命令,并且经常对于linux系统的组织结构和操作方 ...

  6. Linux bash shell 编程笔记(基础篇)

    文章目录 Part.I Introduction Chap.I 传送门 Chap.II 基础知识 Chap.III 实操笔记 Part.II 程序设计控制结构 Chap.I if 分支程序 Chap. ...

  7. linux shell脚本编程笔记(三): 三种引号的区别

    双引号.单引号.反引号的区别 测试用例: OPDATE=`date -d '-1 day' +%Y%m%d` for i in $(seq 10) do FILEDATE=`date -d " ...

  8. linux三剑客-sed命令的学习笔记

    本文为在B站上学习linux命令的学习笔记,视频出处为https://www.bilibili.com/video/BV1244y1e73a?p=23&spm_id_from=pageDriv ...

  9. LINUX下Socket编程 函数格式详解

    你需要了解的一些系统调用: socket() bind() connect() listen() accept() send() recv() sendto() recvfrom() close() ...

最新文章

  1. angler前端框架_2019几大主流的前端框架,几款目前最热门的前端框架
  2. u-boot支持LCD显示(基于TQ2440)
  3. 一文通俗了解对抗生成网络(GAN)核心思想
  4. C++语言学习(十九)——C++类型识别
  5. 传统红色纹样图案背景|中式海报必备素材
  6. 解题报告 poj 1087
  7. linux菜鸟要飞-根目录
  8. 大数据智能运维平台方案-1
  9. 理解JS散度(Jensen–Shannon divergence)
  10. 小福利,excel的常用高阶函数介绍
  11. 【JS】json.stringify()与json.parse()的区别
  12. java随机生成26个字母_js随机生成26个大小写字母
  13. 三星自定义状态栏_三星s9状态栏隐藏方法,三星s9状态栏隐藏小技巧
  14. React报错 React Hook useEffect has a missing dependency: ‘obj‘
  15. Redis知识总结(四万字)
  16. MFC-CListCtrl重绘,添加按钮到单元格
  17. 常用转义字符例如amp的含义
  18. torch.diag()
  19. 电梯监控系统的服务器,电梯物联网及远程实时监控系统方案-20210712141708.docx-原创力文档...
  20. Mac更新系统后 删除文件后可用空间还变少了?

热门文章

  1. Fast Ray Tracing of Arbitrary Implicit Surfaces with Interval and Affine Arithmetic.CGF 2009
  2. 剖析春运抢票软件 功率最高90%?
  3. LRTimelapse Pro 5—延迟摄影制作软件
  4. watch与移动服务器的连接已中断,Apple Watch怎么解除与iPhone绑定配对?
  5. excel 获取系统时间 第二天不变
  6. PS网页设计教程XVIII——在Photoshop中设计优雅的乡村酒店或餐厅的网页布局
  7. 解决github unable to access ‘https://github.com/...: Failed to connect to github.com port 443
  8. 基于关联规则的推荐系统
  9. 油烟净化器和抽油烟机的区别在哪里?开餐馆应该安装哪个
  10. 算法001:合并两个有序的数组