Linux Shell 命令行及脚本编程

Linux 及 Linux Shell 简介

image

1.1.5 Linux的理念

  • 小即是美
  • 让程序只做好一件事
  • 可移植性比效率更重要
  • 一切皆文件
  • 使用Shell脚本来提高效率和可移植性
  • 避免使用可定制性低下的用户界面
  • 所有程序都是数据的过滤器

1.2 什么是Linux Shell

  • Shell 是一个用户程序,或是一个为用户与系统交互提供的环境。
  • Shell 是一个执行从标准输入设备读入命令的语言解释程序。
  • 当你登录活或打开控制台时Shell就会执行。
  • Shell 不是系统内核的一部分,但是它使用系统内核执行程序,创建文件等。
  • Shell进程会提供一个命令行提示符,普通用户用$作提示符,超级用户(root)用#作提示符。

Shell编辑和回调命令

命令 说明
CTRL + W 删除光标位置钱的单词
CTRL + U 清空行
Tab 自动补全文件名的单词
CTRL + R 搜索先前使用的命令
CTRL + C 中止当前命令
CTRL + D 退出登录
ECS + T 调换光标钱的两个单词
上/下箭头 删除光标位置钱的单词

当用户准备结束登陆对话进程时,可以输入logoutexitCTRL + D组合键结束登录。

1.3 Shell 的种类

  • Shell 有多种类型,最常用的有三种,Bourne(sh)C ShellKorn Shell
  • 使用 cat /etc/shells命令查看系统中所有可用的Shell。
命令 说明
cat /etc/shells 查看系统中所有可用的Shell
grep root /etc/passwd 查看用户使用的哪种Shell
echo $SHELL 查看用户使用的哪种Shell
ps -p $$ 查看用户使用的哪种Shell

1.5 Shell脚本是什么

  • Shell 脚本是Linux/Unix编程环境的重要组成部分。
  • Shell 脚本一般有以下及部分组成。
解释 示例
Shell关键字 例如:if…else、for do … done
Shell 命令 例如:export、echo、exit、pwd、return
Linux 命令 例如:date、rm、mkdir
文本处理功能 例如:awk、cut、sed、grep
函数 例如:通常函数吧一些常用的功能放在一起。例如,/etc/init.d 目录中的大部分或全部系统Shell脚本所使用的函数都包含在文件 /etc/init.d/functions中。
控制流语句 例如:例如 if…then…else 或 执行重复操作的Shell循环。

1.6 为什么使用Shell脚本

使用Shell的简单原因

  • 使用简单
  • 节省时间
  • 可以创建你自己的自动化工具和应用程序
  • 使系统管理任务自动化
  • 因为脚本经过很好的测试,所以使用脚本做类似配置服务或系统管理任务时,发生错误的机会将大大减少。

我们经常使用的脚本实例有

  • 监控你的Linux系统
  • 备份数据和创建快照
  • 创建邮件告警系统
  • 查找耗尽系统资源的进程
  • 查找是否所有的网络服务都正常运行等等。

1.7 创建你的第一个Shell 脚本

  • 一个Shell脚本就是一个包含ASCII文本的文件。

如果你像成功的写一个Shell脚本,你需要做一下三件事情。

  • 写一个脚本
  • 允许Shell执行它
  • 把它放在Shell可以找到的地方

1
2
3
#!/bin/bash (1)
# My First Script (2)
ls -l .* (3)
  • 脚本的第一行是很重要的,他是一个告诉Shell使用什么程序解释器的特别指示。
  • 上面例子中使用的是/bin/bash。如果使用其他脚本语言比如Perlawkpython等也同样使用这个机制。
  • 脚本的第二行是一个注释。每一行中出现在#符号后面的任何内容都将被bash忽略。
  • 默认情况下,Linux是不允许文件执行的(权限不足),使用下面命令赋予权限
1
$ chmod 755 myscript
权限 注释
777 全部权限
755 读写和执行的权限
700 脚本私有,只有你可以读写和执行
  • 切换到你保存脚本的目录,执行脚本。
    1
    $ ./ myscript

初识Linux Shell

2.1 Bash Shell

2.1.1 Bash 简介

  • Bash是一个与Bourne Shell兼容的、执行从输入设备或文件读取命令的命令语言解释器。
  • Bash 与原来的Unix sh Shell向后兼容,并且融合了一些有用的Korn ShellC Shell的特性。它相对于sh在编程和交互式使用两方面都做了功能改进。
  • Bash具有很好的一致性,它使用构建时发现编译平台特征的配置系统,因此可以构建在几乎任何一种Unix版本上。

2.1.2 Bash 提供的改进

  • Bash 语法是Bourne Shell 语法的一个改进版本。大多数情况下Bourne Shell脚本可以被Bash正常地运行。

2.2 Shell 在Linux环境中的角色

2.2.1 与登录Shell相关的文件

  • 用户登录时Bash将会使用以下初始化文件和启动脚本。
文件目录 解释
/etc/profile 系统级的初始化文件,定义了一些环境变量,由登录Shell调用执行
/etc/bash.bashrc/etc/bashrc 其文件名根据不同的Linux发行版本而异,每个交互式Shell的系统级的启动脚本,定义了一些函数和别名
/etc/bash.logout 系统级的登录Shell清理脚本,当登录Shell退出时执行,部分Linux发行版默认没有此文件
$HOME/.bash_profile$HOME/.bash_login$HOME/.profile 用户个人初始化脚本,由登录Shell调用执行。这三个脚本只有一个会被执行,按照此顺序查找,第一个才能在的将被执行。
$Home/.bashrc 用户个人的每个交互式Shell的启动脚本
$HOME/.bash_logout 用户个人的登录Shell清理脚本,当登录Shell退出时执行
$HOME/.inputrc 用户个人的由readline使用的启动脚本,定义了处理某些情况下的键盘映射

2.2.2 Bash 启动脚本

  • 在用户登录时自动执行的脚本主要用来设置一些环境变量,例如设置JAVA_HOME的路径。
目录 解释
/etc/profile 当用户在运行级别3登录系统时首先运行
/etc/profile.d /etc/profile运行时,会调用该目录下的脚本
$HOME/.bash_profile$HOME/.bash_login$HOME/.profile /etc/profile运行后第一个存在的被运行
$HOME/.bashrc 上述脚本的第一个运行后即调用此脚本
/etc/bashrc 将被$HOME/.bashrc调用运行
/etc/profile.d 此目录下的脚本将被/etc/bashrc/etc/bash.bashrc调用运行

Bash启动脚本主要设置的环境有

  • 设置环境变量PATHPSI
  • 通过变量EDITOR设置默认的文本编辑器
  • 设置默认的umask(文件或目录的权限属性)
  • 覆盖活移除不想要的变量或别名
  • 设置别名
  • 加载函数

2.2.3 定制自己的Bash登录脚本

2.2.4 Bash 退出脚本

当登录Shell退出时,如果$HOME/.bash_logout脚本存在的话,Bash会读取并执行脚本的内容,此脚本的主要用途:

  • 使用clear命令清理你的屏幕终端输出
  • 移除一些临时文件
  • 自动运行一些命令或脚本等

2.2.5 定制自己的Bash 退出脚本

2.2.6 有效的登录Shell路径

  • /etc/shells 是一个包含有效的登录Shell全路径名的文本文件,这个文件会被chsh命令(变更你的登录Shell)所使用也可被其他程序查询使用。比如ftp服务,查看etc/shells的内容。
1
2
3
4
5
6
7
8
$ cat /etc/shells
out:
/bin/sh
/bin/bash
/bin/nologin
/bin/tcsh
/bin/cs
/bin/ksh

你也可以使用which命令显示shell的全路径

1
2
3
$ which bash
out:
/bin/bash

2.3 SHell中的变量

2.3.1 Shell 中变量的类型

  • Shell中有两种变量的类型:系统变量(环境变量)和用户自定义的变量(本地变量或Shell变量)
  • 系统变量由Linux Bash Shell 创建和维护的变量,你可以通过修改系统变量,如PS1PATHLANGHISTSIZEISPLAY等,配置Shell的样式
  • 常用的系统变量(环境变量)
系统变量 含义
BASH_VERSION 保存bash实例的版本
DISPLAY 设置X display名字
EDITOR 设置默认的文本编辑器
HISTFILE 保存命令历史的文件名
HISTFILESIZE 命令历史文件所能包含的最大行数
HISTSIZE 记录在命令历史中的命令数
HOME 当前用户的主目录
HOSTNAME 你的计算机的主机名
IFS 定义Shell的内部字段分隔符,一般是空格符、制表符和换行符
PATH 搜索命令的路径。它是以冒号分隔的目录列表。Linux下的标准命令之所以能在Shell命令行下的任何路径直接使用,就是因为这些标准命令所在的目录的路径定义在了PATH变量中,Shell会在PATH环境变量指定的全部路径中搜索任何匹配的可执行文件
PS1 你的提示符设定
PWD 当前工作目录。由cd命令设置
SHELL 设置登录Shell的路径
TERM 设置你的登录终端的类型
TMOUT 用于Shell内建命令read的默认超时时间。单位为秒。在交互式的Shell中,此变量的值作为发出命令后等待用户输入的秒数,如果没有输入用户将会自动退出
  • 你可以添加上述变量到你账号的home目录下的初始化文件中,比如~/.bash_profile文件。这样每次登录系统时,这些变量会自动设置成你需要的值。
  • 使用env或者printenv查看当前Shell的所有系统变量。
1
2
3
4
5
6
7
8
$ env
或者
$ printenv
out:
USER=BENNY
LOGNAME=BENNY
HOME=/home/usr
...

