共计 8192 个字符,预计需要花费 21 分钟才能阅读完成。
1. Hello World
1.1. 根本构造
创立 helloWorld.sh
文件,写入如下内容:
#!/bin/bash
echo "hello world"
其中 #!
通知零碎其后门路所指定的程序是解释此脚本文件的 Shell 程序,常见的 Shell 程序有以下几类(可通过命令 cat /etc/shells
查看):
- Bourne Shell(/usr/bin/sh 或 /bin/sh)
- Bourne Again Shell(/bin/bash)
- C Shell(/usr/bin/csh)
- K Shell(/usr/bin/ksh)
- Shell for Root(/sbin/sh)
- ……
其中 Bash 在日常工作中被宽泛应用,同时也是大多数 Linux 零碎默认的 Shell。
执行该 sh 脚本
# 减少可执行权限
➜ chmod u+x helloWorld.sh
# 运行脚本
➜ ./helloWorld.sh
或
➜ sh hellowWorld.sh
1.2. 正文
单行正文
- 以
#
结尾的行是正文
多行正文
- 形式一:用一对
{}
括起来,定义成一个函数,没有中央调用即达到正文的成果。 -
形式二:
:<<EOF 正文内容... 正文内容... 正文内容... EOF
2. 根本语法
2.1. 变量
-
变量定义
- 变量名倡议大写;
- 无效字符仅能蕴含字母、数字、下划线,首个字符不能以数字结尾;
=
两边不能有空格;- 不能应用标点符号;
- 不能应用 bash 里的关键字(可用
help
命令查看保留关键字)。
# 示例 VAR1="whoru" VAR2=100 var3=/data/www var4_name="root"
- 拜访变量
$VAR1
或$(var1)
,其中,加花括号是为了帮忙解释器辨认变量的边界。 - 设置变量只读
readonly VAR1
- 删除变量(不适用于只读变量!)
unset VAR1
-
部分、全局变量
- 不做非凡申明,shell 中所有变量都是 全局变量。
- 能够应用关键字
local
定义局部变量。 - 如果函数外部和内部存在同名变量,则外部会笼罩内部。
2.2. 字符串
-
值用双引号
""
或单引号''
示意-
单引号单限度:
- 单引号里的任何字符都会原样输入;
- 单引号字符串中的变量是有效的;
-
双引号的长处:
- 双引号里能够有变量;
- 双引号里能够呈现转义字符;
-
-
其它
# 字符串拼接 name="xiaoming" var2="hello,"$name # 输入 hello, xiaoming # 获取字符串长度 string="abcd" echo ${#string} # 输入 4 echo `expr length "$string"` # 输入 4 # 提取子字符串 msg="zhangsan is a good man" echo ${msg:1:4} # 输入 hang echo ${msg: -3} # 输入 man
2.3. 数组
- bash 反对一维数组(不反对多维数组),并且没有限定数组的大小。
- 数组元素的下标由 0 开始,获取数组元素要用到下标。
-
定义:
array1=(value0 value1 value2 value3) # 或 array2[0]=value0 array2[1]=value1 array2[2]=value2
-
读取
# 指定下标的元素 ➜ echo ${array2[2]}; // 输入 value2 # 获取数组所有元素 ➜ echo ${array2[*]}; // 输入 value0 value1 value2 ➜ echo ${array2[@]}
-
获取数组元素个数
➜ echo ${#array2[@]}; // 输入 3 ➜ echo ${#array2[*]};
-
获得数组中指定下标元素的字符长度
➜ echo ${#array2[2]};
2.4. 传递参数
在执行 Shell 脚本时,能够向脚本传递参数,脚本内获取参数的格局为 $n
,这里的 n 指传递给脚本的第 n 个参数。
如下脚本文件 demo.sh
:
#!/bin/bash
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
执行该文件,并传递参数,如下:
➜ ./demo3.sh param1 param2 param3
执行的文件名:./demo3.sh
第一个参数为:param1
第二个参数为:param2
第三个参数为:param3
其中,$0
是一个非凡变量,代表以后脚本文件名,还有几个相似的变量如下:
变量 | 阐明 |
---|---|
$# | 传递给脚本的参数个数。 |
$* | 以一个单字符串的模式显示所有向脚本传递的参数,如 "$1 $2 ... $n" |
$@ | 与 $* 雷同,然而应用引号把每个参数包裹起来,如 "$1" "$2" ... "$n" |
$? | 最初一个执行的命令的退出状态:0 失常;1 或其它任何值,示意有谬误 |
$$ | 脚本运行的以后过程 ID 号 |
$! | 最初一个后盾命令的过程号。 |
3. 运算符
3.1. 算数运算符
原生 bash 不反对简略的数学运算,然而能够通过其余命令来实现,例如 awk 和 expr,其中 expr 最罕用。
假设有两个变量:a=10
b=20
运算符 | 阐明 | 举例 |
---|---|---|
+ |
加法 | `expr $a + $b` 后果为 30。 |
- |
减法 | `expr $a - $b` 后果为 -10。 |
* |
乘法 | `expr $a \* $b` 后果为 200。 |
/ |
除法 | `expr $b / $a` 后果为 2。 |
% |
取余 | `expr $b % $a` 后果为 0。 |
= |
赋值 | a=$b 将把变量 b 的值赋给 a。 |
== |
用于比拟两个数字是否雷同 | [$a == $b] 返回 false。 |
!= |
用于比拟两个数字是否不雷同 | [$a != $b] 返回 true。 |
留神:
- 表达式和运算符之间要有空格,如
2+2
是谬误的,必须写成2 + 2
; - 残缺的表达式要被反引号
` `
包裹起来;
3.2. 关系运算符
关系运算符只反对数字,不反对字符串,除非字符串的值是数字。
假设有两个变量:a=10
b=20
运算符 | 阐明 | 举例 |
---|---|---|
-eq |
检测两个数是否相等 | [$a -eq $b] 返回 false。 |
-ne |
检测两个数是否不相等 | [$a -ne $b] 返回 true。 |
-gt |
检测右边的数是否大于左边的 | [$a -gt $b] 返回 false。 |
-lt |
检测右边的数是否小于左边的 | [$a -lt $b] 返回 true。 |
-ge |
检测右边的数是否大于等于左边的 | [$a -ge $b] 返回 false。 |
-le |
检测右边的数是否小于等于左边的 | [$a -le $b] 返回 true。 |
3.3. 布尔操作符
假设有两个变量:a=10
b=20
运算符 | 阐明 | 举例 |
---|---|---|
! |
非运算,表达式为 true 则返回 false,否则返回 true。 | [! false] 返回 true。 |
-o |
或运算,有一个表达式为 true 则返回 true。 | [$a -lt 20 -o $b -gt 100] 返回 true。 |
-a |
与运算,两个表达式都为 true 才返回 true。 | [$a -lt 20 -a $b -gt 100] 返回 false。 |
3.4. 逻辑运算符
假设有两个变量:a=10
b=20
运算符 | 阐明 | 举例 |
---|---|---|
&& | 逻辑的 AND | [[$a -lt 100 && $b -gt 100]] 返回 false |
|| | 逻辑的 OR | [[$a -lt 100 || $b -gt 100]] 返回 true |
3.5. 字符串运算符
假设有两个变量:a="abc"
b="efg"
运算符 | 阐明 | 举例 |
---|---|---|
= |
检测两个字符串是否相等 | [$a = $b] 返回 false。 |
!= |
检测两个字符串是否不相等 | [$a != $b] 返回 true。 |
-z |
检测字符串长度是否为 0(空) | [-z $a] 返回 false。 |
-n |
检测字符串长度是否不为 0(非空) | [-n "$a"] 返回 true。 |
str | 检测字符串是否为不为空 | [$a] 返回 true。 |
3.6. 文件测试运算符
运算符 | 阐明(如果是,则返回 true) | 举例 |
---|---|---|
-b |
检测文件是否是块设施文件 | [-b $file] |
-c |
检测文件是否是字符设施文件 | [-c $file] |
-d |
检测文件是否是目录 | [-d $file] |
-f |
检测文件是否是一般文件(既不是目录,也不是设施文件) | [-f $file] |
-g |
检测文件是否设置了 SGID 位 | [-g $file] |
-k |
检测文件是否设置了粘着位(Sticky Bit) | [-k $file] |
-p |
检测文件是否是有名管道 | [-p $file] |
-u |
检测文件是否设置了 SUID 位 | [-u $file] |
-r |
检测文件是否可读 | [-r $file] |
-w |
检测文件是否可写 | [-w $file] |
-x |
检测文件是否可执行 | [-x $file] |
-s |
检测文件是否为非空(文件大小是否大于 0)文件 | [-s $file] |
-e |
检测文件(包含目录)是否存在 | [-e $file] |
4. 流程管制
4.1. if 语句
大多应用 关系运算符 查看关系
# 语法格局
if condition1
then
command1
...
elif condition2
then
command2
else
commandN
fi
4.2. case 语句
# 语法格局
case 值 in
模式 1)
command1
command2
...
commandN
;;
模式 2)command1
command2
...
commandN
;;
*)
commandDefault
;;
esac
4.3. while 语句
用于一直执行一系列命令,也用于从输出文件中读取数据;命令通常为测试条件。其格局为:
# 语法格局
while condition
do
command
done
4.4. until 循环
执行一系列命令直至条件为 true 时进行,它与 while 循环
在解决形式上刚好相同。
# 语法格局
until condition
do
command
done
4.5. for 循环
# 语法格局
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
4.6. 有限循环
# 语法 1
while :
do
command
done
# 语法 2
while true
do
command
done
# 语法 3
for ((; ;))
4.7. 退出循环
break
跳出整个循环,执行循环体前面的代码,反对break n
退出多层嵌套循环continue
完结以后循环,同样反对continue n
退出多层
5. 输出、输入重定向
5.1. 命令列表
命令 | 阐明 |
---|---|
command > file |
将输入后果重定向到 file。 |
command < file |
将输出重定向到 file。 |
command >> file |
将输入以追加的形式重定向到 file。 |
n > file |
将文件描述符为 n 的文件重定向到 file。 |
n >> file |
将文件描述符为 n 的文件以追加的形式重定向到 file。 |
n >& m |
将输入文件 m 和 n 合并。 |
n <& m |
将输出文件 m 和 n 合并。 |
<< tag |
将开始标记 tag 和完结标记 tag 之间的内容作为输出。 |
对于文件描述符:
0
通常是规范输出(STDIN),Unix 程序默认从stdin
读取数据。1
规范输入(STDOUT),Unix 程序默认向stdout
输入数据。2
规范谬误输入(STDERR),Unix 程序会向stderr
流中写入错误信息。
示例:
# 将 stdout 和 stderr 合并后重定向到 file
➜ command > file 2>&1
5.2. /dev/null 文件
这是一个非凡的文件,写入到它的内容都会被抛弃;如果尝试从该文件读取内容,也什么也读不到。咱们通常将命令的输入重定向到它,起到“禁止输入”的成果。
如:
# 屏蔽 stdout 和 stderr
➜ command > /dev/null 2>&1
5.3. Here 文档
# 将两个 delimiter 之间的内容(document) 作为输出传递给 command。command << delimiter
document
delimiter
阐明:
- 结尾的
delimiter
肯定要顶格写,后面不能有任何字符,前面也不能有任何字符,包含空格和 tab 缩进。 - 开始的
delimiter
前后的空格会被疏忽掉。
6. 函数
6.1. 根本语法
[function] funcName [()] {
command;
[return int;]
}
阐明:
function
关键字非必须;- 如果该函数不传入变量,这函数名的前面的括号能够不加;
-
return
函数返回值- 非必须,默认返回最初一条命令的执行后果;
- 它只能返回 1 ~ 255 之间的整数,通常只是用来供其它中央获取状态,比方 0 胜利,1 或 非 0 失败;
- 也能够应用
echo
输入一个字符串作为函数的返回值。
- 调用函数仅应用其函数名,如
funcName
; - 所有函数在应用前必须定义,即函数调用必须要在函数申明之后。
6.2. 函数参数
func() {
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
...
echo "第十个参数为 ${10} !"
...
}
# 调用并传参
func param1 param2 param3
阐明:
- 在函数体外部,通过
$n
的模式来获取参数的值,例如:$1 示意第一个参数,$2 示意第二个参数; - 当 n >= 10 时,须要应用
${n}
来获取参数。
7. 蕴含文件(封装函数库)
通常咱们将专用的函数抽离到独自文件,以便反复调用,缩小冗余代码。
对于一个函数库文件:
- 后缀名任意,通常应用
.lib
进行标识; - 个别不授予可执行权限;
- 不须要跟脚本放在同一级目录,只需在脚本援用时指定;
- 通常第一行个别应用
#!/bin/echo
输入正告信息,防止用户执行。
示例:
#!/bin/echo
# /home/user1/lib/comm_function.lib
function add {echo "`expr $1 + $2`"}
#!/bin/bash
# /home/user1/test.sh
# 引入函数库文件
# 应用相对 或 相对路径
. ./lib/comm_function.lib
# 应用文件中的函数
add 1 3
➜ sh -x test_functions.sh
+ . ./lib/comm_function.lib
+ add 1 3
++ expr 1 + 3
+ echo 4
4
8. 常用命令
8.1. find 命令
语法:find [门路] [选项] [操作]
选项
选项 | 阐明 | 选项 | 阐明 |
---|---|---|---|
-name |
文件名 | -iname |
文件名(疏忽大小写) |
-perm 777 |
文件权限 | -type f|d|l|c|b|p |
文件类型 |
-user |
文件属主 | -nouser |
无无效属主 |
-group |
文件属组 | -nogroup |
无无效属组 |
-size -n|+n |
文件大小 | -prune |
排除某些查找目录(通常与 -path 一起应用) |
-mindepth n |
从 n 级子目录开始查找 | -maxdepth n |
最多搜寻到 n 级子目录 |
-mtime -n|+n |
文件批改工夫(天) | -mmin -n|+n |
文件批改工夫(分钟) |
-newer file1 |
文件批改工夫比 file1 早 |
示例:
# 文件名
➜ find /etc/ -name '*.conf'
# 文件类型
# f 文件;d 目录;c 字符设施文件;# b 块设施文件;l 链接文件;p 管道文件
➜ find /etc/ -type f
# 文件大小
# -n 小于等于;+n 大于等于
➜ find . -size +100M
➜ find . -size -10k
# 文件批改工夫
# -n < n 天以内批改过的文件;# n = n 天批改过得文件;# +n > n 天以外批改过的文件;➜ find . -mtime -3
➜ find . -mtime 3
➜ find . -mtime +3
# 排除目录
# -path ./test1 -prune 排除 test1 目录
# -path ./test2 -prune 排除 test2 目录
# -o type f 固定结尾写法
➜ find . -path ./test1 -prune -o -path ./test2 -prune -o type f
操作
-print
打印输出-exec 'command' {} \;
其中{}
是后面查找匹配到的后果-ok
与 exec 性能一样,但每次操作都给用户提醒,由用户决定是否执行对应的操作。
示例:
# 查找 30 天以前的日志文件并删除
➜ find /var/log -name '*.log' -mtime +30 -exec rm -f {} \;
# 查找所有 .conf 文件,并挪动到指定目录
➜ find /etc/apache -name '*.conf' -exec cp {} /home/user1/backup \;
8.2. echo 命令
用于字符串的输入,根本格局 echo string
。
应用示例:
# 显示一般字符
➜ echo "It is a test" # 输入 It is a test
# 显示转义字符
➜ echo "\"It is a test\""# 输入"It is a test"
# 显示变量
#!/bin/sh
NAME="xiaoming"
➜ echo "$NAME It is a test" # 输入 xiaoming is a test
# 显示换行
➜ echo -e "OK! \n" # -e 开启本义
➜ echo "It is a test"
# 显示不换行
➜ echo -e "OK! \c" # -e 开启本义 \c 不换行
➜ echo "It is a test"
# 显示后果定向至文件
➜ echo "It is a test" > myfile
# 显示命令执行后果
➜ echo `date`
8.3. printf 命令
模拟 C 程序库(library)里的 printf() 程序,次要用于格式化输入。
默认 printf
不会像 echo
主动增加换行符,咱们能够手动增加 \n
。
其根本语法格局为:
➜ printf format-string [arguments...]
阐明:
format-string
为格局管制字符串arguments
为参数列表。
示例:
➜ printf "%-10s %-8s %-4s\n" 姓名 性别 体重 kg
➜ printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
➜ printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
➜ printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
姓名 性别 体重 kg
郭靖 男 66.12
杨过 男 48.65
郭芙 女 47.99
其中:
%s
%c
%d
%f
都是格局代替符;%-10s
指一个宽度为 10 个字符(- 示意左对齐,没有则示意右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果有余则主动以空格填充,超过也会将内容全副显示进去。%-4.2f
指格式化为小数,其中.2 指保留 2 位小数。
更多应用示例:
# 没有引号也能够输入
➜ printf %s abcdef
# 格局只指定了一个参数,但多出的参数依然会依照该格局输入,format-string 被重用
➜ printf %s abc def
abcdef
➜ printf "%s\n" abc def
abc
def
# 如果没有 arguments,那么 %s 用 NULL 代替,%d 用 0 代替
➜ printf "%s and %d \n"
and 0
8.4. test 命令
用于查看某个条件是否成立,它能够进行数值、字符和文件三个方面的测试(详见第 3 节对应的运算符局部)。
根本应用示例:
cd /bin
if test -e ./bash
then
echo '文件已存在!'
else
echo '文件不存在!'
fi
9. 补充
9.1. 变量替换
规定 | 阐明 | 示例 var="Hello shell" |
---|---|---|
${变量 #匹配规定} |
从头开始匹配,最短删除 | ${var#*e} => llo shell |
${变量 ## 匹配规定} |
从头开始匹配,最长删除 | ${var##*e} => ll |
${变量 % 匹配规定} |
从尾开始匹配,最短删除 | ${var%e*} => Hello sh |
${变量 %% 匹配规定} |
从尾开始匹配,最长删除 | ${var%%e*} => H |
${变量 / 旧字符串 / 新字符串} |
只替换匹配到的第一个 | ${var/e/*} => H*llo shell |
${变量 // 旧字符串 / 新字符串} |
全副替换 | ${var//e/*} => H*llo sh*ll |
9.2. 有类型变量
shell 中变量默认都是 字符串,除非应用以下形式申明。
declare 或 typeset 参数 | 阐明 |
---|---|
-r | 只读 |
-i | 整数 |
-a | 数组 |
-f | 在脚本中显示定义的函数和内容 |
-F | 在脚本中显示定义的函数 |
-X | 将变量申明为环境变量 |
示例:
➜ declare -r var1="hello shell type"
➜ var1="hello lalala"
zsh: read-only variable: var1
9.3. 应用 bc 进行浮点数运算
零碎内置,反对 +
、-
、*
、/
、^ 指数
、% 取余
,并应用 scale
指定小数位数,默认 0
。
示例:
➜ which bc
/usr/bin/bc
# 示例
➜ echo "5+4" | bc
9
➜ echo "5-4" | bc
1
➜ echo "5*4" | bc
20
➜ echo "5/4" | bc
1
➜ echo "scale=3;5/4" | bc
1.250
➜ echo "5%4" | bc
1
➜ echo "5^4" | bc
625
9.4. […] 与 [[…]]
-
[[
是关键字,许多 shell 并不反对这种形式。- 所有的字符都不会被文件扩大或是标记宰割,然而会有参数援用和命令替换;
- 更能避免脚本里的许多逻辑谬误,比如说
&&
,||
,<
和>
操作符能在一个[[...]]
测试里通过,但在[...]
构造会产生谬误。 - 会进行算术扩大。
-
[
是一条命令,与 test 等价,大多数的 shell 都反对。- 在其中的表达式应是它的命令行参数,所以串比拟操作符
>
与<
必须本义,否则就变成 IO 重定向操作符了。 - 不会进行算术扩大。
- 在其中的表达式应是它的命令行参数,所以串比拟操作符