目录
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 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 重定向操作符了。
- 不会进行算术扩展。