Linux 中 Shell 脚本基础知识

本篇博客介绍下 Linux Shell 脚本编写中常用的语法知识。

Shell简介

Shell 是顺序执行的命令列表

bash shell 的命令分为两类:外部命令和内部命令。外部命令是通过系统调用或独立的程序实现的,如sed、awk等等。内部命令是由特殊的文件格式(.def)所实现,如cd、history、exec等等。

#!/bin/bash的含义

#!/bin/bash 指此脚本使用 /bin/bash 来解释执行,#! 是特殊的表示符,其后跟的是解释执行此脚本的 shell 的路径。

$参数的含义

  • $$ 是脚本运行的当前进程ID号
  • $# 表示传递给脚本的参数个数
  • $! 是最后执行的后台命令的PID
  • $0 是脚本本身的名字
  • $1 是传递给该Shell脚本或函数的第一个参数
  • $2 是传递给该Shell脚本或函数的第二个参数
  • $n 是传递给该Shell脚本或函数的第n个参数
  • $? 显示最后命令的退出状态,0表示没有错误,其他表示有错误
  • $@ 表示传递给该脚本或函数的所有参数的列表
  • $* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过九个。

关于 @* 的异同:

相同点:都是引用所有参数

不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数,分别存储在1、2、3,则“@”等价于“1”、“2”、“3”(传递了三个参数);而“*”表示“123”(传递了一个参数)

getopts 命令

内部命令 getopts可以很方便地处理命令行参数,其一般格式为:

1
getopts options variable

getopts 的设计目标是在循环中运行,每次执行循环,getopts就检查下一个命令行参数,并判断它是否合法,即检查参数是否以“-”开头,后面跟一个包含在options中的字母。如果是,就把匹配的选项字母存储在指定的变量variable中,并返回退出状态0;如果“-”后面的字母没有包含在options中,就在variable中存入一个“?”,并返回退出状态0;如果命令行中已经没有参数,或者下一个参数不以“-”开头,就返回不为0的退出状态。

注意以下几点:

1、getopts允许把选项堆叠在一起(如-ms)。

2、如果要带参数,须在对应选项后加“:”(如h后需要加参数h:ms),此时选项和参数之间至少有一个空白字符分隔,这样的选项不能堆叠。

3、如果在需要参数的选项之后没有找到参数,它就在给定的变量中存入“?”,并向标准错误输出中写入错误信息;否则将实际参数写入特殊变量:OPTARG。

4、另外一个特殊变量:OPTIND,反映下一个要处理的参数索引,初值是1,每次执行getopts时都会更新。

local

一般用于局部变量声明,多在函数内部使用。

1、Shell脚本中定义的变量是全局变量,其作用域从被定义的地方开始,到Shell脚本结束或被显式删除处为止。

2、Shell函数定义的变量默认是全局变量,其作用域从函数被调用时执行变量定义的地方开始,到Shell脚本结束或被显式删除处为止。函数定义的变量可以被显式定义成局部的,其作用域局限于函数内。但请注意,函数的参数是局部的。

3、如果同名,Shell函数定义的局部变量会屏蔽脚本定义的全局变量。

shift

shift 命令用于对参数的移动(左移),通常用于在不知道传入参数个数的情况下依次遍历每个参数,然后进行相应处理,常见于Linux中各种程序的启动脚本。

> /dev/null 2>&1

Linux 中的标准输入和输出包括:

  • 0,表示标准输入(stdin),即键盘输入。
  • 1,表示标准输出(stdout),即屏幕输出,系统默认是1。
  • 2,表示标准错误输出(stderr),即屏幕输出。
1
nohup command > /dev/null 2>&1

等价于

1
nohup command 1 > /dev/null 2>&1

1)nohup:表示当前用户和系统的会话下的进程忽略响应hup消息,从而不挂断地运行命令。

2)command:表示Shell命令或者为一个可执行的程序。

3)>:表示重定向到哪里。