2.3.2 如何自定义变量和给变量赋值

  • Shell中创建和设置变量是很简单的,其语法如下:
1
varName=varValue

使用=给变量赋值,输入的次序是:变量名赋值操作符赋予的值
赋值操作符=的周围不要有任何空格,比如下面的变量定义将会得到command not found的错误。

1
2
3
varName= varValue
varName =varValue
varName= varValue
  • 可以将任意字符集合复制给一个变量
1
$ username="benny"
  • 或者
1
$ username=benny
  • 将一个数字复制给变量
1
$ var=1
  • 需要注意的是Shell的默认复制是字符串赋值
1
2
3
4
$ var=$var+1
$ echo $var
out:
1+1
  • Bash中,要将算数表达式的数值给一个变量,可以使用let命令
1
2
3
4
$ let var=2+1
$ echo $var
out:
3
  • 将一个变量的值直接复制给另一个变量,如下所示:
1
2
3
4
5
$ a=3
$ b=$a
$ echo $b
out:
3
  • 将命令的执行结果复制给变量,如下所示:
1
2
3
4
$ var=$(pwd)
$ echo $var
out:
/home/benny
  • Bash的内置命令read读入的内容复制给变量:
1
2
3
4
5
$ echo -n "Enter var:"; read var
Enter var: 此处需要你自己输入(比如输入520)
$ echo $var
out:
520

2.3.3 变量命名规则

  • 变量名必须以字母下划线字符_开头,后面跟字母、数字或下划线字符,第一个字符不能为数字。不要使用 *和其他特殊字符命名你的变量。
  • 变量名是大小写敏感的,比如定义几个变量
    1
    2
    3
    4
    5
    6
    7
    8
    $ echo $var
    out: 123
    $ echo $Var
    out: 1
    $ echo $vAR
    out: 2
    $ echo $VAR
    out: 3

2.3.4 实例:使用echo和printf 打印变量的值

  • 使用echo命令显示变量值,还可以使用printf命令显示变量值。
1
2
3
4
$ var=123
$ printf "%s\n" $var
out:
123
  • printf 命令的语法格式如下:
1
printf <FORMAT> <ARGUMENTS...>
  • 一个典型的 printf命令调用如下所示:
1
printf "FirstName" : %s\nLastName:%s" "$FIRSTNAME" "LASTNAME"
  • $FIRSTNAME是格式规范,而后面的两个变量则是作为参数传入。格式用字符串中的%s是指示打印参数的格式类型的分类符,这些分类符有不同的名字。
  • 分类符表
  • 参考书中page20
  • 转义字符表
  • 参考书中page20
  • printf命令不同,echo命令没有提供格式化选项,因此echo命令比printf命令简单易用
  • echo命令也提供转义字符的功能,可以是用转义字符与printf命令中的基本相同,但需使用-e选项激活转义字符功能。
1
2
3
4
5
6
7
$ var=10
$ echo "the number is $var"
out:
the number is 10
$ echo -e "Username: $USER\tHome directory:$HOME\n"
out:
Username:benny Home directory: /home/beny
  • 有时,你需要使用${}避免一些歧义
1
2
3
4
$ LOGDIR="/var/log/"
$ echo "the log file is $LOGDIRmessage"
out:
the log file is
  • Bash将尝试找一个LOGDIRmessages的变量,而不是$LOGDIR,为了避免这种歧义,我们需要使用${}语法,如下:
1
2
$ echo "the log file is ${LOGDIR}messages"
the log file is /var/log/messages

