shell 脚本的关键在于输入多个命令 并 处理每个命令的结果, 甚至需要将一个命令的结果传给另一个命令.
1 使用多个命令
将多个命令放在一行, 用分号 “;
” 分隔, 就可以让多个命令串行执行了:
$ date
Fri Sep 3 23:51:38 CST 2021
$ who
miyan pts/6 Sep 3 00:27 (112.65.13.166)
$ date; who
Fri Sep 3 23:51:49 CST 2021
miyan pts/6 Sep 3 00:27 (112.65.13.166)
2 创建 shell 脚本文件
创建 shell 脚本文件时, 必须在 文件的第一行指定的要使用的 shell.
其格式为:
#!/bin/bash
在 shell 脚本中, 井号(#
) 用于注释一行. 然而在 shell 脚本的第一行是个例外, #
后面的 !
表明用哪个 shell 来运行脚本, 可以用 bash
, zsh
等等.
第一个 shell 脚本(脚本名为 temp.sh):
#!/bin/bash
# 日期, 登录用户
date
who
执行:
$ temp.sh
zsh: command not found: temp.sh
报错了!!! 这是因为 shell 是通过 PATH
环境变量的路径来查找命令的, 我写的 temp.sh
不在 PATH
路径中:
$ echo $PATH
/usr/local/apache-maven-3.6.3/bin:/usr/local/bin:/usr/bin:/home/miyan/bin:/usr/local/sbin:/usr/sbin:/data/soft/zookeeper/bin
有两种方法可以执行 temp.sh
脚本:
- 把
temp.sh
所在的目录添加到PATH
环境变量中. - 用
绝对路径
或相对路径
执行temp.sh
.
一般都是用 相对路径
执行 shell 脚本, 下面展示了用 相对路径
和 绝对路径
执行 temp.sh
脚本:
$ chmod +x temp.sh
$ ./temp.sh
Sat Sep 4 00:23:46 CST 2021
miyan pts/6 Sep 4 00:12 (112.65.13.166)
$ /home/miyan/test/temp.sh
Sat Sep 4 00:24:06 CST 2021
miyan pts/6 Sep 4 00:12 (112.65.13.166)
默认情况下, 新建的文件是没有执行权限的(和 umusk
配置有关), 所以需要用 chmod
先加上执行权限 x
.
3 显示消息
大多数 shell 命令都会产生自己的输出.
如果需要自己输出指定的文本信息, 需要使用 echo
命令.
比如:
$ echo this is a echo test
this is a echo test
$ echo let's see if this'll work
lets see if thisll work
$ echo "let's see if this'll work"
let's see if this'll work
echo
使用单引号 '
或 双引号 "
来划定文本字符串, 如果在字符串中用到 '
或 "
的一种, 你 必须 使用另外一种把它包起来.
echo
默认输出的文本会 换行. 如果不想 换行, 可以使用 -n
参数实现 不换行 :
$ cat temp.sh
#!/bin/bash
echo -n "当前显示时间: "
date
$ ./temp.sh
当前显示时间: Sat Sep 4 00:40:06 CST 2021
4 使用变量
4.1 环境变量
在脚本中, 只要在 环境变量名 前加上 $
就能够使用 环境变量的值.
这和在命令行中使用没有区别:
$ echo $USER
miyan
$ echo $HOME
/home/miyan
4.2 用户变量
可以在 shell 脚本种自定义变量, 这种自定义的变量就是 用户变量.
注意:
- 用户变量名 是大小写敏感的
- 用户变量名 只能有 字母, 数字, 下划线 组成, 且不能以数字开头.
- 使用等号(
=
)给用户变量赋值时, 在变量名
与=
与值
三者之间, 不能出现空格!!! - 不需要指定变量类型, shell 会自动决定变量的 数据类型.
- 用户变量 和 环境变量 一样, 也用
$
引用. - 引用变量需要
$
, 而对变量赋值时不需要$
.
示例:
$ cat temp.sh
#!/bin/bash
days=10
guest="miyan"
echo "$guest checked in $days days ago"
days=3
guest="rosie"
echo "$guest checked in $days days ago"
value1="hello world"
value2=value1
value3=$value1
echo "value2: '$value2', value3: '$value3'"
执行结果:
$ ./temp.sh
miyan checked in 10 days ago
rosie checked in 3 days ago
value2: 'value1', value3: 'hello world'
注意到,
value2
被赋予的是 value1
字符串;
value3
被赋予的是 hello world
($value1
的值)字符串.
所以, 没有 $
, shell 会把变量名解释成 普通的字符串.
shell 数组变量示例:
$ cat temp.sh
#!/bin/bash
# 方式一
test_array_a=(1 2 3)
#方式二
test_array_b[0]=1
test_array_b[1]=2
test_array_b[2]=3
for i in ${test_array_a[@]}; do
echo "$i"
done
for i in ${test_array_b[@]}; do
echo "$i"
done
$ ./temp.sh
1
2
3
1
2
3
定义数组有两种方式:
1, 用括号来表示, 元素用"空格"符号分割开, 语法格式如下:
array_name=(value1 value2 ... valuen)
;
2, 使用下标来定义数组, 语法格式如下:
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
获取 shell 数组单个下标为 index 的元素: ${array_name[index]}
;
获取 shell 数组中的所有元素: ${my_array[*]}
或 ${my_array[@]}
.
4.3 命令替换
命令替换: 把命令的输出信息提取出来.
有两种方法可以实现命令替换:
- 反引号字符
`
$()
格式
示例:
$ echo "现在时间是: $(date)"
现在时间是: Sat Sep 4 01:13:25 CST 2021
$ echo "现在时间是: `date`"
现在时间是: Sat Sep 4 01:13:48 CST 2021
如上所示, 把 date
命令的输出提取出来, 放在 echo
命令中显示.
命令替换 通常用于给 变量赋值.
5 重定向输入和输出
5.1 输出重定向
如果想要 把命令的输出 重定向(保存)到文件中, 而不是显示在标准输出(显示器)上, 则需要用到 输出重定向.
输出重定向的使用方法:
command > outputFile
输出重定向符号有>
和 >>
两个:
- 操作符
>
, 把输出重定向到文件, 如果文件已经存在, 则会覆盖原文件. - 操作符
>>
, 如果文件已经存在, 会追加数据, 不会覆盖原文件的数据.
示例:
$ echo "hello world" > test
$ cat test
hello world
$ whoami >> test
$ cat test
hello world
miyan
5.2 输入重定向
输入重定向 与 输出重定向 相反, 输入重定向 将文件的内容重定向到命令.
输入重定向的使用方法:
command < inputFile
输入重定向符号有<
和 <<
两个.
<
用于把文件的内容重定向到命令:
$ echo "hello" > test
$ wc -l < test
1
$ echo "world" >> test
$ wc -l < test
2
<<
是 内联输入重定向(inline input redirection), 它不是把文件的内容重定向到命令, 而是把 命令行指定的输入 重定向到命令. 使用 <<
时, 必须使用一个 文本标记(可以是任一字符串, 一般时 EOF
) 来标记 输入内容的开始和结尾.
示例:
$ wc -l << EOF
heredoc> hello
heredoc> world
heredoc> EOF
2
上面的 heredoc>
这个标记用于提示我们继续输入字符串, 当我们输入第二个 EOF
后, 表示输入结束, 然后 wc
命令开始执行.
6 管道
管道: 把一个命令的输出 作为 另一个命令的输入.
管道符号: |
.
管道的使用方法, 把 command1 的输出 作为 command2 的输入:
command1 | command2
注意, 管道|
连接的两个命令并不是依次执行的, Linux 系统会同时运行两个命令, 在第一个命令产生输出的同时, 输出会立即送到第二个命令. 其中不会有任何中间文件和缓冲区.
管道|
使用示例, 查看当前目录下的文件和目录所占的磁盘空间大小, 并按降序排列:
du -sh * | sort -hr
管道的功能也可以用 重定向 来实现:
$ du -sh * > test
$ sort -hr < test
7 执行数学运算
这里简单介绍一下 整数运算, 浮点数运算 用 bc
命令实现, 这里不涉及.
expr
命令是 Bourne shell 提供的处理 数学表达式(expression) 的命令.
expr
真不好用, 看示例:
$ expr 2+5
2+5
$ expr 2 + 5
7
$ expr 2 * 5
expr: syntax error
$ expr 2 \* 5
10
可以看到, expr
要求运算符两边必须有空格, *
需要转义(防止被解释成通配符).
bash shell 为了保持和 Bourne shell 兼容而包含了 expr
命令, 但同时 bash
提供了一种更简单的方法执行 数学表达式.
可以用美元符 $
和 方括号[]
把数学表达式包起来:
$[ operation ]
示例:
$ echo $[2+5]
7
$ echo $[2*5]
10
8 退出脚本
shell 中运行的每一个命令都是使用 退出状态码(exit status) 告诉 shell 它是否已经执行完毕.
exit status 是一个 0~255
的整数值, 在命令结束运行时 由命令传给 shell. 可以捕获这个值并在脚本中使用.
8.1 查看退出状态码
Linux 提供了一个专门的变量 $?
保存上一个已执行的命令的 退出状态码(exit status). 对于需要检查的命令, 必须 在其运行完毕后立刻 查看 $?
变量:
$ date
Sat Sep 4 12:02:51 CST 2021
$ echo $?
0
exit status 为 0
表示命令成功结束.
如果命令结束时报错, 那么 exit status 就是一个正数值:
$ hellomiyan
zsh: command not found: hellomiyan
$ echo $?
127
Linux 的一些 退出状态态 可以参考下表:
退出状态码 | 描述 |
---|---|
0 | 命令成功结束 |
1 | 一般性未知错误 |
2 | 不适合的shell命令 |
126 | 命令不可执行 |
127 | 没找到命令 |
128 | 无效的退出参数 |
130 | 通过 ctrl+c 终止命令 |
255 | 正常范围之外的退出状态码 |
8.2 exit 命令
默认情况下, shell 脚本会以 最后一个命令的退出状态码 退出.
示例:
$ cat temp.sh
#!/bin/bash
echo "math expression:"
echo "2 + 5 = $[2+5]"
$ ./temp.sh
math expression:
2 + 5 = 7
$ echo $?
0
我们可以使用 exit
命令, 返回自己的 退出状态码:
$ cat temp.sh
#!/bin/bash
echo "math expression:"
exit 1
echo "2 + 5 = $[2+5]"
$ ./temp.sh
math expression:
$ echo $?
1
可以看到, exit
命令执行以后, 就退出脚本了, exit
后面的命令不会被执行.
Reference
[1]. Linux命令行与shell脚本编程大全(第三版)