4)/dev/null:表示Linux的空设备文件。

5)2:表示标准错误输出

6)&1:&表示等同于的意思,2>&1,表示把标准错误输出定向到前一个(&的作用)打开的文件中去。

7)&:命令最后面加一个&,表示后台执行,即这条指令在后台运行。

1 > /dev/null,表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,不显示任何信息。

2 > &1,表示标准错误输出重定向等同于标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。

> /dev/null 2>&1,常用来避免shell命令或程序等运行中有内容输出。

命令组

命令组是在括号中的命令列表,将会作为一个子shell来运行,一般格式为:

1
(cmd1,cmd2,cmd3)

命令执行控制

1、&& 表示命令之间的关系为:与,格式为:

1
cmd_1 && cmd_2 # 如果命令cmd_1执行成功,则执行cmd_2,否则cmd_2不执行。

2、|| 表示命令之间的关系为:或,格式为:

1
cmd_1 || cmd_2 # 如果命令cmd_1执行成功,则不执行命令cmd_2,否则执行cmd_2。

3、; 表示命令之间的关系为:顺序,格式为:

1
cmd_1 ; cmd_2 # 无论cmd_1是否执行成功,都会执行cmd_2。

$() 与 ` `

在 bash 中,$() 与 ` `(反引号) 都是用来作命令替换的,先完成引号里面的命令,然后将其结果替换出来,再重组成新的命令。

在操作上,这两者都能达到相应的效果,但是建议使用 $()

  • ` ` 很容易与''(单引号)搞混乱,惹来无端麻烦。
  • 在多层次的复合替换中,` ` 必须要做额外的转义处理,而 $() 比较直观。
  • $() 的弊端是,并不是所有的类 unix 系统都支持这种方式,但是反引号是肯定支持的。

$var 和 ${var}

一般情况下,$var 和 ${var} 是没有区别的,但是用 ${}会比较精确地界定变量名称的范围。

退出状态码

shell 中运行的每个命令都使用退出状态码来告诉 shell 它完成了处理。退出状态码是一个 0~255 之间的整数值,在命令执行结束时由命令返回给 shell。

典型的退出状态码及其意义:

  • 0:命令运行成功
  • 1:通知未知错误
  • 2:误用 shell 命令
  • 126:命令不可执行
  • 127:没有找到命令
  • 128:无效退出参数
  • 130:命令通过 Ctrl + C 终止
  • 255:退出状态码越界
  • 137:OOMKilld,内存溢出错误。

exit 命令用于在 shell 脚本中指定退出状态码,退出状态码最大只能到 255,若其大于 255,则返回模除 256 后的余数。

Shell脚本调试

脚本调试的主要工作就是发现引发脚本错误的原因以及脚本源代码中定位错误。

通过 echo 方式

功能描述:最简单也是最常用的调试方式,在任何怀疑出错的地方用 echo 打印变量

使用场景:所有怀疑可能有问题的地方

示例:

1
echo $xdhuxc

通过选项方式

-n

功能描述:读取 shell 脚本,但不实际执行。

使用场景:用于测试 shell 脚本中是否存在语法错误。

示例:bash -n xdhuxc.sh

xdhuxc.sh内容如下:

1
2
3
4
5
6
7
#!/usr/bin/env bash
function xdhuxc() {
    echo "bash function is called"
}
echo "xdhuxc start"
xdhuxc
echo "xdhuxc end"
-c

功能描述:使 shell 解释器从字符串而非文件中读取并执行命令。

使用场景:当需要调试一小段脚本的执行结果时,非常方便。

示例:

1
bash -c 'a=2;b=3;let c=a+b;echo c=$c'
-v

功能描述:区别于 -x 参数,该选项打印命令行的原始内容,-x 参数打印出经过替换后的命令行的内容。

使用场景:仅想显示命令行的原始内容。

示例:

1
bash -v xdhuxc.sh
-x