2.3.5 变量的引用

  • 引用一个变量的时候,最好使用双引号将变量名括起来。例如:`”$cariable”‘
  • 这样可以防止被引用的变量值中的特殊字符(除:$、'\)被解释为其他错误含义。
  • 使用双引号可以防止变量中的值中由多个单词租车发给你的字符串分离,一个双引号括起来的变量使它自身编程一个单一词组,即使值中包含空格。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@iZ251k7t70aZ var]# for var in $list
> do
> echo "$var"
> done
one
two
three
[root@iZ251k7t70aZ var]# for var in "$list"
> do echo "$var"
> done
one two three
  • 注意: 单引号的操作类似与双引号,但是它不允许引用变量,因为在单引号中字符'$'的特殊含义将会失效。每个特殊的字符,除了字符',都将按字面含义解释。
1
2
3
4
5
[root@iZ251k7t70aZ var]# var=123
[root@iZ251k7t70aZ var]# echo '$var'
$var
[root@iZ251k7t70aZ var]# echo "$var"
123

2.3.6 export

  • 使用export命令可以将变量被子Shell引用,可以使用export命令将变量进行输出

命令:export [-fnp] [变量或函数名称]=[变量设置值]

  • -f表示export的一个函数;-n表示将export属性从指定变量多函数上移除 p表示打印当前Shell所有输出的变量,与单独执行export命令结果相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@iZ251k7t70aZ var]# echo $$
17691
[root@iZ251k7t70aZ var]# java_home=/usr/local
[root@iZ251k7t70aZ var]# echo $java_home
/usr/local
[root@iZ251k7t70aZ var]# bash
[root@iZ251k7t70aZ var]# echo $$
17738
[root@iZ251k7t70aZ var]# echo $java_home
[root@iZ251k7t70aZ var]# exit
exit
[root@iZ251k7t70aZ var]# export java_home
[root@iZ251k7t70aZ var]# bash
[root@iZ251k7t70aZ var]# echo $$
17750
[root@iZ251k7t70aZ var]# echo $java_home
/usr/local
[root@iZ251k7t70aZ var]# bash
[root@iZ251k7t70aZ var]# echo $$
17762
[root@iZ251k7t70aZ var]# echo $java_home
/usr/local
[root@iZ251k7t70aZ var]#

系统变量会自动输出到后续命令的执行环境


2.3.7 如何删除变量

  • bash下使用unset命令来删除相应的变量或函数。unsert命令会自动

命令: unset [-fv] [变量或函数名称]

  • -f选项表示删除一个已定义的函数;-v选项表示删除一个变量
1
2
3
4
[root@iZ251k7t70aZ var]# echo $java_home
/usr/local
[root@iZ251k7t70aZ var]# unset java_home
[root@iZ251k7t70aZ var]# echo $java_home

使用unset命令不能删除一个只读的变量,否则将会出现类似如下的错误:

1
2
3
4
5
[root@iZ251k7t70aZ var]# readonly java_home=/usr/local
[root@iZ251k7t70aZ var]# echo $java_home
/usr/local
[root@iZ251k7t70aZ var]# unset java_home
bash: unset: java_home: cannot unset: readonly variable

2.3.8 如何检查变量是否存在

命令:${ varName? ERROR : The Varibale is not defined}

1
2
3
4
5
6
7
[root@iZ251k7t70aZ var]# JAVA_HOME=/usr/local
[root@iZ251k7t70aZ var]# echo ${JAVA_HOME?ERROR:The variable is not defined}
/usr/local
[root@iZ251k7t70aZ var]# unset JAVA_HOME
[root@iZ251k7t70aZ var]# echo ${JAVA_HOME?ERROR:The variable is not defined}
bash: JAVA_HOME: ERROR:The variable is not defined
[root@iZ251k7t70aZ var]#

2.4 Shell环境进阶

2.4.1 回调历史命令

1
2
3
4
5
6
7
8
9
[root@iZ251k7t70aZ var]# history
44 ls
45 ./startup.sh
46 cd /usr
47 ls
48 cd local/
49 ls
50 c dtom
51 cd tomcat7/
  • 在命令提示符下,可以通过CTRL + R 组合键输入相应的关键字可以搜索命令
  • Shell命令提示符下,可以简单的输入!!,来重复执行上一条执行过的命令
  • 你还可以回调最近一次执行的以指定字符开头的命令
1
2
3
4
5
[root@iZ251k7t70aZ var]# ls
account cache cvs db empty games lib local lock log mail nis opt preserve racoon run spool tmp www yp
[root@iZ251k7t70aZ var]# !l
ls
account cache cvs db empty games lib local lock log mail nis opt preserve racoon run spool tmp www yp
  • 你可以使用由history命令列出的列表的行号来重新调用响应的命令
1
2
3
4
5
6
7
8
9
10
11
[root@iZ251k7t70aZ var]# history
1052 ls -l
1053 clear
1054 ls
1055 ls
1056 clear
1057 history
[root@iZ251k7t70aZ var]# !1055
ls
account cache cvs db empty games lib local lock log mail nis opt preserve racoon run spool tmp www yp
[root@iZ251k7t70aZ var]#

2.4.2 Shell中的拓展

  • Shell中的拓展有8中,分别是

    • 大括号拓展
    • 波浪号拓展
    • 参数和变量拓展
    • 命令替换
    • 算数拓展
    • 进程替换
    • 单词拆分
    • 文件名拓展
  • 大括号拓展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@iZ251k7t70aZ var]# echo a{b,c,d}e
abe ace ade
[root@iZ251k7t70aZ var]# echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
[root@iZ251k7t70aZ var]# echo {0..10}
0 1 2 3 4 5 6 7 8 9 10
[root@iZ251k7t70aZ var]# echo {5..-3}
5 4 3 2 1 0 -1 -2 -3
[root@iZ251k7t70aZ var]# echo {g..a}
g f e d c b a
[root@iZ251k7t70aZ var]#
[root@iZ251k7t70aZ var]# echo {a..c}{1..3}
a1 a2 a3 b1 b2 b3 c1 c2 c3
[root@iZ251k7t70aZ var]# echo a{{b,c,d}a,{e,f,g}b,h}i
abai acai adai aebi afbi agbi ahi
[root@iZ251k7t70aZ var]#
  • 大括号可以拓展许多命令配合使用,使你的命令更简化
1
2
3
4
5
6
7
8
9
10
# 在当前文件夹下创建dir1 dir2 dir3
[root@iZ251k7t70aZ local]# ls
aegis bin etc filedirectroy games include lib lib64 libexec mvnreporsitory mysql pic sbin share src tomcat7
[root@iZ251k7t70aZ local]# mkdir {dir1,dir2,dir3}
[root@iZ251k7t70aZ local]# ls
aegis bin dir1 dir2 dir3 etc filedirectroy games include lib lib64 libexec mvnreporsitory mysql pic sbin share src tomcat7
[root@iZ251k7t70aZ local]#
# 在当前文件夹下创建 dir1 dir2 dir3
[root@iZ251k7t70aZ usr]# mkdir / {dir1,dir2}
  • Bash4.0中还提供给了一些大括号的新功能,比如在序列表达式中指定一个增量<INCR>

语法如下: {....}

1
2
$ echo {1..10..2}
1 3 5 7 9
  • 波浪号扩展可以用来指代你自己的主目录,或其他人的主目录
1
2
3
4
5
6
7
[root@iZ251k7t70aZ benny]# cd ~ #进入自己的主目录
[root@iZ251k7t70aZ ~]# pwd
/root
[root@iZ251k7t70aZ ~]# cd ~benny #进入benny的主目录
[root@iZ251k7t70aZ benny]# pwd
/home/benny
[root@iZ251k7t70aZ benny]#
  • Bash支持一下三种方式来实现文件名拓展

    • * 匹配任何字符串,包括空字符串
    • 匹配任意单个字符
    • [...]匹配方括号内的任意字符
  • 列出所有以字母a或b开头的配置文件

ls /etc/[ab]*.conf

创建和使用别名

  • 在Linux系统环境下,我们通常需要使用命令行来处理一些任务,并且会很频繁的使用某些命令语句,为了节省时间,我们可以在文件~/.bashrc中为这些命令语句创建别名。

一旦你修改了~/.bashrc文件,你必须重新启动Shell后,新的设置才会生效。

  • 语法如下:

alias name=’command’

+ name 用户自定义的用于别名的任意简短字符
+ command  任意linux命令
  • 打开当前目录下最后被修改的文件

alias Vim=’vim -ls -t | head -1’’

  • 找出当前目录下,5个最大的文件

alias findbig=’find . -type f -exec ls -s {} \;’ | sort -n -r | head -5’

  • 列出当前目录下所有文件,包括隐藏文件,并附加指示符和颜色表识

    alias ls=’ls -aF –color==always’

  • 清楚全部历史命令记录和屏幕

    alias hcl=’history -c; clear’

  • 查看磁盘控件使用情况

    alias dus=’df -h’

  • 切换到不同目录

    alias ..=’cd ..’
    alias …=’cd ../..’

  • alias命令查看所有别名

  • 查看一个特定的别名

    1
    2
    3
    4
    5
    6
    7
    [root@iZ251k7t70aZ /]# alias dus='df -h'
    [root@iZ251k7t70aZ /]# dus
    Filesystem Size Used Avail Use% Mounted on
    /dev/hda1 20G 7.7G 11G 42% /
    tmpfs 501M 0 501M 0% /dev/shm
    [root@iZ251k7t70aZ /]# alias dus
    alias dus='df -h'
  • 当你想调用实际的命令而暂时停止使用别名

    $ \aliasname

  • 删除一个别名 unalias

    unalias dus

  • 删除所有别名

    unalias -a

2.4.4 修改Bash提示符

此处没懂

2.4.5 设置Shell选项

  • set可以设置的Bash选项

语法: set(选项)(参数)

选项 注释
-a: 标示已修改的变量,以供输出至环境变量。
-b: 使被中止的后台程序立刻回报执行状态。
-C: 转向所产生的文件无法覆盖已存在的文件。
-d: Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。
-e: 若指令传回值不等于0,则立即退出shell。
-f: 取消使用通配符。
-h: 自动记录函数的所在位置。
-H Shell:可利用”!”加<指令编号>的方式来执行history中记录的指令。
-k: 指令所给的参数都会被视为此指令的环境变量。
-l: 记录for循环的变量名称。
-m: 使用监视模式。
-n: 只读取指令,而不实际执行。
-p: 启动优先顺序模式。
-P: 启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。
-t: 执行完随后的指令,即退出shell。
-u: 当执行时使用到未定义过的变量,则显示错误信息。
-v: 显示shell所读取的输入值。
-x: 执行指令后,会先显示该指令及所下的参数。
参数 状态 注释
allexport off 从设置开始标记所有新的和修改过的用于输出的变量
braceexpand on 允许符号扩展,默认选项
emacs on 在进行命令编辑的时候,使用内建的emacs编辑器, 默认选项
errexit off 如果一个命令返回一个非0退出状态值(失败),就退出.
errtrace off
functrace off
hashall on
histexpand on 在做临时替换的时候允许使用!和!! 默认选项
history on 允许命令行历史,默认选项
ignoreeof off 禁止coontrol-D的方式退出shell,必须输入exit。
interactive-comments on 在交互式模式下, #用来表示注解
keyword off 命令把关键字参数放在环境中
monitor on 允许作业控制
noclobber off 保护文件在使用重新动向的时候不被覆盖
noexec off 在脚本状态下读取命令但是不执行,主要为了检查语法结构。
nolog off
noglob off 禁止路径名扩展,即关闭通配符
notify off 在后台作业以后通知客户
nounset off 在扩展一个没有的设置的变量的时候, 显示错误的信息
onecmd off 在读取并执行一个新的命令后退出
physical off 如果被设置,则在使用pwd和cd命令时不使用符号连接的路径 而是物理路径
pipefail off 限制错误。还可以使用trap来截获信号
posix off 改变shell行为以便符合POSIX要求
privileged off 一旦被设置,shell不再读取.profile文件和env文件 shell函数也不继承任何环境
verbose off 为调试打开verbose模式
vi off 在命令行编辑的时候使用内置的vi编辑器
xtrace off 打开调试回响模式
  • 开启一个Bash命令,关闭Ctrl+d

set -o ignoreeof

  • 关闭一个Bash选项

set +o ignoreeof

  • 查看由Bash内只命令shopt控制的Bash选项及其状态
参数 状态
cdable_vars off
cdspell off
checkhash off
checkwinsize on
cmdhist on
dotglob off
execfail off
expand_aliases on
extdebug off
extglob off
extquote on
failglob off
force_fignore on
gnu_errfmt off
histappend off
histreedit off
histverify off
hostcomplete on
huponexit off
interactive_comments on
lithist off
login_shell on
mailwarn off
no_empty_cmd_completion off
nocaseglob off
nocasematch off
nullglob off
progcomp on
promptvars on
restricted_shell off
shift_verbose off
sourcepath on
xpg_echo off
  • 使用shopt命令开启和关闭Bash选项的语法如下

shopt -s feature-name # 开启一个Bash选项

shopt -u feature-name # 关闭一个bash选项

  • shopt命令,cdspell选项,用于检测cd命令中目录名字的拼写错误并纠正。错误检查包括调换的字符,缺少的字符,和重复的字符。
1
2
3
4
5
6
7
8
9
[root@iZ251k7t70aZ ~]# cd /var/lid
-bash: cd: /var/lid: No such file or directory
[root@iZ251k7t70aZ ~]# shopt -s cdspell
[root@iZ251k7t70aZ ~]# cd /var/lid
/var/lib
[root@iZ251k7t70aZ lib]# cd /var/lid
/var/lib
[root@iZ251k7t70aZ lib]# pwd
/var/lib

选项cdspell只在交互式Shell中有效

  • 你可以使用shopt和set为你定制一个Bash环境,编辑你的~/.bashrc文件,可以添加如下命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 纠正目录拼写
shopt -q -s cdspell
# 当终端创建口大小改变时,确保显示得到更新
shopt -q -s extglob
#开启扩展模式匹配特性
shopt -q -s extglob
# 退出时追加而不是重启命令历史
shopt -s histpapperd
# 使Bash尝试保存历史记录中多行命令的所有行
shopt -q -s cmdhist
# 得到后天任务结束的及时通知
set -o notify

实例:

  • 使用declare命令定义一个新的环境变量”mylove”,并且将其值设置为”java”,输入如下命令:

declare mylove=’Visual C++’ #定义新环境变量

  • 再使用set命令将新定义的变量输出为环境变量,输入如下命令:

set -a mylove #设置为环境变量

  • 执行该命令后,将会新添加对应的环境变量。用户可以使用env命令和grep命令分别显示和搜索环境变量”mylove”,输入命令如下:

env | grep mylove #显示环境变量值

你可以定制系统范围的Bash环境,默认情况下,文件/etc/profile作为Bash的系统范围用户参数文件,而在CentOS,FedoraRedhat下推荐的方法是使用目录/etc/profle.d中的文件。

3 常用Shell(Bash)命令

4 Shell 命令进阶

5 Shell 编程基础

5.1 Shell 脚本的第一行 "#!" (Shebang)

  • #!(Shebang)是一个有# !构成的字符序列,出现在脚本文件第一行的前两个字符,用于指示一个解释程序。
  • 语法格式:

#!INTERPRETER [OPTION]...
INTERPRETER必须是一个程序的绝对路径

  • 当一个内容经以#!开头的脚本作为一个程序运行时,程序加载器会将脚本第一行的#!之后的内容解析为一个解释程序,然互殴用这个指定的解释程序替代其运行,并将脚本的路径作为第一个参数传递给解释程序。
  • 例如一个脚本的路径名为path/to/script 并且它的内容如下行开头

#!/bin/sh  

  • 程序加载器被指示用解释程序/bin/bash替代其运行,并将路径path/to/script作为第一个参数传递给解释程序/bin/bash
  • 几乎所有的Bash脚本的内容都是以/bin/bash开头,并确保Bash将作为脚本的解释程序
  • 如果没有指定#!,则会默认用/bin/sh作为解释程序,但还是推荐你将Bash脚本的第一行设为#!/bin/bash

5.2 Shell 中的注释

  • Shell脚本中,#是注释表示符。
  • Shell脚本中, 还可以使用Bash的HERE DOCUMENT 特性添加多行的注释内容
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
echo "下面是注释"
<<COMMENT
comment line 1
comment line 2
comment line 3
COMMENT
echo "上面是注释"

5.3 设置脚本的权限和执行脚本

  • 在运行一个Shell脚本之前,确保Shell脚本文件具有可执行的权限,否则会报错permission denied
  • 给脚本添加执行权限

chmod u+x ./multicomments.sh

  • 给所有用户执行脚本的权限

chmod +x ./multicomments.sh

  • 运行一个Shell脚本,使用绝对路径相对路径两种方式都可以。

  • (1)绝对路径

1
2
$ /home/benny/scripts/helloworld.sh
hello world!
  • (2)相对路径
1
2
3
$ cd /home/benny
$ ./scripts/hellowrold.sh
hello world!
  • 如果想像运行一个命令一样运行一个脚本,即不需要指定绝对路径或相对路径只需要输入脚本名称即可。
  • 要实现这一目的,需要将脚本所在目录的路径添加到你的PATH环境变量中,那么就可以在任何路径下直接运行目录PATH环境变量中。
  • 例如:将目录路径home/benny/scripts加入PATH环境变量中,就可以在任何路径下直接运行目录home/benny/scripts下的Shell脚本。
1
2
3
4
$ export PATH=$PATH:/home/benny/scripts
$ cd /tmp
$ hellowrold.sh
out : hello world

通过export命令添加的PATH变量会在终端关闭后消失,所以建议通过编辑/etc/profile来改PATH环境变量,也可以改根目录下的.bashrc(即:~/.bashrc

5.4 Shell变量进阶

5.4.1 Bash中的参数拓展

5.4.2 Bash的内部变量

  • Bath的内部变量会影响Bash脚本的行为。

  • $BATH 用于引用Bash实例的全路径名

1
2
$ echo $BATH
/bin/bash
  • $HOME 当前用户的home目录
1
2
[root@iZ251k7t70aZ ~]# echo "your home directory is $HOME"
your home directory is /root
  • $IFS 是内部字段分隔符的缩写。此变量决定当Bath解析字符串时将怎样识别字段,或单词分界线。
  • 变量$IFS 的默认值是空格(空格、制表符和换行),但可以被修改。
1
2
3
4
[root@iZ251k7t70aZ ~]# set x y z #使用set命令,将x,y,z赋予位置参数1,2,3
[root@iZ251k7t70aZ ~]# IFS=":;-" #指定Bash的内部字段分隔符
[root@iZ251k7t70aZ ~]# echo "$*" #拓展特殊参数*
x:y:z
  • $SECONDS变量,脚本已经运行的秒数

  • $TMOUT变量,如果$TMOUT变量指定了一个非零的值,此值就会被内部指令read作为默认的超市秒数,在一个交互式的Shell中$TMOUT的值被作为命令行提示符等待输入的秒数,如果在指定的秒数内没有输入,Bash将自动被终结。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
echo "开始执行脚本"
set -o nounset
TMOUT=3
echo "Are you sure (y/n)"
read input
if ["$input" == "y"]
then
echo "Continue ...."
else
echo "Exit!"
fi
  • UID当前用户账号标识码(ID号)与/etc/passwd中记录的相同,此变量记录的是当前账户的真实ID,即使该账户通过su命令已经临时获得了另一个账号的权限,$UID是一个只读变量,不接受从命令行或脚本的修改。
  • 使用$UID变量来判断当前账号是否为root

5.4.3 Bash 中的位置参数和特殊参数

  • Bash中的位置参数事由除0以外的一个或多个数字表示的参数。
  • 位置参数事由ShellShell的函数呗引用时有ShelShell函数的参数赋值,并且可以使用Bash的内部命令set来重新赋值,位置参数可以被引用为${N},或当N只含有一个数字时被引为$N
1
2
3
[root@iZ251k7t70aZ myscript]# set 123 four five
[root@iZ251k7t70aZ myscript]# echo "$1 $2 $3 $4"
123 four five
  • 多于一个数字的位置参数在拓展时必须放在大括号中,比如${10}
  • 位置参数不能用过赋值语句来赋值,只能通过Bash的命令setshift来设置和取消他们,当shell函数运行时候,位置参数会被临时替换。
  • bash 对一些参数的处理比较特殊,这些参数只能被引用,但不能修改他们的值,这些特殊参数分别是*@#?-$!0_
参数 释义 更多
* 拓展为从1开始的所有位置参数
@ 也将拓展为从1开始的所有位置参数
# 拓展为位置参数的个数
? 拓展为最近一个在前台执行的命令的退出状态
- 拓展为当前的选项标志
$ 拓展为当前Shell的进程号
! 拓展为最近一次执行的后台命令的进程号
0 拓展为Shell或Shell脚本的名称
_ 在Shell启动时,它被设置为开始运行的shell或者Sehll脚本中
参数 释义 更多
$0 这个程式的执行名字
$n 这个程式的第n个参数值,n=1..9
$* 这个程式的所有参数,此选项参数可超过9个。
$# 这个程式的参数个数
$$ 这个程式的PID(脚本运行的当前进程ID号)
$! 执行上一个背景指令的PID(后台运行的最后一个进程的进程ID号)
$? 执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)
$- 显示shell使用的当前选项,与set命令功能相同
$@ 跟$*类似,但是可以当作数组用

5.4.4 使用declare命令指定变量的类型

  • declare命令是Bash的内部命令,用于声明变量和修改变量的属性,与Bash的另一个内部命令typeset的用法和用途完全相同

  • -r 选项,declare命令将吧指定的变量定义为只读变量,这些变量将不能再赋予新值或被清除

  • -i 选项,declare命令将吧指定的变量定义为整数型变量,赋予整形变量的任何类型的值都将被换成整数

  • -x 选项,declare命令将吧指定的变量通过环境输出到后续命令
  • -p 选项,declare命令将显示指定变量的属性和值

有时一个任务或命令会运行很长时间,如果不能确定这个任务什么时候才能结束,这是最好就是把它放到后台运行,然后一旦退出系统,这个任务将被终止

  • nohup 命令能让运行的命令或脚本在你退出系统后继续在后台运行。
  • 语法如下:

nohup COMMAND [ARG]… &

  • COMMAND:Shell脚本或命令的名称
  • [ARG]:脚本或命令的参数
  • &:nohup命令不能自动地将任务放在后台运行,你必须明确地在nohup命令的末尾添加操作控制符 &

使用nohup命令运行一个脚本script.sh

1
2
3
$ nohup sh script.sh &
[1] 12496
$ nohup : appending output to 'nohup.out'
  • 其中[1]是任务编号,12496是任务的进程号,最后一句表示当前脚本运行输出的内容都将被写入到但钱目录下的文件 nohup.out中。
  • 当你退出系统后在重新登陆,你仍会看到脚本script.sh在后台运行。
1
2
$ ps -ef | grep 12496
out:benny 12496 1 0 18:15? 00:00:00 sh script.sh

5.4.5 Bash 中的数组变量

  • 声明一个数组的语法:

ARRAYNAME[INDEX]=value

  • INDEX手机一正数,或是一个值为正数的算数表达式
  • 显式的声明一个数组变量使使用Bash的内部命令declare

$ declare -a ARRAYNAME

  • 带有一个索引编号的声明也是可以接受的,但索引编号将被忽略,数组的属性可以使用Bash的内部命令declare和readonly指定,这些属性将被应用到数组的所有变量。
  • 定一个数组变量

$ declare -a linux={‘java’,’php’,’javascript’}

  • 数组变量还可以使用复合赋值的格式

$ ARRAYNAME={value1,value2,value3…valueN}

  • 若要引用数中某一项的内容,必须使用{}如果索引编号是@*,那么数组的所有成员都将被使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ echo ${linux[@]}
out: java php javascript
$ arr1=(one two three)
$ echo ${arr1[0]} ${arr1[1]} ${arr1[2]}
out: one two three
$ echo ${arr1[*]}
out: one two three
$ echo ${arr1[@]}
out: one two three
$ arr1[3]=four
$ echo ${arr1[@]}
out:one two three four
$ echo $arr1
out:one
  • 如果引用数组时,不指定索引编号,则引用的将是数组中的第一元素,即使用索引编号为0
  • 使用unset命令可以删除一个数组或数组中的成员变量
1
2
3
4
5
6
7
8
9
$ unset arr1[2]
$ echo ${arr1[@]}
out: one two four
$ unset arr1
$ echo ${arr1[@]}
out:

5.5 Shell 算术运算

5.5.1 Bash的算数运算符

5.5.2 数字常量

5.5.3 使用算数拓展和let进行算数运算

5.5.4 使用expr命令

5.6 退出脚本

5.6.1 退出状态码

  • 每一个命令都会返回一个退出状态
状态值 释义
0 表示运行成功,程序执行未遇到任何问题
1 ~ 125 表示运行失败,脚本命令、系统命令错误或参数传递错误
126 找到了该命令但无法执行
127 未找到要运行的命令
> 128 命令被系统强行结束
  • Shell脚本和它里面的函数也会返回一个退出状态码。
  • 可以通过检查Bash的特殊变量$?来查看上一条命令的退出状态码
1
2
3
4
5
6
7
8
9
[root@iZ251k7t70aZ usr]# ls
a b bin etc games include java kerberos lib lib64 libexec local sbin share src tmp X11R6
[root@iZ251k7t70aZ usr]# echo $?
0
[root@iZ251k7t70aZ usr]# ls /benny
ls: /benny: No such file or directory
[root@iZ251k7t70aZ usr]# echo $?
2
[root@iZ251k7t70aZ usr]#

5.6.2 使用exit 命令

  • 语法如下:退出并且返回退出状态码
1
$ exit N
  • 退出状态码N可以被其他命令或脚本用来采取他们自己的行为,如果退出状态码N被省略,则将把最后一条运行的命令的退出状态作为脚本的退出状态码。
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
cd $SOME_DIR
if [ $? -eq 0 ]; then
rm -rf *
else
echo 'Cannot change directory!'
exit 1
fi
  • 上述例子,检查csd命令的退出状态,如果其不为0,将打印一个错误消息,并使用exit命令终结脚本运行,返回退出状态码1。
  • linux shell 命令中判断对文件和文件夹的判断,判断表达式

[ ] 部分是判断表达式,
-d 表示判断是否是目录(directory)
&& 是“逻辑与”操作符(这个与C语法类似啊),只有&&前面的判断成立(返回逻辑真),后面的语句才会得到执行。
总之,含义就是:
若/root/Desktop/为目录,就执行 chmod 777 /root/Desktop/download.desktop
若/root/Desktop/不是目录(不存在该目录),就执行 chmod 777 /root/桌面/download.desktop
其实就是处理 Desktop 是英文和中文两种情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
BAK=/data
TAPE=/dev/st0
echo "Trying to backup ${BAK} direcotry to type device ${TAPE}"
[! -d $BAK] &&{echo "Source backup directory $BAK not found"}
if [$? -ne 0]
then echo "An Error occurred while making a type backup"
exit 3
fi
exit 0 #如果备份成功,则返回0

** [里面的参数 还有待继续学习]

5.7 调试脚本

  • 常用的脚本调试方法是Bash-x 选项启动一个子Shell
  • 它将以调试模式运行,使Shell在执行脚本的过程中把实际执行的每一个命令显示出来,并且在每一个命令行的行首显示一个 +
  • +号后面显示的是经过了参数拓展之后的命令行的内容,有助于分析是什么命令。
1
2
3
4
5
6
7
8
[root@iZ251k7t70aZ myscript]# ./param.sh
this is /bin/bash
Linux iZ251k7t70aZ 2.6.18-371.11.1.el5 #1 SMP Wed Jul 23 15:12:55 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@iZ251k7t70aZ myscript]# bash -x param.sh
+ echo 'this is /bin/bash'
this is /bin/bash
+ uname -a
Linux iZ251k7t70aZ 2.6.18-371.11.1.el5 #1 SMP Wed Jul 23 15:12:55 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
  • Bash 中还有一个 -v 选项,该选项激活详细输出模式
  • 在此模式,由Bash读入的脚本的每一个命令行豆浆在执行前被输出。
  • 通常情况下,将-v选项和-x选项同时使用
1
2
3
4
5
6
7
8
9
[root@iZ251k7t70aZ myscript]# bash -xv param.sh
#!/bin/bash
echo "this is /bin/bash"
+ echo 'this is /bin/bash'
this is /bin/bash
uname -a
+ uname -a
Linux iZ251k7t70aZ 2.6.18-371.11.1.el5 #1 SMP Wed Jul 23 15:12:55 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@iZ251k7t70aZ myscript]#
  • -x选项虽然使用起来比较方便,但它输出的调试信息仅限于参数拓展后的每一条执行命令以及行首的一个+号,但却没有代码行的行号这样的重要信息。
  • 可以通过Bash的一些内部环境变量来增加-x选项的输出信息
内部环境变量 释义
$LINENO 表示Shell脚本的当前行号
$FUNCNAME 它是一个包含了当前在执行调用栈中的所有Shell函数名称的数组变量。${FUNCNAME[0]}代表正在执行的Shell函数的名称,${FUNCNAME[1]则代表调用函数${FUNCNAME[1]则代表调用函数${FUNCNAME[0]}的函数的名字,一次类推
$PS4 使用Bash-x选项时,每一条执行的命令的行首会显示+号,而这个+号其实就是变量$PS4的默认值
  • 利用变量$PS4的这一特性,结合上述另两个Bash内部变量,通过重新定义变量$PS4就可以增强-x选项的输出信息
1
$ export PS4='+{$LINENO:${FUNCNAME[0]}}'
  • 然后使用Bash -xv 选项来调试脚本
1
2
3
4
5
6
7
8
9
10
[root@iZ251k7t70aZ myscript]# export PS4='+{$LINENO:${FUNCNAME[0]}}'
[root@iZ251k7t70aZ myscript]# bash -xv param.sh
#!/bin/bash
echo "this is /bin/bash"
+{2:}echo 'this is /bin/bash'
this is /bin/bash
uname -a
+{3:}uname -a
Linux iZ251k7t70aZ 2.6.18-371.11.1.el5 #1 SMP Wed Jul 23 15:12:55 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@iZ251k7t70aZ myscript]#
  • Bash 中执行选项-n,用于测试Shell脚本中是否存在语法错误。

  • Bash 的内置变量总结

5.8 Shell脚本编程风格

  • 每个代码行不多于80个字符
  • 保持一直的缩进深度,程序结构的缩进应与逻辑嵌套深度一致
  • 每一个代码块之间留一个空行,可以提高脚本的可读性
  • 每个脚本文件都必须要有一个文件头注释,任何一个不简短而不显而易见的函数都需要注释
  • 文件头提供文件名和他的内容等一些信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
#
#=====================================
#
# FILE: param.sh
#
# USAGE: ./param.sh
#
# DESCRIPTION: Read the Bash variable $SECONDS
#
# OPTIONS: ----
#
# REQUIREMENTS:----
#
# BUGS:---
#
# NOTES:---
#
# ORGANIZATION:---
#
# CREATETED:---
#
# REVISION:---

6.1 Shell的条件执行–条件测试

6.1.1 使用test命令

  • Shell脚本可以使用条件逻辑,使脚本可以根据参数,Shell变量或是其他条件的值采取不同的行动。
  • test命令运行你做各种测试并每当测试成功或失败时设置它的退出状态码为0表示真)或1表示假
  • test命令可以用于:

    • 文件属性测试
    • 字符串测试
    • 算术测试
  • test命令的语法如下:

test EXPRESSION || [空格EXPRESSION空格]

[ EXPRESSION ] 注意: 表达式前后需要有空格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@iZ251k7t70aZ myscript]# test -d "$HOME"; echo $?
0
[root@iZ251k7t70aZ myscript]# test -d "$HOMEs"; echo $?
1
[root@iZ251k7t70aZ myscript]# [ "abc" != "def"]; echo $?
-bash: [: missing `]'
2
[root@iZ251k7t70aZ myscript]# ["abc" != "def" ]; echo $?
-bash: [abc: command not found
127
[root@iZ251k7t70aZ myscript]# [ "abc" != "def" ]; echo $?
0
[root@iZ251k7t70aZ myscript]# test 8 -gt 3 && echo TRUE || echo False
TRUE
[root@iZ251k7t70aZ myscript]# test -8 -gt 3 && echo TRUE || echo False
False
操作符 描述符
-e 文件存在
-a 文件存在,这个选项的效果与-e相同. 但是它已经被”弃用”了, 并且不鼓励使用.
-f 表示这个文件是一个一般文件(并不是目录或者设备文件)
-s 文件大小不为零
-d 表示这是一个目录
-b 表示这是一个块设备(软盘, 光驱, 等等.)
-c 表示这是一个字符设备(键盘, modem, 声卡, 等等.)
-p 这个文件是一个管道
-h 这是一个符号链接
-L 这是一个符号链接
-S 表示这是一个socket
-t 文件(描述符)被关联到一个终端设备上这个测试选项一般被用来检测脚本中的stdin([ -t 0 ]) 或者stdout([ -t 1 ])是否来自于一个终端.
-r 文件是否具有可读权限(指的是正在运行这个测试命令的用户是否具有读权限)
-w 文件是否具有可写权限(指的是正在运行这个测试命令的用户是否具有写权限)
-x 文件是否具有可执行权限(指的是正在运行这个测试命令的用户是否具有可执行权限)
-g set-group-id(sgid)标记被设置到文件或目录上,如果目录具有sgid标记的话, 那么在这个目录下所创建的文件将属于拥有这个目录的用户组, 而不必是创建这个文件的用户组. 这个特性对于在一个工作组中共享目录非常有用.
-u set-user-id (suid)标记被设置到文件上,如果一个root用户所拥有的二进制可执行文件设置了set-user-id标记位的话, 那么普通用户也会以root权限来运行这个文件. [1] 这对于需要访问系统硬件的执行程序(比如pppd和cdrecord)非常有用. 如果没有suid标志的话, 这些二进制执行程序是不能够被非root用户调用的.
-O 判断你是否是文件的拥有者
-G 文件的group-id是否与你的相同
-N 从文件上一次被读取到现在为止, 文件是否被修改过
f1 -nt f2 文件f1比文件f2新
f1 -ot f2 文件f1比文件f2旧
f1 -ef f2 文件f1和文件f2是相同文件的硬链接
! “非” – 反转上边所有测试的结果(如果没给出条件, 那么返回真).
  • 检查名命令文件/bin/cp是否存在,如果存在则打印找到此文件
1
2
3
4
5
[root@iZ251k7t70aZ myscript]# test -e /bin/cp && "the command $_ found " || "the command $_ not found"
-bash: the command /bin/cp found : No such file or directory
[root@iZ251k7t70aZ myscript]# [ -d /local ] && echo "真" || echo "假"

上述命令语句中的$_表示前一个执行的命令中的额最后一个参数。

  • 字符串测试操作符表
操作符 描述
-z 《String》 如果《String》为空则为真
-n 《String》 如果《String》不为空则为真
《String1》=《String2》 如果《String1》与《String2》相同则为真
《String1》!=《String2》 如果《String1》与《String2》不相同则为真
《String1》<《String2》 如果《String1》的字典顺序排在《String2》之前则为真
《String1》>《String2》 如果《String1》的字典顺序排在《String2》之后则为真
  • 算术测试操作符表
操作符 描述
-eq 等于,如:if [ “$a” -eq “$b” ]
-ne 不等于,如:if [ “$a” -ne “$b” ]
-gt 大于,如:if [ “$a” -gt “$b” ]
-ge 大于等于,如:if [ “$a” -ge “$b” ]
-lt 小于,如:if [ “$a” -lt “$b” ]
-le 小于等于,如:if [ “$a” -le “$b” ]
< 小于(需要双括号),如:((“$a” < “$b”))
<= 小于等于(需要双括号),如:((“$a” <= “$b”))
> 大于(需要双括号),如:((“$a” > “$b”))
>= 大于等于(需要双括号),如:((“$a” >= “$b”))

6.1.2 if结构的语法格式

  • if语句的基本语法:

if TEST-COMMANDS ; then CONSEQUENT-COMMANDS ; fi

if TEST-COMMANDS ; then
CONSEQUENT-COMMANDS
fi

if TEST-COMMANDS
then
CONSEQUENT-COMMANDS
fi

6.1.3 if…else…fi

  • if…else…fi的语法基本结构

if TEST-COMMANDS
then
CONSEQUENT-COMMANDS
else
CONSEQUENT-COMMANDS
fi

6.1.5 多级的if…elif…else…fi

  • if…elif…else…fi语法结构:

if TEST-COMMANDS
then
CONSEQUENT-COMMANDS
elif TEST-COMMANDS
then
CONSEQUENT-COMMANDS
elif TEST-COMMANDS
then
CONSEQUENT-COMMANDS
else
CONSEQUENT-COMMANDS
fi

6.2 条件执行

6.2.1 逻辑与&&

1
2
3
4
if [ on $var ] && [ -e $var ]
then
echo "..."
fi
  • test命令中我们可以使用-a 选项来表示逻辑与。
  • 将上述命令改写:
1
2
3
4
if [ -n $var -a -e $var ]
then
echo "..."
fi

6.2.2 逻辑或||

1
2
3
4
5
NOW='data +%a'
if [ "$NOW" = "MON" ] || [ "$NOW" = "SAT" ]
then
echo "..."
fi
  • 上述脚本中我们使用"[[ ]]" 代替"[]",那么次脚本会简介一些
1
2
3
4
5
NOW='data +%a'
if [[ $NOW = "MON" || $NOW = "SAT" ]]
then
echo "..."
fi
  • test命令的-a选项类似,我们可以使用test命令的-o选项来表示逻辑或
  • 将上述命令改写成:
1
2
3
4
5
6
# 定义变量NOW,并将计算得到的今天是星期几赋值给变量NOW
NOW='data +%a'
if [ "$NOW" = "MON" -o "$NOW" = "$SAT" ]
then
echo "..."
fi

6.2.3 逻辑非 “!”

1
2
3
4
5
6
7
if [! -d /home/benny]
then
mkdir /home/benny
else
echo "the directory is exist"
fi

6.3 case语句实例

  • case语句是多级的if…then…else…fi语句很好的替代方式。
  • 它可以让一个条件与多个模式相比较,而且case语句的结构的读写比较方便
  • case语句的语法实例
1
2
3
4
5
6
7
8
9
10
11
12
case EXPRESSION in
PATTERN 1 )
CONSEQUENT-COMMANDS
;;
PATTERN 2 )
CONSEQUENT-COMMANDS
;;
PATTERN N )
CONSEQUENT-COMAANDS
;;
esac

