目录
一、变量
shell脚本只有一种数据类型,就是string字符串类型。
变量名必须是字母开头,可以包含数字、下划线等,不能有空格,不能使用关键字。
1. 普通变量
变量取值,可以与其他字符串构成新的字符串
变量赋值,用“=”赋值两端无空格
2. 环境变量
环境变量的级别:
- 系统级:System env-var
- 用户级:User login env-var
- 脚本级:Script env-var
查看系统的环境变量:
-
echo:显示某个环境变量,如 echo $PATH
-
export:设置一个新环境变量,export PATH=&PATH:/...要添加的路径.../
-
env:显示所有环境变量
-
unset:清除环境变量
-
set:显示本地定义的shell变量和环境变量
查看环境变量
#include <stdio.h>
int main(int argc, char* argv[], char* env[]) {
for (int i = 0; env[i]; ++i) {
printf("env[%d]: %s\n", i, env[i]);
}
}
通过环境变量区分root用户和普通用户
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char* who = getenv("USER");
if (strcmp(who, "root") == 0) {
printf("user: %s\n", who);
}
else {
printf("user: %s, 权限不够\n", who);
}
}
自定义环境变量
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MY_ENV "myenv"
int main() {
char* myenv = getenv(MY_ENV);
if (myenv == NULL) {
printf("%s, not found\n", MY_ENV);
}
else {
printf("%s = %s\n", MY_ENV, myenv);
}
return 0;
}
3. 位置变量
#!/bin/bash
echo $0
echo $1
echo $2
echo $3
echo $4
echo $5
echo $6
echo $7
echo $8
echo $9
打印命令行指令参数选项
命令行指令通过不同的参数执行不同的任务
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char* argv[], char* env[]) {
if (argc != 2) {
printf("usage: \n\t%s [-a/-b/-c/-ab/-ac/-bc/-abc]\n", argv[0]);
return 1;
}
if (strcmp("-a", argv[1]) == 0) {
printf("功能%s\n", argv[1]);
}
else if (strcmp("-b", argv[1]) == 0) {
printf("功能%s\n", argv[1]);
}
else if (strcmp("-ab", argv[1]) == 0) {
printf("功能%s\n", argv[1]);
}
else if (strcmp("-abc", argv[1]) == 0) {
printf("功能%s\n", argv[1]);
}
//...
}
4. 特殊变量
- 最后一条命令状态:$? (0成功,1失败,2错误)
- 传递给script的参数的个数:$#
- 将命令行所有的参数作为一个整体的字符串:$*
- 查看进程的PID:$$
#!/bin/bash
echo $#
echo $*
二、输入输出
1. read命令接收输入
- 接收标准输入(键盘)的输入
- 接收其他文件的输入
- 得到输入后,将输入放入一个变量中
- read命令可以结合echo命令进行输入提示,也可用 -p 选项做输入提示
- read命令会一直阻塞等待用户输入,可以通过 -t 选项设置等待秒速
- read命令可以通过 -n 选项限制输入的字符数
- read命令可以通过 -s 选项进行静默输入,不显示输入内容,如密码的输入
2. echo命令输出字符串
echo命令加上 -n 选项,输出不换号
echo命令加上 -e 选项,支持转义字符
- \b:向前回退一个
- \c:在字符串输入末尾不换号
- \n:回车换行
- \r:回车
- \v:换行
- \t:水平方向跳Tab制表符距离
echo的引用与转义
- " ":可取值输出
- ' ':将单引号内的值原样输出
- ` `:将反撇号的内容当作命令取执行
- \:转义字符
三、表达式
1. 算术表达式
expr命令:+ - * / % (乘法运算符必须加上"",其他运算符可加可不加)
let命令:可赋值和计算
算术扩展格式:双小括号
bc工具:yum install -y bc
2. 逻辑表达式
通过 test命令 进行逻辑判断:
- 执行状态为0表示true,执行状态为1表示false,如果变量可能为空,需要加双引号
- -n 选项:变量非空则命令成功
- -z 选项:变量为空则命令成功
- -eq 是按照数值比较,= 是按照字符进行比较
复合条件判断:
- -gt ,greater than ,大于
- -lt ,less than,小于
- -ge,greter or equal,大于等于
- -le,less or equal,小于等于
- -eq,数值等于
- -a ,and,与运算,可以用 && 替换
- -o,or,或运算,可以用 || 替换
test命令可用 [ ] 中括号等价替换,注意 "[ " 和 " ]",前后中括号之间都要有空格
文件或目录属性判断:
- -e:判断文件或目录是否存在
- -d:判断是否为目录以及是否存在
- -f:判断是否为文件以及是否存在
- -r、-w、-x:判断权限
四、分支控制
1. if 分支
if 分支的结构
# 格式1:if 条件; then 执行; fi
#! /bin/bash
a=5
if [ $a -gt 3 ]
then
echo "OK"
fi
# 格式2:if 条件1; then 执行1; else 执行2; fi
#! /bin/bash
a=1
if [ $a -gt 3 ]; then
echo "OK"
else
echo "error"
fi
# 格式3:if 条件1; then 执行1; elif 条件2; then 执行2; else 执行3; fi
#! /bin/bash
a=5
if [ $a -gt 8 ]; then
echo ">8"
elif [ $a -gt 3 ]; then
echo ">3"
else
echo "<3"
fi
2. case 分支
case 变量值 in
模式 1)
命令序列1;;
模式 2)
命令序列2;;
.......
*)
默认命令序列;;
esac
case值得注意的特点
- case行尾必须为单词"in",每一模式必须以右括号“)”结束
- 双分号";;"表示命令序列结束
- 模式字符串中,可以用方括号表示一个连续的范围,如"[0-9]";还可以用管道符号" | “表示或,如"A|B”.
- 最后的 " *) " 表示默认模式,其中的 星号 相当于通配符
#!/bin/bash
read read -p "请输入一个字符,并按Enter键确定:" KEY
case "$KEY" in
[a-z]|[A-Z]) echo "您输入的是字母!";;
[0-9]) echo "您输入的是数字!";;
*) echo "您输入的是空格,功能键或其他控制字符。";;
esac
五、循环控制
1. for 循环
for val in valuelist...
do
commands
done
for((exp1;exp2;exp3))
do
commands
done
# exp1 初始化
# exp2 变量取值范围
# exp3 变量增量
2. while 循环
while command
do
command
...
done
# while后面的命令执行成功则循环,否则跳出循环
while 简单循环
通过while脚本实现给文件内容加行号
3. until 循环
until command
do
commands
done
# until后面的命令成功的时候结束循环,失败的时候进入循环
通过until脚本监控某用户是否登录
#!/bin/bash
until who | grep "$1" >/dev/null 2>&1
do
sleep 3
done
echo "Be careaful, $1 is online now!"
# 2>&1 是把标准错误输出重定向
# /dev/null 是系统垃圾堆,将所有接收到的数据都丢失
# 若该用户未登录则命令指令不成功,即循环等待
# 当用户登录,until突出循环
4. select 循环
select var in menu...
do
commands
done
# select 后面跟的是菜单
# in 关键字后面是菜单取值列表
# 这些值以空格隔开
# 用户选择数字对应菜单值
# 变量var获取值后执行循环一次
#!/bin/bash
PS3="Please Select[1-4]:"
select m in new insert modify delete exit;
do
if [ $m = exit ]; then
break
fi
echo $m;
done
# PS3是一个环境变量,它是selet的提示符
六、函数
1. 函数的格式
#shell中的函数定义
#其中function为关键字,FUNCTION_NAME为函数名
function FUNCTION_NAME(){
command1 #函数体中可以有多个语句,不允许有空语句
command2
...
}
#省略关键字function,效果一致
FUNCTION_NAME(){
command1
command2
...
}
# 函数名后的小括号中没有参数
# 函数function关键字可以不写
# 函数必须遵循先定义再定义原则
2. 函数的返回值
#!/bin/bash
echo -n "请输入文件名:"
read FILE
function checkFileExist(){
if [ -f $FILE ]; then
return 0
else
return 1
fi
}
echo "call function checkFileExist"
checkFileExist
if [ $? -eq 0 ]; then
echo "$FILE exist"
else
echo "$FILE not exist"
fi
3. 函数参数
#!/bin/bash
function checkFileExist(){
if [ -f $1 ]; then
return 0
else
return 1
fi
}
echo "Call function countLine"
checkFileExist $1 # 函数调用
if [ $? -eq 0 ]; then
echo "$1 exist"
else
echo "$1 not exist"
fi
#执行脚本时,通过直接向脚本传递文件全路径的方式传递参数
#可以看到这种方式不会因为测试文件的不一样而修改脚本本身的内容,非常简单
4. 库函数
由于Shell是一门面向过程的脚本型语言,而且用户主要是Linux系统管理人员,所以并没有非常活跃的社区,这也造成了Shell缺乏第三方函数库,所以在很多时候需要系统管理人员根据实际工作的需要自行开发函数库。下面建立一个叫 funclib 的函数库,该函数库目前只有一个函数,用于判断文件是否存在。
_checkFileExists(){
if [ -f $1 ]; then
echo "File:$1 exists"
else
echo "File:$1 not exist"
fi
}
其他脚本在希望直接调用_checkFileExists函数时,可以通过直接加载 funclib 函数库的方式实现。加载方式有如下两种:
#使用“点”命令
$ . /PATH/TO/LIB
#使用source命令
$ source /PATH/TO/LIB
假设现在有个脚本想要直接调用_checkFileExists函数,可以通过加载 funclib 函数库来实现。
可以通过 set 和 unset 命令查看当期那内存中已经定义和载入的函数,使用 unset 清除函数,函数修改后必须重新载入。