功能描述:提供跟踪执行信息,在执行脚本的过程中,把实际执行的每个命令显示出来,行首显示 +,+ 后面显示经过替换之后的命令行内容,有助于分析实际执行的是什么命令

使用场景:是调试 shell 脚本的强有力工具,shell 脚本调试的首选手段。

示例:

1、在命令行提供参数

1
$sh -x xdhuxc.sh

2、脚本开头提供参数

1
#!/usr/bin/env bash -x

3、在脚本中用 set 命令启用或禁用参数:其中 set -x 表示启用;set +x 表示禁用。

通过 test 的方式,返回值为 0,则为真;返回值为1,则为假。

1
test -f /etc/hosts

通过 trap 来调试

功能描述:用于捕获指定的信号并执行预定义的命令

语法格式:trap cmd signal

说明: signal 是要捕获的信号

cmd 是捕获到指定的信号,所要执行的命令,可以用 kill -l 命令查看系统中全部可用的信号名。捕获信号后所执行的命令,可以是一条或多条合法的 shell 语句,也可以是一个函数名。shell 脚本执行时,会产生三个伪信号(之所以称为伪信号,是因为这是 shell 自己产生的,而非操作系统产生的),通过使用 trap 捕获这三个伪信号并输出信息对调试有很大的帮助。

shell 的三个伪信号:

  • EXIT:从一个函数中退出或整个脚本执行完毕。
  • ERR:当一个命令执行不成功,返回非 0 状态时。
  • DEBUG:脚本中每一条命令执行之前。

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/bin/env bash

errorTrap() {
    echo "[LINE : $1] Error: Command or function exited with status $?"
}

hello() {
    return 1;
}
trap $(errorTrap $LINENO) ERR
xdhuxc
hello

执行 xdhuxc.sh 脚本:

1
2
bash xdhuxc.sh

使用调试工具 bashdb

使用 shell 调试器 bashdb,可以实现对 shell 脚本的断点设置、单步执行和变量观察等许多功能。

使用方式:

1
2
3
4
5
bashdb -c xdhuxc.sh
或者
bashdb xdhuxc.sh
或者
bashdb --debug xdhuxc.sh

bash -x xdhuxc.sh

使用命令 bash -x xdhuxc.sh 调试脚本xdhuxc.sh,示例输出如下:

1
2
3
4
5
6
7
8
++ cd ../..
++ pwd
+ DCE_HOME=/yonyou/developercenter_enterprise
+ DCE_IP=192.168.244.133
+ echo -e /yonyou/developercenter_enterprise
/yonyou/developercenter_enterprise
+ echo -e 192.168.244.133
192.168.244.133

"+" 号后面显示的是经过了变量替换之后的命令行的内容,有助于分析实际执行的是什么命令

带加号表示该条语句是Shell执行的。

不带加号表示该语句是Shell产生的输出。

set -e

set -e 语句告诉 bash 如果任何语句的执行结果不是 true,则应该退出。这样做的好处是防止错误像滚雪球般变大而导致一个致命的错误,而这些错误本应该在之前就被处理掉。

字符串比较

字符串比较的作用:测试字符串是否相等,长度是否为零,字符串是否为NULL等 常用的字符串比较符号如下:

比较符 说明
= 比较两个字符串是否相同,相同则为“是”
!= 比较两个字符串是否不同,不同则为“是”
-n 比较字符串的长度是否大于0,如果大于0则为“是”
-z 比较字符串的长度是否等于0,如果等于0则为“是”

数字比较

常用的数字比较符如下:

比较符 说明
-eq 等于
-ge 大于等于
-le 小于等于
-ne 不等于
-gt 大于
-lt 小于

字符串测试

在编写 Shell 脚本时,经常需要进行判断,以下是一些测试字符串的方法:

  • [ -z abc ] abc的值的长度为零则为真
  • [ -n abc ] 如果字符串的长度不为零,则为真
  • [ -a file_name ] 如果file_name存在,则为真
updatedupdated2018-09-072018-09-07
加载评论