case语句结构一定要以"esac"结尾,每一个命令列表都以两个";;"为终结,只有最后一个命里该列表的";;"可以被省略。

  • 通过一个Linux下信号处理的脚本来学习case语句的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
# ===============================
# FILE: killsignal.sh
# USAGE: ./killsignal.sh
# DESCRIPTION:
# AUTHOR: benny
# CREATED: 10/24/2013
# ===============================
if [ $# -lt 2]
then
echo "..."
exit
fi
case "$1" in
1)
echo "..."
;;
2)
kill -SIGHUP $2
;;
*)
echo "前面的都没有匹配,则匹配此选项"
esac
  • 多重模式匹配的case语句的脚本实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash
# ===============================
# FILE: killsignal.sh
# USAGE: ./killsignal.sh
# DESCRIPTION:
# AUTHOR: benny
# CREATED: 10/24/2013
# ===============================
NOW='date +%a'
case $NOW in
# 若今天为星期一
Mon)
echo "FULL Backup"
;;
#若今天为星期二、星期三、星期四
Tue | Wed | Thu )
echo "Partial backup"
;;
# 若今天为星期六、星期天
Sat | Sun)
echo "no backup"
;;
*)
echo "notihing"
;;
exac

7.1 for循环

  • 首先,循环条件中使用的变量必须是已经初始化的,然后在循环中开始执行
  • 在每一次循环开始时,进行一次测试
  • 重复的执行一个代码块

