bash特性
1. 快捷键
^c # 终止前台运行的程序
^z # 将前台运行的程序挂起到后台
^d # 退出 等价exit
^l # 清屏
^a | home # 光标移到命令行的最前端
^e | end # 光标移到命令行的后端
^u # 删除光标前所有字符
^k # 删除光标后所有字符
^r # 搜索历史命令
2. 通配符
* # 匹配0或多个任意字符
? # 匹配任意单个字符
[list] # 匹配[list]中的任意单个字符,或者一组单个字符[a-z]
[!list] # 匹配除list中的任意单个字符
{} # 生成序列-数字与字母
echo test{1..10}
echo {01..100} # 前面加0可使位数对齐
echo test{,7,9}
echo {1..10..2}
3. 引号
- 双引号 " " 会把引号的内容当成整体来看待,允许通过$符号引用其他变量值,不支持通配符
- 单引号 ' ' 所见即所得,会把引号的内容当成整体来看待,禁止引用其他变量值,shell*特中**殊符号都被视为普通字符
- 反引号` `反引号和 $()一样,引号或括号里的命令会优先执行,如果存在嵌套,反撇号不能用
- 不加引号,与双引号类似,可以支持通配符
- () 开启子shell执行命令,$BASH_SUBSHELL不等于0
echo "$(hostname)"
# server
echo '$(hostname)'
# $(hostname)
echo "hello world"
# hello world
echo 'hello world'
# hello world
echo $(date +%F)
# 2018-11-22
echo `echo $(date +%F)`
# 2018-11-22
echo `date +%F`
# 2018-11-22
echo `echo `date +%F`` # 存在嵌套,反撇号失效
# date +%F
echo $(echo `date +%F`)
# 2018-11-22
(pwd;echo $BASH_SUBSHELL;whoami) # 开启子shell执行命令 BASH_SUBSHELL不等于0
变量定义
1. 直接定义
变量名=变量值 等号两边无空格
$变量名 和 ${变量名}的不同点:${变量名}可以只截取变量的一部分,而$变量名不可以
A=hello # 定义变量A
echo $A # 调用变量A
echo ${A} # 还可以这样调用
unset A # 取消变量
a=${1:-"false"} # 如果$1存在并且不为空那么a=$1,否则a=false
a=${1:="false"} # 如果$1不为空那么a=$1,否则$1=false且a=false
a=${1:+"false"} # 如果$1为空,什么都不做,否则a=false
# ? 当变量没有赋值,提示错误信息,没有赋值功能
a=${1:?"false"} # 如果$1为空,false当作stderr输出,否则输出变量值
echo ${var?} # 变量没有被赋值,提示错误信息
var=""
echo ${var:?} # 变量没有被赋值或赋空值时,提示错误信息
# 提取子串
${string:position} # 在string中从位置$position开始提取子串
echo ${A:2:4} # 表示从A变量中第3个字符开始截取,截取4个字符
# 如果$string 为"*"或"@",那么将提取从位置$position开始的位置参数
# 截取子串
${varible##*string} # 从左向右截取最后一个string后的字符串
${varible#*string} # 从左向右截取第一个string后的字符串
${varible%%string*} # 从右向左截取最后一个string后的字符串
${varible%string*} # 从右向左截取第一个string后的字符串
# 替换子串
${varible/old/new} # 匹配old,替换为new(从左往右第一个)
${varible//old/new} # 贪婪替换(替代所有)
${varible/old} # 匹配old,删除
- 取出一个目录下的目录和文件: dirname和 basename
A=/root/Desktop/shell/mem.txt
echo $A
/root/Desktop/shell/mem.txt
dirname $A # 取出目录
/root/Desktop/shell
basename $A # 取出文件
mem.txt
- 变量"内容"的删除和替换
# 一个“%”代表从右往左删除
# 两个“%%”代表从右往左去掉最多
# 一个“#”代表从左往右去掉删除
# 两个“##”代表从左往右去掉最多
url=www.taobao.com
echo ${#url} # 获取变量的长度
14
echo ${url#*.}
taobao.com
echo ${url##*.}
com
echo ${url%.*}
www.taobao
echo ${url%%.*}
www
# 替换:/ 和 //
echo ${url/ao/AO} # 用AO代替ao(从左往右第一个)
echo ${url//ao/AO} # 贪婪替换(替代所有)
2. 交互式定义
让用户自己给变量赋值,比较灵活
语法: read [选项] 变量名
常见选项
|
选项 |
释义 |
|
-p |
定义提示用户的信息 |
|
-n |
定义字符数(限制变量值的长度) |
|
-s |
不显示(不显示用户输入的内容) |
|
-t |
定义超时时间,默认单位为秒(限制用户输入变量值的超时时间) |
举例
# 用法1:用户自己定义变量值
read name
read -p "Input your name:" name
# 用法2:变量值来自文件
cat 1.txt
10.1.1.1 255.255.255.0
read ip mask < 1.txt
echo $ip
10.1.1.1
echo $mask
255.255.255.0
3. 有类型定义
目的: 给变量做一些限制,固定变量的类型,比如:整型、只读
用法: declare 选项 变量名=变量值
常用选项
|
选项 |
释义 |
举例 |
|
-i |
将变量看成整数 |
declare -i A=123 |
|
-r |
定义只读变量 |
declare -r B=hello 等于 readonly B=hello |
|
-a |
定义普通数组;查看普通数组 |
|
|
-A |
定义关联数组;查看关联数组 |
|
|
-x |
将变量通过环境导出 |
declare -x AAA=123456 等于 export AAA=123456 |
举例
declare -i A=123
echo $A
123
A=hello
echo $A
0
declare -r B=hello
echo $B
hello
B=world
-bash: B: readonly variable
unset B
-bash: unset: B: cannot unset: readonly variable
变量分类
1. 本地变量
- 当前用户自定义的变量在当前进程中有效,其他进程及当前进程的子进程无效
- 使用 source 或者 . 方式执行脚本,是在当前shell环境中执行脚本,可以保存变量
2. 环境变量
- 当前进程有效,并且能够被 子进程 调用
- env 显示当前shell中的全局变量
- set | declare 显示当前shell中的所有变量,包括全局变量和局部变量
- export 变量名=变量值 显示或设置环境变量
- unset 删除变量或函数
# 临时将一个本地变量(临时变量)变成环境变量
export A=hello
# 永久生效
# vim /etc/profile 或者 ~/.bashrc
export A=hello
# 说明:系统中有一个变量PATH,环境变量
export PATH=$PATH:/usr/local/mysql/bin
3. 全局变量
- 所有的用户和程序都能调用,且继承,新建的用户也默认能调用
相关配置文件
|
文件名 |
说明 |
备注 |
|
$HOME/.bashrc |
当前用户的bash信息,用户登录时读取 |
定义别名、umask、函数等 |
|
$HOME/.bash_profile |
当前用户的环境变量,用户登录时读取 |
|
|
$HOME/.bash_logout |
当前用户退出当前shell时最后读取 |
定义用户退出时执行的程序等 |
|
/etc/bashrc |
全局的bash信息,所有用户都生效 |
|
|
/etc/profile |
全局环境变量信息 |
系统和所有用户都生效 |
|
$HOME/.bash_history |
用户的历史命令 |
history -w 保存历史记录 history -c 清空历史记录 |
说明: 以上文件修改后,都需要重新source让其生效或者退出重新登录
- 用户登录 系统 读取 相关文件的顺序
- /etc/profile
- $HOME/.bash_profile
- $HOME/.bashrc
- /etc/bashrc
- $HOME/.bash_logout
4. 系统变量
- 即内置bash中的变量,shell本身已经固定好了它的名字和作用
|
内置变量 |
含义 |
|
$? |
上一条命令执行后返回的状态;状态值为0表示执行正常,非0表示执行异常或错误 |
|
$0 |
当前执行的程序或脚本名 |
|
$# |
脚本后面接的参数的个数 |
|
$* |
脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开 |
|
$@ |
脚本后面所有参数,参数是独立的,也是全部输出 |
|
$1~$9 |
脚本后面的位置参数,$1表示第1个位置参数,依次类推 |
|
${10}~${n} |
扩展位置参数,第10个位置变量必须用{}大括号括起来(2位数字以上扩起来) |
|
$ |
当前所在进程的进程号 |
|
$! |
后台运行的最后一个进程号 (当前终端) |
|
!$ |
调用最后一条命令历史中的参数 |
|
$PWD |
当前目录 |
|
$RANDOM |
默认会产生0~32767的随机整数 |
|
$IFS |
内部域分隔符 |
#!/bin/bash
OLD_IFS=$IFS # 保存原始值
IFS=#39;\t' # 改变IFS的值
...
...
IFS=$OLD_IFS # 还原IFS的原始值
$*:表示将变量看成一个整体,$@:表示变量是独立的
#!/bin/bash
for i in "$@"
do
echo $i
done
echo "======我是分割线======="
for i in "$*"
do
echo $i
done
# bash test.sh a b c
a
b
c
======我是分割线=======
a b c
# 打印一个随机数
echo $RANDOM
# 查看系统上一次生成的随机数
set|grep RANDOM
# 产生0~1之间的随机数
echo $[$RANDOM%2]
# 产生0~3之间的随机数
echo $[$RANDOM%4]
# 产生0~9内的随机数
echo $[$RANDOM%10]
# 产生0~100内的随机数
echo $[$RANDOM%101]
# 产生50-100之内的随机数
echo $[$RANDOM%51+50]
# 产生三位数的随机数
echo $[$RANDOM%900+100]
数*运学**算
默认情况下,shell只能支持简单的整数运算
1. 运算内容
加(+)、减(-)、乘(*)、除(/)、求余数(%)、++、+=
2. 运算符号
|
表达式 |
举例 |
|
$(( )) |
echo $((1+1)) |
|
$[ ] |
echo $[10-5] |
|
expr |
expr 10 / 5 |
|
let |
n=1; let n+=1 等价于 let n=n+1 |
数组
- 普通数组:只能使用整数作为数组索引(元素的下标)
- 关联数组:可以使用字符串作为数组索引(元素的下标)
1. 普通数组
- 一次赋予一个值
数组名[索引下标]=值
array[0]=v1
array[1]=v2
array[2]=v3
array[3]=v4
- 一次赋予多个值
数组名=(值1 值2 值3 ...)
array=(var1 var2 var3 var4)
array1=(`cat /etc/passwd`) # 将文件中每一行赋值给array1数组
array2=(`ls /root`)
array3=(harry amy jack "Miss Hou")
array4=(1 2 3 4 "hello world" [10]=linux)
2. 数组读取
${数组名[元素下标]}
echo ${array[0]} # 获取数组里第一个元素
echo ${array[*]} # 获取数组里的所有元素 *可替换为@
echo ${#array[*]} # 获取数组里所有元素个数 *可替换为@
echo ${!array[*]} # 获取数组元素的索引列表 *可替换为@
echo ${array[@]:1:2} # 访问指定的元素;1代表从下标为1的元素开始获取;2代表获取后面几个元素
# 查看普通数组信息
declare -a
# for循环遍历数据
for i in ${array[@]}
do
echo $i
done
3. 关联数组
声明关联数组
declare -A asso_array1
declare -A asso_array2
declare -A asso_array3
关联数组赋值
- 一次赋一个值
# 数组名[索引or下标]=变量值
asso_array1[linux]=one
asso_array1[java]=two
asso_array1[php]=three
- 一次赋多个值
asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss Hou")
- 查看关联数组
declare -A asso_array1='([php]="three" [java]="two" [linux]="one")'
declare -A asso_array2='([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Miss Hou")'
declare -A | egrep "asso_array[12]"
- 获取关联数组值
echo ${asso_array1[linux]} # 获取索引为liunx的值
one
echo ${asso_array1[*]} # 获取数组值
three two one
echo ${!asso_array1[*]} # 获取数组索引
php java linux
echo ${#asso_array1[*]} # 获取数组元素个数
3
- 其他定义方式
declare -A books
declare -A | grep books
let books[linux]++
declare -A | grep books
条件判断
语法格式
- test 条件表达式 更多判断使用man test查看
- [ 条件表达式 ] 两边都要有空格
- [[ 条件表达式 ]] 两边都要有空格,支持正则,两个变量的包含关系可使用=~匹配
- ((条件表达式)) 两边不需要有空格
[ ] 和[[ ]] 中对变量进行判断,需要加双引号
1. 判断文件类型
|
判断参数 |
含义 |
|
-e |
判断文件是否存在(任何类型文件) |
|
-f |
判断文件是否存在并且是一个普通文件 |
|
-d |
判断文件是否存在并且是一个目录 |
|
-L |
判断文件是否存在并且是一个软连接文件 |
|
-b |
判断文件是否存在并且是一个块设备文件 |
|
-S |
判断文件是否存在并且是一个套接字文件 |
|
-c |
判断文件是否存在并且是一个字符设备文件 |
|
-p |
判断文件是否存在并且是一个命名管道文件 |
|
-s |
判断文件是否存在并且是一个非空文件(有内容) |
|
! |
结合其他判断条件,表示取反 |
举例说明
test -e file # 只要文件存在条件为真
[ -d /shell01/dir1 ] # 判断目录是否存在,存在条件为真
[ ! -d /shell01/dir1 ] # 判断目录是否存在,不存在条件为真
[[ -f /shell01/1.sh ]] # 判断文件是否存在,并且是一个普通的文件
2. 判断文件权限
|
判断参数 |
含义 |
|
-r |
当前用户对其是否可读 |
|
-w |
当前用户对其是否可写 |
|
-x |
当前用户对其是否可执行 |
|
-u |
是否有suid,高级权限冒险位 |
|
-g |
是否sgid,高级权限强制位 |
|
-k |
是否有t位,高级权限粘滞位 |
3. 判断文件新旧
说明:这里的新旧指的是文件的修改时间
|
判断参数 |
含义 |
|
file1 -nt file2 |
比较file1是否比file2新 |
|
file1 -ot file2 |
比较file1是否比file2旧 |
|
file1 -ef file2 |
比较是否为同一个文件,或者用于判断硬连接,是否指向同一个inode |
4. 判断数值
|
在 [ ] 和 test 中使用的比较符号 |
在 (( )) 和 [[ ]] 中使用的比较符号 |
含义 |
|
-eq |
== 或 = |
相等 |
|
-ne |
!= |
不等 |
|
-gt |
> |
大于 |
|
-lt |
< |
小于 |
|
-ge |
>= |
大于等于 |
|
-le |
<= |
小于等于 |
在 [ ]、test和[[ ]]中,支持-eq字符符号,以及支持< > = !=数学符号
在 [ ]中使用数学比较符号,需要添加转移符号\
[ 1 \> 2 ] && echo yes || echo no
5. 判断字符串
|
判断参数 |
含义 |
|
-z |
判断是否为空字符串,字符串长度为0则成立 |
|
-n |
判断是否为非空字符串,字符串长度不为0则成立 |
|
string1 = string2 |
判断字符串是否相等 |
|
string1 != string2 |
判断字符串是否相不等 |
6. 逻辑运算符
|
在[ ] 和 test 中使用的操作符 |
在[[ ]] 和 (( )) 中使用的操作符 |
含义 |
举例 |
|
-a |
&& |
and,与 |
[ 1 -eq 1 -a 1 -ne 0 ] [ 1 -eq 1 ] && [ 1 -ne 0 ] |
|
-o |
|| |
or,或 |
[ 1 -eq 1 -o 1 -ne 1 ] |
|
! |
! |
not,非 |
[ ! 1 -eq 1 ] |
- && 前面的表达式为真,才会执行后面的代码
- || 前面的表达式为假,才会执行后面的代码
- ; 只用于分割命令或表达式,不考虑前面的语句是否正确执行,都会执行;号后面的内容
举例说明
- 数值比较
[ $(id -u) -eq 0 ] && echo "the user is admin"
[ $(id -u) -ne 0 ] && echo "the user is not admin"
[ $(id -u) -eq 0 ] && echo "the user is admin" || echo "the user is not admin"
uid=`id -u`
test $uid -eq 0 && echo this is admin
[ $(id -u) -ne 0 ] || echo this is admin
[ $(id -u) -eq 0 ] && echo this is admin || echo this is not admin
[ $(id -u) -eq 0 ] && echo this is admin || echo this is not admin
[[ 1 -eq 1 && 1 -ne 0 ]] && echo yes || echo no # yes
[[ ! 1 -eq 1 && 1 -ne 0 ]] && echo yes || echo no # no
- 类C风格的数值比较
# 注意:在(( ))中,=表示赋值;==表示判断
((1==2));echo $?
((1<2));echo $?
((2>=1));echo $?
((2!=1));echo $?
((`id -u`==0));echo $?
((a=123));echo $a
unset a
((a==123));echo $?
- 字符串比较
# 注意:双引号引起来,看作一个整体;= 和 == 在 [ 字符串 ] 比较中都表示判断
a='hello world';b=world
[ $a = $b ];echo $?
[ "$a" = "$b" ];echo $?
[ "$a" != "$b" ];echo $?
[ "$a" == "$b" ];echo $?
test "$a" != "$b";echo $?
test -z $a;echo $?
a=hello
test -z $a;echo $?
test -n $a;echo $?
test -n "$a";echo $?
[ '' = $a ];echo $?
[[ '' = $a ]];echo $?
[ 1 -eq 0 -a 1 -ne 0 ];echo $?
[ 1 -eq 0 && 1 -ne 0 ];echo $?
[[ 1 -eq 0 && 1 -ne 0 ]];echo $?
判断语句
1. if 结构
if [ condition ];then
command
command
fi
if test 条件;then
命令
fi
if [[ 条件 ]];then
命令
fi
[ 条件 ] && command
2. if...else 结构
if [ condition ];then
command1
else
command2
fi
[ 条件 ] && command1 || command2
3. if...elif...else 结构
if [ condition1 ];then
command1
elif [ condition2 ];then
command2
else
command3
fi
4. if 嵌套结构
if [ condition1 ];then
command1
if [ condition2 ];then
command2
fi
else
if [ condition3 ];then
command3
elif [ condition4 ];then
command4
else
command5
fi
fi
5. case语句
case为多重匹配语句,如果匹配成功,执行相匹配的命令
语法结构
# 说明:pattern表示需要匹配的模式
case var in # 定义变量;var代表是变量名
pattern 1) # 模式1;用 | 分割多个模式,相当于or
command1 # 需要执行的语句
;; # 两个分号代表命令结束
pattern 2)
command2
;;
pattern 3)
command3
;;
*) # default,不满足以上模式,默认执行*)下面的语句
command4
;;
esac # esac表示case语句结束
应用案例
# 脚本提示让用户输入需要管理的服务名,然后提示用户需要对服务做什么操作,如启动,关闭等操作
#!/bin/env bash
read -p "请输入你要管理的服务名称(vsftpd):" service
case $service in
vsftpd|ftp)
read -p "请选择你需要做的事情(restart|stop):" action
case $action in
stop|S)
service vsftpd stop &>/dev/null && echo "该$serivce服务已经停止成功"
;;
start)
service vsftpd start &>/dev/null && echo "该$serivce服务已经成功启动"
;;
esac
;;
httpd|apache)
echo "apache hello world"
;;
*)
echo "请输入你要管理的服务名称"
;;
esac
循环语句
1. for循环
用于将一组命令执行 已知的次数
1)列表循环
- 基本语法格式
for variable in {list}
do
command
command
…
done
# 或者
for variable in a b c
do
command
command
done
- 举例说明
for var in {1..10};do echo $var;done
for var in 1 2 3 4 5;do echo $var;done
for var in `seq 10`;do echo $var;done
for var in {0..10..2};do echo $var;done
for var in {2..10..2};do echo $var;done
for var in {10..1};do echo $var;done
for var in {10..1..-2};do echo $var;done
for var in `seq 10 -2 1`;do echo $var;done
2)类C风格循环
- 基本语法结构
for(( expr1; expr2; expr3 ))
do
command
command
…
done
# expr1:定义变量并赋初值
# expr2:决定是否进行循环(条件)
# expr3:决定循环变量如何改变,决定循环什么时候退出
for (( i=1;i<=5;i++))
do
echo $i
done
- 举例说明
for ((i=2;i<=10;i+=2));do echo $i;done
# 计算1-100奇数和
sum=0
for i in {1..100..2}
do
sum=$[$i+$sum]
done
echo "1-100的奇数和为:$sum"
# 或者
sum=0
for ((i=1;i<=100;i+=2))
do
let sum=$i+$sum
done
echo "1-100的奇数和为:$sum"
2. while循环
条件为真就进入循环,条件为假就退出循环
- 基本语法格式
while 表达式
do
command...
done
# 或者
while [ $i -eq 1 ] 或者 (( $i == 1 ))
do
command
command
...
done
- 应用案例
# 循环打印1-5数字
i=1
while (($i<=5))
do
echo $i
let i++
done
# 计算1-50偶数和
sum=0
i=2
while [ $i -le 50 ]
do
let sum=sum+i
let i+=2
done
echo "1-50的偶数和为:$sum"
3. until循环
条件为假就进入循环,条件为真就退出循环
- 基本语法格式
until expression
do
command
command
...
done
# 打印1-5数字
i=1
until [ $i -gt 5 ]
do
echo $i
let i++
done
4. 循环控制
循环体: do....done之间的内容
- continue;继续,表示循环体内下面的代码不执行,重新开始下一次循环
- break;打断,马上停止执行本次循环,执行循环体后面的代码
- exit;退出,表示直接退出整个程序
- shift;移位,使位置参数向左移动,默认移动1位,可以使用shift 2
5. 循环嵌套
一个循环体内又包含另一个 完整 的循环结构,称为循环的嵌套
每次外部循环都会触发内部循环,直至内部循环完成,才接着执行下一次的外部循环
for循环、while循环和until循环可以相互嵌套
- 案例
# 打印九九乘法表
# Y轴:循环9次,打印9行空行
# X轴:循环次数和Y轴相关;打印的是X和Y轴乘积 $[] $(())
# for循环
for ((y=1;y<=9;y++))
do
for ((x=1;x<=$y;x++))
do
echo -ne "$x*$y=$[$x*$y]\t"
done
echo
echo
done
# while循环
y=1
while [ $y -le 9 ]
do
x=1
while [ $x -le $y ]
do
echo -ne "$x*$y=$[$x*$y]\t"
let x++
done
echo
echo
let y++
done
# until循环
y=1
until [ $y -gt 9 ]
do
x=1
until [ $x -gt $y ]
do
echo -ne "$x*$y=$[ $x*$y ]\t"
let x++
done
echo
echo
let y++
done
函数
- shell中允许将 一组命令集合 或 语句 形成一段 可用代码 ,这些代码块称为shell函数
- 给这段代码起个名字称为函数名,后续可以直接调用该段代码的功能
1. 函数定义
# 方法1
函数名(){
函数体(一堆命令的集合,来实现某个功能)
return
}
# 方法2
function 函数名(){
函数体(一堆命令的集合,来实现某个功能)
echo hello
echo world
return
}
函数中可以定义位置参数,如 $1、$2等,在函数调用时传入位置参数即可
函数中可以使用 local关键字,定义局部变量
函数中 return可以结束一个函数,return默认返回函数中最后一个命令状态值,也可以给定参数值,范围是0-256之间
2. 函数调用
- 脚本中调用
#!/bin/bash
# 打印菜单
menu(){
cat <<-END
h 显示命令帮助
f 显示磁盘分区
d 显示磁盘挂载
m 查看内存使用
u 查看系统负载
q 退出程序
END
}
menu # 调用函数
- 当前命令行调用
cat func.sh # 函数有入参,用$1 $2 ...表示
#!/bin/bash
hello(){
echo "hello args is $1 and $2"
hostname
}
source func.sh # or
. func.sh
hello my world
- 定义到用户环境变量
# vim ~/.bashrc
文件中增加如下内容:
hello(){
echo "hello $1"
hostname
}
# 用户打开bash后可以直接使用该函数