7.1.1 for循环语法

  • 语法结构
  • for循环的基本语法结构如下:
1
2
3
4
5
6
7
for VAR in item1 item2 ... itemN
do
command1
command2
...
commandN
done
  • for循环变量的内容的语法:
1
2
3
4
5
6
7
for VAR in $fileName
do
command1
command2
...
commandN
done
  • for循环命令替换语法:
1
2
3
4
5
6
7
8
for var in $(Linux-command-name)
# 或者使用 for VAR in 'Linux-command-name'
do
command1
command2
...
commandN
done
  • 在for循环中,每次指定列表中的(item1…itemN) 新值被赋值给变量VAR后,for循环都会执行一次,它将重复的执行do和done之间的所有语句,知道条件不满足时为止。
  • 这些值通常都是
    • 字符串
    • 数字
    • 命令行参数
    • 文件名
    • Linux命令的输出
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
#=====================================
# FILE: killsignal.sh
# USAGE: ./killsignal.sh
# DESCRIPTION: 简单的使用for循环脚本
# AUTHOR: benny
# CREATED: 10/24/2013
#==========================
for i in 1 2 3 # 从1~3循环
do
echo "the for loop is run $i times"
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
#=====================================
# FILE: killsignal.sh
# USAGE: ./killsignal.sh
# DESCRIPTION: 使用变量内容的for循环脚本实例
# AUTHOR: benny
# CREATED: 10/24/2013
#==========================
filenames="/etc/yp.conf /etc/nsswitch.conf /etc/auto.master"
for file in $filenames
do
[ -f $file ] && echo "the file $file was found " || echo "the file is not found"
done
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
#=====================================
# FILE: killsignal.sh
# USAGE: ./killsignal.sh
# DESCRIPTION: 使用变量内容的for循环脚本实例
# AUTHOR: benny
# CREATED: 10/24/2013
#==========================
echo "Printing file list in /tmp diretroy:"
for file in 'ls /etm/*'
do
echo $file
done
  • for循环还有三项表达式语法:
1
2
3
4
5
6
7
for (( EXP1; EXP2; #XP3 ))
do
command1
command2
...
commandN
done
  • EXP1 初始化表达式
  • EXP2 循环测试活条件
  • EXP3 计算表达式

7.2 while 循环

7.2.1 while 循环语法

1
2
3
4
5
6
7
while [ Condition ]
do
Command1
Command2
...
CommandN
done
  • while循环示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
#=====================================
# FILE: killsignal.sh
# USAGE: ./killsignal.sh
# DESCRIPTION: 简单的使用while循环
# AUTHOR: benny
# CREATED: 10/24/2013
#==========================
var=1
while [ $var -lt 3 ]
do
echo "the for loop is run $var times"
var=$((var+1))
done

7.2.2 无限while循环

  • 定义一个无限while循环可以使用如下3种命令:
    • true 命令 —— 不做任何事,表示成功,返回退出状态码0
  • 语法格式:
1
2
3
4
5
6
7
8
9
10
while :
do
echo "..."
done
# 或
while true
do
echo "..."
done
# 或
  • 菜单驱动程序,持续运行知道用户按下”4”为止
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
while :
do
clear # 清理终端屏幕
echo "============="
echo " MAIN - MENU "
echo "============="
read -p "Enter your choice[ 1 - 4 ]:" choice # 从标准输入中读取用户的输入,并赋值给变量choice
case $choice in
1)
echo "Today is $(date + %Y-%m-%d)"
;;
2)
uname -a # 打印系统信息
;;
3)
w
read -p "Press [Enter] key to continue..." readEnterKey
;;
4)
echo "buy"
exit 0
;;
*)
echo "Error : Invalid option"
read -p "Presdss key to continue...." readEnterKey
;;
exac

7.3 until 循环语句实例

  • until循环与while循环类似,也同样基于一个条件,但until循环的判断条件正好与while循环的判断条件相反,until循环在条件为假的条件下才会持续的运行。一旦条件被满足,即为真,就会退出循环。
  • 语法如下:
1
2
3
4
5
6
7
until [ COMMANDS ]
do
command1
command2
...
commandN
done
  • until循环与while循环相比:
    • until 循环执行直到返回0状态
    • while 循环执行直到返回非0状态
    • until循环总是执行至少一次

7.4 select 循环语句

  • 语法格式
1
2
3
4
5
6
7
select VAR in LIST
do
command1
command2
...
commandN
done
  • select循环有以下特点

    • select语句使用Bash内部变量PS3的值作为它的提示符之一
    • 打印到屏幕上的列表LIST中的每一项会在前面加上一个数字编号
    • 当用户输入的数字与某一个数字编号一致时,列表中的相应的项即被赋予变量VAR
    • 如果用户输入的内容为空,将重新显示列表LIST中的项和提示符信息
    • 可以通过添加一个退出选项,或按CTRL+C 或 CTRL+D组合键退出select循环
  • 脚本实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
PS3="Run command:"
select choice in date w hostname "uname -a " Exit //指定select循环列表
do
case $choice in
date)
echo "=============="
echo "Current System date and time:"
ehco "================"
;;
w)
echo "=============="
echo "who is log on:"
ehco "================"
;;
hostname)
echo "=============="
echo "Hostname :"
ehco "================"
;;
"uname -a")
echo "=============="
echo "System information"
ehco "================"
;;
Exit)
echo "Bye!"
exit 0
;;
esac
done

7.5 循环控制

7.5.1 break 语句

  • break 语句用于从for、while、until、select循环中退出、停止循环的执行
  • 语法如下:
1
break [n]
  • n 代表嵌套循环的层级,如果指定了n,break将推出n级嵌套循环。
  • 如果没有指定n或n不大于等于1,则退出状态码为0,否则退出状态码为n
  • 此实例需要查看

7.5.2 continue 语句

  • continue 语句用于跳过循环体中剩余的命令直接跳转到循环体的顶部,而重新开始循环的下一次重复。
  • continue语句可以应用于for、while或until循环
  • 语法如下:
1
continue [n]

第8章 Shell函数

8.1 函数的定义

  • 函数语法:
1
2
3
4
5
6
7
8
9
# 函数名
function_name(){
# 函数体 ,在函数中执行的命令行
commands...
# 参数返回,return语句是可选的
#如果没有return语句,则以函数最后一条命令的运行结果作为返回值
#如果使用return语句,则return后跟数值n(数值范围:0~255)
[ return ini; ]
}
  • 或者如果你愿意,可以在函数名字前面加上关键字function
1
2
3
function function_name(){
commands...
}
  • 如果有function关键字,则可以省略圆括号"()"。函数体,也叫做符合命令块,是包含在{}之间的命令列表。

  • 在一行内定义一个函数,此时,函数体内的个命令之间必须使用分号;隔开,语法如下

1
2
3
4
5
function name { commands1; commands2;commandsN; }
或者
name() { commands1; commands2;commandsN; }
  • 可以使用内部命令unset的’-f’选线过来取消函数的定义

通常情况下,函数体外的大括号与函数体之间必须用空白符(空格、回车或制表符等)换行符分开,因为大括号{}是保留字,但只有{}与其中间的命令列表呗加空格或者其它Shell元字符(比如 , 或; 或|等)分隔时,才能被识别为保留字

8.2 函数的参数,变量与返回值

8.2.1 向函数传递参数

  • Shell函数有自己的命令行参数,函数使用特殊变量$1,$2,…,$n来范文传递给它的参数
1
2
3
4
5
6
name(){
arg1=$1
arg1=$2
arg1=$3
command on $arg1
}
  • 使用如下语法来调用函数
1
name foo bar
  • 上面的函数中

    • name = 函数名
    • foo = 参数1 传递给函数的第一个参数(位置参数$1)
    • bar = 参数2 传递给函数的第二个参数(位置参数$2)
  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/bin/bash
#=====================================
# FILE: passed.sh
# USAGE: ./passed.sh
# DESCRIPTION:
# AUTHOR: benny
# CREATED: 10/24/2013
#==========================
# 定义函数passed
passwed(){
# 定义变量a,将此传递给函数passed()的第一个参数赋值给此变量
a=$1
# 打印特殊函数0的值,即脚本名称
echo "passed(): \$0 is $0 "
# 打印位置参数1的值,即指定给函数的第一个参数
echo "passed() : \$1 is $1"
echo "passed() : \$a is $a"
# 打印传递给函数passed的参数个数
echo "passed() : total args is $#"
# 打印传递给函数passed()的所有函数
echo "passed() : total args is $@"
# 打印传递给函数passed()的所有函数
echo "passed() : total args is $*"
}
echo "**** calling passed() first time *****"
# 调用函数passwd()并指定一个参数'one'
passed one
echo "**** calling passed() second time *****"
passed one two three
out:
**** calling passed() first time *****
passed(): $0 is ./passed.sh
passed() : $1 is one
passed() : $a is one
passed() : total args is 1
passed() : total args is one
passed() : total args is one
**** calling passed() second time *****
passed(): $0 is ./passed.sh
passed() : $1 is one
passed() : $a is one
passed() : total args is 3
passed() : total args is one two three
passed() : total args is one two three
  • 在Shell函数中:

    • 所有函数参数都可以通过$1,$2,…,$N来访问
    • $0 指代Shell脚本的名字
    • $* $@ 保存传递给函数所有的参数
    • $# 保存村递给函数的位置参数的个数
  • 8.2.2 本地变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash
#=====================================
# FILE: fvar.sh
# USAGE: ./fvar.sh
# DESCRIPTION: 本地变量
# AUTHOR: benny
# CREATED: 10/24/2013
#==========================
# 定义函数create_logFile
create_logFile(){
#修改变量d的名字
d=$1
echo "create_logFile : d is set to $d"
}
# 定义变量d
d=/tmp/diskUsage.log
echo "Before calling create_logFile d is set to $d"
# 调用函数create_logFile并指定一个参数
create_logFile "/home /benny/diskUsage.log"
echo "After calling create_logFile d is set to $d"
out:
[root@iZ251k7t70aZ myscript]# ./fvar.sh
Before calling create_logFile d is set to /tmp/diskUsage.log
create_logFile : d is set to /home /benny/diskUsage.log
After calling create_logFile d is set to /home /benny/diskUsage.log
  • 通常情况下,我们可以使用local命令来创建一个本地变量
  • 语法
1
2
3
4
5
6
7
8
9
10
11
12
local var=value
local varName
或者
function name(){
# 定义一个本独变量var
local var=$1
command1 on $var
}
  • local命令只能在函数内部使用
  • local命令将变量名的可见范围控制在函数内部
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
# 定义全变量d
d=/tmp/diskUsage.log
#定义函数create_logFile
function create_logFile(){
# 定义本地变量,这个变量制度及此函数可见
local d=$1
echo "create_logFIle(): d is set to $d"
}
echo "Before caling create_logFIle d is set to $d"
# 调用函数create_logFile() 并指定一个参数
create_logFile "/home/benny/diskUsage.log"
echo "After calling create_log_file() d is set to $d"
~
out:
[root@iZ251k7t70aZ myscript]# ./localfvar.sh
Before caling create_logFIle d is set to /tmp/diskUsage.log
create_logFIle(): d is set to /home/benny/diskUsage.log
After calling create_log_file() d is set to /tmp/diskUsage.log

8.2.3 使用return命令

  • 如果函数理由Shell内置命令return,则函数执行到return语句时结束,并且返回到Shell脚本中调用函数位置的下一个命令。
  • 如果return带有一个数值型参数,则这个参数就是函数的返回值,返回值的最大值是255;否则函数的返回值就是函数体内最后一个执行的命令的返回状态
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[root@iZ251k7t70aZ myscript]# vi checkpid.sh
#!/bin/bash
checkpid(){
# 定义本地变量
local i
# 使用for循环遍历传递给此函数的所有参数
for i in $*
do
# 如果目录/proc/$i存在,则执行此函数返回
# 在一般的Linux系统中,如果进程正在运行,则在/proc目录下会存在一个以进程号命名的子目录
[ -d "/proc/$i" ] && return 0
done
# 返回1
return 1
}
checkpid $pid1 $pis2 $pid3
if [ $? == 0 ]
then
echo "the one of them is running"
else
echo "These Pids are not running"
fi
out:
[root@iZ251k7t70aZ myscript]# ./checkpid.sh
These Pids are not running
  • 上述中的if判断语句也可以改成,因为返回值是01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
checkpid $pid1 $pis2 $pid3
if [ $? == 0 ]
then
echo "the one of them is running"
else
echo "These Pids are not running"
fi
改成
if (checkpid $pid1 $pis2 $pid3) ; then
echo "the one of them is running"
else
echo "These Pids are not running"
fi

8.3 函数的调用

8.3.1 在Shell命令行调用函数

  • 在命令行中,可以通过直接输入函数的名字,来调用或引用函数
1
$ funciton_name
  • 定义yDay()函数来显示昨天的日期
1
[root@iZ251k7t70aZ myscript]# yDay() { date --date='1 day ago'; }
  • 引用函数dDay()
1
2
[root@iZ251k7t70aZ myscript]# yDay
Wed Jul 13 15:09:44 CST 2016

8.3.2 在脚本中调用函数

  • 要在脚本中调用该函数
    • 首先要创建函数
    • 确保函数位于调用此函数的语句之前
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# 定义变量TEST
TEST=" /tmp/fileName"
# 调用delete_file; 失败
delete_file
#定义函数delete_file
delete_file(){
echo "Deleting file...."
out:
[root@iZ251k7t70aZ myscript]# ./dfile.sh
./dfile.sh: line 7: delete_file: command not found
}
  • 出错的原因是因为脚本的执行顺序是从上而下运行
  • 调用了未定义的方法
  • 为了避免出现这样的问题,要在脚本的开头定义和编写函数
  • 改写一下上面的脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# 定义函数delete_file
function delete_file(){
echo "Delteting ...."
}
# 定义变量TEST
TEST=" /tmp/fileName"
# 调用delete_file;
delete_file

8.3.3 从函数文件中调用函数

  • 你可以把所有的函数存储到一个文件中
  • 你可以把所有订单而函数加载到当前脚本或者是命令行
  • 加载函数文件中所有函数的语法如下
1
2
3
4
5
$ . /path/to/your/functions.sh
或者
$ . functions.sh
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/bin/bash
#====================
# FILE: functions.sh
# USAGE:./functions.sh
# DESCRIPTION:
#====================
# 定义变量
declare -r TRUE=0
declare -r FLASE=1
declare -r PASSWD_FILE=/etc/passwd
################################
#用途: 将字符串转换成小写
#参数:
# $1 -> 要转换为小写的字符串
################################
function to_lower(){
# 定义本地变量str
local str="$@"
# 定义本地变量output
local output
# 将变量str的值转换为小写后赋值给变量output
output=$(tr '[A_Z]' '[a-z]'<<<"{str}")
echo $output
}
  • 加载一个函数文件到脚本中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@iZ251k7t70aZ myscript]# vi functiondemo.sh
#!/bin/bash
#============================
# FILE: functionDemo.sh
# AUTHOR: benny
# DESCRIPTION: 加载函数文件到脚本
# USAGE: ./funcionDemo.sh
#============================
# 加载函数文件 functions.sh
# 这里的路径需要根据你的手机环境做改动
. /usr/local/myscript/functions.sh
# 定义本地变量
# var1 是没有被function.sh使用个
var1="The manabharata is the longst and , arguably, one of the greatest epicpoems in any language"
# 调用函数is_root 执行成功或失败,会分别打印不同的信息
is_root && echo "you are logged in as root" || echo "you are not logged in as root"
# 调用函数is_user_exists
is_user_exists "benny" && e "Account found" || echo "Account not found"
# 打印变量的值
echo -e "*** orignal quote : \n${var1}"
# 调用函数to_lower()
# 将$var1 作为参数传递给to_lower()
# 将echo内使用命令替换
echo -e "*** Lowercase version : \n${to_lower $(var1)}"

8.3.4 递归函数调用

  • 递归函数是重复调用其自身的函数,并且没有递归调用次数的限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
factorial(){
# 定义本地变量
local i=$1
# 定义本地变量f
local f
# 声明变量i为整数
declare -i f
# factorial被调用直到$f的值<=2
# 开始递归
[ $i -le 2 ] && echo $i || {f=$((i-1));f=$(factorial $f);f=$(( f * i ));echo $f; }
}
# 显示函数的用户
[ $# -eq 0 ] && { echo "Usage : $0 number ";exit 1; }
# 调用函数factorial
factorial $1
  • 在Bash下,递归函数执行速度慢,应尽可能避免使用递归函数。

将函数放在后台运行

  • &操作符可以将命令放在后台运行并释放你的终端,同样也可以将函数放在后台运行
  • 语法规则
1
2
3
4
5
6
7
8
9
10
11
12
# 定义函数name
name(){
echo "Do something"
sleep 1
}
# 将函数放在后台运行
name &
# 继续执行其他命令
commmands...
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/bash
# prigress.sh 当进行备份时显示进度
# 定义函数progress
progress(){
echo -n "$0: please wait ...."
while true
do
echo -n "."
# 休眠5秒
sleep 5
done
}
# 定义函数dobackup
dobackup(){
# 运行备份命令
tar zcvf /dev/st0 /home >/dev/null 2>&1
}
# 将函数放在后台运行
progress &
# 保存函数 progress() 运行的进程号
# 需要使用PID来结束此函数
MYSELF=$!
echo "$MYSELF"
# 开始备份
# 转移控制到函数dobackup
dobackup
# 杀死进程
kill $MYSELF >/dev/null 2>&1
echo -n "...done"
echo
  • shell中可能经常能看到:>/dev/null 2>&1
    • 命令的结果可以通过%>的形式来定义输出
    • /dev/null 代表空设备文件
    • > 代表重定向到哪里,例如:echo "123" > /home/123.txt
    • 1 表示stdout标准输出,系统默认值是1,所以>/dev/null等同于1>/dev/null
    • 1 表示stderr标准错误
    • & 表示等同于的意思,2>&1,表示2的输出重定向等同于1
    • 1>/dev/null 首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,说白了就是不显示任何信息。
    • 2>&1 接着,标准错误输出重定向等同于标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。
Contents
  1. 1. Linux 及 Linux Shell 简介
    1. 1.1. 1.1.5 Linux的理念
    2. 1.2. 1.2 什么是Linux Shell
    3. 1.3. 1.3 Shell 的种类
    4. 1.4. 1.5 Shell脚本是什么
    5. 1.5. 1.6 为什么使用Shell脚本
    6. 1.6. 1.7 创建你的第一个Shell 脚本
  2. 2. 初识Linux Shell
    1. 2.1. 2.1 Bash Shell
    2. 2.2. 2.1.1 Bash 简介
    3. 2.3. 2.1.2 Bash 提供的改进
  3. 3. 2.2 Shell 在Linux环境中的角色
    1. 3.1. 2.2.1 与登录Shell相关的文件
    2. 3.2. 2.2.2 Bash 启动脚本
    3. 3.3. 2.2.3 定制自己的Bash登录脚本
    4. 3.4. 2.2.4 Bash 退出脚本
    5. 3.5. 2.2.5 定制自己的Bash 退出脚本
    6. 3.6. 2.2.6 有效的登录Shell路径
  4. 4. 2.3 SHell中的变量
    1. 4.1. 2.3.1 Shell 中变量的类型
    2. 4.2. 2.3.2 如何自定义变量和给变量赋值
    3. 4.3. 2.3.3 变量命名规则
    4. 4.4. 2.3.4 实例:使用echo和printf 打印变量的值
    5. 4.5. 2.3.5 变量的引用
    6. 4.6. 2.3.6 export
    7. 4.7. 2.3.7 如何删除变量
    8. 4.8. 2.3.8 如何检查变量是否存在
  5. 5. 2.4 Shell环境进阶
    1. 5.1. 2.4.1 回调历史命令
    2. 5.2. 2.4.2 Shell中的拓展
  6. 6. 创建和使用别名
  7. 7. 2.4.4 修改Bash提示符
  8. 8. 2.4.5 设置Shell选项
    1. 8.1. 实例:
  9. 9. 3 常用Shell(Bash)命令
  10. 10. 4 Shell 命令进阶
  11. 11. 5 Shell 编程基础
    1. 11.1. 5.1 Shell 脚本的第一行 "#!" (Shebang)
    2. 11.2. 5.3 设置脚本的权限和执行脚本
  12. 12. 5.4 Shell变量进阶
    1. 12.1. 5.4.1 Bash中的参数拓展
    2. 12.2. 5.4.2 Bash的内部变量
    3. 12.3. 5.4.3 Bash 中的位置参数和特殊参数
  13. 13. 5.4.4 使用declare命令指定变量的类型
  14. 14. 5.5 Shell 算术运算
    1. 14.1. 5.5.1 Bash的算数运算符
    2. 14.2. 5.5.2 数字常量
    3. 14.3. 5.5.3 使用算数拓展和let进行算数运算
    4. 14.4. 5.5.4 使用expr命令
  15. 15. 5.6 退出脚本
    1. 15.1. 5.6.1 退出状态码
    2. 15.2. 5.6.2 使用exit 命令
  16. 16. 5.7 调试脚本
  17. 17. 5.8 Shell脚本编程风格
  18. 18. 6.1 Shell的条件执行–条件测试
    1. 18.1. 6.1.1 使用test命令
    2. 18.2. 6.1.2 if结构的语法格式
    3. 18.3. 6.1.3 if…else…fi
    4. 18.4. 6.1.5 多级的if…elif…else…fi
  19. 19. 6.2 条件执行
    1. 19.1. 6.2.1 逻辑与&&
    2. 19.2. 6.2.2 逻辑或||
    3. 19.3. 6.2.3 逻辑非 “!”
  20. 20. 6.3 case语句实例
  21. 21. 7.1 for循环
    1. 21.1. 7.1.1 for循环语法
  22. 22. 7.2 while 循环
    1. 22.1. 7.2.1 while 循环语法
    2. 22.2. 7.2.2 无限while循环
  23. 23. 7.3 until 循环语句实例
  24. 24. 7.4 select 循环语句
  25. 25. 7.5 循环控制
    1. 25.1. 7.5.1 break 语句
    2. 25.2. 7.5.2 continue 语句
  26. 26. 第8章 Shell函数
  27. 27. 8.1 函数的定义
  28. 28. 8.2 函数的参数,变量与返回值
    1. 28.1. 8.2.1 向函数传递参数
  29. 29. 8.2.3 使用return命令
  30. 30. 8.3 函数的调用
    1. 30.1. 8.3.1 在Shell命令行调用函数
    2. 30.2. 8.3.2 在脚本中调用函数
    3. 30.3. 8.3.3 从函数文件中调用函数
    4. 30.4. 8.3.4 递归函数调用
    5. 30.5. 将函数放在后台运行