生效范围为当前shell,该shell之后生成的子shell都会带有该变量内容,但是该shell子shell定义的全局变量不会继承给上级shell
[root@localhost ~]# export a=c
[root@localhost ~]# echo $a
c
[root@localhost ~]# bash
[root@localhost ~]# echo $a
c
[root@localhost ~]# export b=v
[root@localhost ~]# echo $b
v
[root@localhost ~]# exit
exit
[root@localhost ~]# echo $b
只在当前shell中生效,不会对子shell生效
[root@localhost ~]# a=c
[root@localhost ~]# bash
[root@localhost ~]# echo $a
根据参数的位置,定义变量名称,$1$2$3…
$1 # 第一个参数
$2 # 第二个参数
$10 # 10之后的变量都需要使用 {} 来对变量名称扩起,为了生命主体
$@ # 输出所有参数
$# # 输出位置参数个数
$* # 与 $@ 差不多相同
[root@localhost ~]# cat sh.sh
#!/bin/bash
echo $1
echo $2
echo ${10}
echo $@
echo $#
echo $*
[root@localhost ~]# bash sh.sh a b c d e f j l z m c
a
b
m
a b c d e f j l z m c
11
a b c d e f j l z m c
# 当我们在 shell 脚本中定义了循环主体为 $@ 后那么输出的结果就是单个的字符进程处理
# 当我们在 shell 脚本中定义了循环主题为 $* 后那么输出结果就是所有字符串一起输出,作为一个整体
[root@localhost ~]# cat sh.sh
#!/bin/bash
for i in "$@"
do
echo $i
done
for j in "$*"
do
echo $j
done
[root@localhost ~]# bash sh.sh a b c d e f g
a
b
c
d
e
f
g
a b c d e f g
普通变量就是普通赋值
不能使用 shell 的保留字及内置变量进行赋值,例如 for if 等
变量名必须以数字或字母以及下划线,变量名中不可以有 - 来作为变量名
# 使用字符串直接赋值
[root@localhost ~]# Name="My name is jason_chen"
[root@localhost ~]# echo $Name
My name is jason_chen
# 变量之间赋值
[root@localhost ~]# name="chen"
[root@localhost ~]# Name=$name
[root@localhost ~]# echo $Name
chen
# 使用 `` 反撇号赋值命令执行结果
[root@localhost ~]# ls_data=`ls`
[root@localhost ~]# echo $ls_data
anaconda-ks.cfg Desktop Documents Downloads install.log install.log.syslog Music Pictures Public sh.sh Templates Videos
# 变量追加值
[root@localhost ~]# Name=chen
[root@localhost ~]# age=19
[root@localhost ~]# Name+=$age
[root@localhost ~]# echo $Name
chen19
[root@localhost ~]# Name=chen
[root@localhost ~]# Name+=:$age
[root@localhost ~]# echo $Name
chen:19
# 使用 ${} 进行调用变量,相当于声明变量主体来进行调用
[root@localhost ~]# echo ${Name}
chen
# 使用 "" 调用变量,变量在 "" 会输出变量内存储的内容
[root@localhost ~]# echo "$Name"
chen
# 使用 '' 调用变量,变量在 '' 不会输出存储内容,只会将变量名相当于一个字符串进行输出
[root@localhost ~]# echo '$Name'
$Name
# 如果想要输出两个变量的组合,但是如果中间有特殊字符将两个变量隔开后,那么系统在识别变量的时候将会识别到 $Name_ 而不是 $Name 所以输出结果就只有 $age 的内容
[root@localhost ~]# echo $Name_$age
19
# 使用 ${} 将变量扩起,这样就不会造成系统识别变量错误的情况了
[root@localhost ~]# echo ${Name}_$age
chen_19
# 直接调用与加上双引号调用的区别,根据变量名直接调用 echo 只是将结果整体输出,作为一个整体的字符串,而如果加上双引号的话输出结果会根据位置来单个输出
[root@localhost ~]# echo $ls_data
anaconda-ks.cfg Desktop Documents Downloads install.log install.log.syslog Music Pictures Public sh.sh Templates Videos
[root@localhost ~]# echo "$ls_data"
anaconda-ks.cfg
Desktop
Documents
Downloads
install.log
install.log.syslog
Music
Pictures
Public
sh.sh
Templates
Videos
[root@localhost ~]# Name=chen
[root@localhost ~]# echo $Name
chen
[root@localhost ~]# unset Name
[root@localhost ~]# echo $Name
类似于浏览器返回的 403、404 脚本中也可以自己定义返回值,来根据返回值判断脚本执行状况
# $? 的执行,成功后返回结果为 0,失败后返回结果为 1-255
[root@localhost ~]# ls /date/
error.log seccess.log
[root@localhost ~]# echo $?
0
# 此时我的 / 目录下并没有 /aaa 目录,所以在查看一个不存在的目录时当然会报错
[root@localhost ~]# ls /aaa
ls: cannot access /aaa: No such file or directory
[root@localhost ~]# echo $?
2
# $? 的使用,通过 if 直接来判断 $? 的值,来返回结果
[root@localhost ~]# ls /aaa; if [ $? -eq 0 ];then echo "yes"; else echo "no"; fi
ls: cannot access /aaa: No such file or directory
no
[root@localhost ~]# ls /date; if [ $? -eq 0 ];then echo "yes"; else echo "no"; fi
error.log seccess.log
yes
自定义返回值
# 如果自定义返回值后,不论你的命令执行成功与否都会返回自己定义的返回值,在脚本中使用该 exit 命令来判断脚本执行状态是很有用的哇!
[root@localhost ~]# (ls /data/;exit 100)
ls: cannot access /data/: No such file or directory
[root@localhost ~]# echo $?
100
[root@localhost ~]# (ls /date;exit 100)
error.log seccess.log
[root@localhost ~]# echo $?
100
# 可以使用转义符 \ 来取消特殊符号的特殊含义
# '' 双引号防止所有扩展
# "" 防止扩展,但是如果加上 $ 符号后就不会防止扩展
1. 将命令行分成单个命令词
2. 展开别名
3. 展开大括号的声明 {}
4. 展开波浪符生命 ~
5. 命令替换 $() 和 ``
6. 再次命令分解成命令词
7. 展开文件通配符 *\?\[abc] 等
8. 准备 I/O 重定向
9. 运行命令
[root@localhost ~]# echo $-
himBH
i:表示该 shell 是一个交互式的终端,默认情况在脚本执行时 i 是关闭的
m:打开监控模式,可以通过 job 来控制进程的后台
# h:打开选项后,shell 会将命令所在的路径 hash 记录下来,避免下次执行命令时还会查询。
[root@localhost ~]# hash
hits command
1 /bin/ls
1 /bin/find
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# set +h
[root@localhost ~]# hash
-bash: hash: hashing disabled
# B:大括号扩展
[root@localhost ~]# echo {1..100}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
# H:history 命令,!n 即可执行 history 中的历史命令
[root@localhost ~]# set +B
[root@localhost ~]# echo {1..100}
{1..100}
909 ls
910 history
[root@localhost ~]# !909
-bash: !909: command not found
[root@localhost ~]# set -H
[root@localhost ~]# !909
ls
anaconda-ks.cfg Documents install.log Music Public Templates
Desktop Downloads install.log.syslog Pictures sh.sh Videos
# set -u 选项,如果脚本中的变量没有定义,那么会直接结束脚本,不会继续执行
[root@localhost ~]# cat sh.sh
#!/bin/bash
echo $a
[root@localhost ~]# sh sh.sh
[root@localhost ~]# cat sh.sh
#!/bin/bash
set -u
echo $a
set +u
[root@localhost ~]# sh sh.sh
sh.sh: line 3: a: unbound variable
# set -e 选项,如果脚本中任何一个位置返回一个非零的返回值就会结束脚本
[root@localhost ~]# printf "(%s)\n" 1 2 3 4
(1)
(2)
(3)
(4)
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# printf "(%s)" 1 2 3 4
(1)(2)(3)(4)
# 将命令放在每个文件最前
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
~/.bash_profile
~/.bashrc
/etc/bashrc
# 将命令放在每个文件最后
/etc/profile.d/*.sh
/etc/bashrc
/etc/profile
/etc/bashrc
~/.bashrc
~/.bash_profile
profile 类
为交互式登陆的 shell 提供配置
全局:/etc/profile /etc/profile.d/*.sh
个人:~/.bash_profile
bashrc 类
为非交互式登录和交互式登陆的 shell 提供配置
全局:/etc/bashrc
个人:~/.bashrc
source [filename] # 可以让文件配置立即生效
* / % # 乘,除,(取余取模)
+ - # 加、减
i++ i-- # 自增、自减
++i --i # 自增、自减
i++与++i的区别
[root@localhost ~]# i=1;let var=i++;echo $var
1
[root@localhost ~]# i=1;let var=++i;echo $var
2
= *= /= %= += -= <<= >>= &= ^= |=
<<= >>= # 是二进制移位
! ~ # 取反
** # 乘方
<< >> # 移位
<= >= < > # 大于小于
== != # 等于,不等于
& # and 符号
| # or 符号
^ # 异或符号
&& # 逻辑 and 符号
|| # 逻辑 or 符号
# let 计算方式
[root@localhost ~]# let var=1+1
[root@localhost ~]# echo $var
2
# (()) 计算方式
[root@localhost ~]# ((var=1*3))
[root@localhost ~]# echo $var
3
# $[] 计算方式
[root@localhost ~]# var=$[1*4]
[root@localhost ~]# echo $var
4
$RANDOM
# 生成一个 0-50 的随机数
[root@localhost ~]# echo $((RANDOM%50))
26
[root@localhost ~]# echo $[RANDOM%50]
45
根据布尔值决定后面的命令执行
& and 符号,前一个命令执行后,后一个才可以继续执行
| or 符号,前一个执行后一个就不会执行
^ 异或符号,根据二进制取出异或值
test [expression]
[ expression ] # 与 test 相同
[[ expression ]] # []的升级版
-a # 测试文件是否存在
-b # 测试文件是否为块设备文件
-c # 测试文件是否为字符设备文件
-d # 测试文件是否为目录
-e # 测试文件是否存在
-f # 测试文件是否为普通文件
-h # 测试文件是否为链接文件
-L # 与 -h 相同
-r # 测试我呢见是否刻度
-s # 测试文件是否为空
-w # 测试文件是否可写
-x # 测试文件是否可执行
-z # 测试文件是否为空
-n # 测试文件是否不为空
-v # 测试变量是否定义
# -v 测试变量是否定义
[root@DNS ~]# test -v $b
[root@DNS ~]# echo $?
0
[root@DNS ~]# b=10
[root@DNS ~]# test -v $b
[root@DNS ~]# echo $?
1
[root@DNS ~]# [[ -v $b ]]
[root@DNS ~]# echo $?
1
[root@DNS ~]# [ -v $b ]
[root@DNS ~]# echo $?
1
-eq 是否等于
-lt 是否小于
-le 是否小于等于
-gt 是否大于
-ge 是否大于等于
-ne 是否不等于
# 定义变量并使用数值判断,来判断变量内容是否大于等于或者相等
[root@DNS ~]# i=10
[root@DNS ~]# j=20
[root@DNS ~]# [ $i -eq $j ]
[root@DNS ~]# echo $?
1
[root@DNS ~]# [ $i -lt $j ]
[root@DNS ~]# echo $?
0
[root@DNS ~]# [ $i -gt $j ]
[root@DNS ~]# echo $?
1
[root@DNS ~]# [[ $i -gt $j ]]
[root@DNS ~]# echo $?
1
算数表达比较
== 等于
!= 不等于
<= 小于等于
>= 大于等于
> 大于
< 小于
[root@DNS ~]# x=10;y=10;[ $x == $y ];echo $?
0
[root@DNS ~]# x=10;y=10;((x == y ));echo $?
0
字符串表达比较
# -z 测试字符串是否为空,没定义或者为空为真,只要定义不空就是假
[root@DNS ~]# [ -z $x ]
[root@DNS ~]# echo $?
0
[root@DNS ~]# x=10
[root@DNS ~]# [ -z $x ]
[root@DNS ~]# echo $?
1
# -n 测试字符串是否不空,没定义或者为空为假,只要定义不空就是真
# 变量必须要加双引号,因为如果不加双引号 -n 就是判断的字符串大于 0,即为真,如果加上双引号,那么 -n 选项判断的就是该变量内的数据,而不是变量名本身。
[root@localhost ~]# [ -n "$x" ]
[root@localhost ~]# echo $?
1
[root@localhost ~]# x=asd
[root@localhost ~]# [ -n "$x" ]
[root@localhost ~]# echo $?
0
[[ expression ]] 用法
== 左侧字符串是否与右侧的 PATTERN 相同
=~ 左侧字符串是否能被右侧的表达式匹配
通配符*
[root@localhost ~]# File=ab;[[ $File == a* ]]
[root@localhost ~]# echo $?
0
[root@localhost ~]# File=a*;[[ $File == a\* ]]
[root@localhost ~]# echo $?
0
[root@localhost ~]# File=a*;[[ $File == a"*" ]]
[root@localhost ~]# echo $?
0
通配符 ?
[root@localhost ~]# File=123;[[ $File == ?? ]];echo $?
1
[root@localhost ~]# File=123;[[ $File == ??? ]];echo $?
0
例子:判断合理的成绩
[root@localhost ~]# score=100;[[ $score =~ ^(100|[0-9][0-9])$ ]]
[root@localhost ~]# echo $?
0
[root@localhost ~]# score=101;[[ $score =~ ^(100|[0-9][0-9])$ ]]
[root@localhost ~]# echo $?
1
例子:使用 [[ ]] 判断文件后缀
[root@localhost ~]# file=a.txt;[[ $file == *.txt ]]
[root@localhost ~]# echo $?
0
[root@localhost ~]# file=a.txt;[[ $file =~ \.txt$ ]]
[root@localhost ~]# echo $?
0
例子:判断非负整数
[root@localhost ~]# num=123a;[[ $num =~ ^[0-9]+$ ]]
[root@localhost ~]# echo $?
1
[root@localhost ~]# num=a123;[[ $num =~ ^[0-9]+$ ]]
[root@localhost ~]# echo $?
1
[root@localhost ~]# num=1a23;[[ $num =~ ^[0-9]+$ ]]
[root@localhost ~]# echo $?
1
[root@localhost ~]# num=123;[[ $num =~ ^[0-9]+$ ]]
[root@localhost ~]# echo $?
0
例子:判断合法 IP
[root@localhost ~]# ipadd=224.0.0.1 ;[[ $ipadd =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-5]{2})\.){3}([0-9]|1[0-9]{2}|2[0-5]{2})$ ]]&&echo $ipadd
224.0.0.1
() 会创建一个子进程来执行括号中的命令,更加安全,会继承父进程非环境变量的变量。
{} 在当前进程执行,会修改当前的 shell 环境。
[root@localhost ~]# name=chen;(echo $name;name=ding;echo $name);echo $name
chen
ding
chen
[root@localhost ~]# name=chen;{ echo $name;name=ding;echo $name; };echo $name
chen
ding
ding
# 只支持 [] 不支持 [[ ]]
-a 并且,按位与
-o 或者,按位或
[root@localhost ~]# a=10;b=10; [ $a == $b -a $b == $a ] && echo "chen"
chen
[root@localhost ~]# a=10;b=10; [ $a == $b -o $x == $a ] && echo "chen"
chen
read 控制用户输入内容
-p 显示提示信息
-s 静默输入
-n 定义输入字符疮毒
-d '结束符' 定义结束符
-t 定义结束时间
[root@localhost ~]# echo "1 2" >>1.txt ;read a b <1.txt;echo $a+$b|bc
3
[root@localhost ~]# echo 1 2|(read z v;echo $z $v);echo $z $v
1 2
[root@localhost ~]# echo 1 2|{ read z v;echo $z $v;};echo $z $v
1 2
[root@localhost ~]# echo 1 2|read z v;echo $z $v
#!/bin/bash
read -p "Please ping hostipaddress ---> " ipaddr
[[ $ipaddr =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-5]{2})\.){3}([0-9]|1[0-9]{2}|2[0-5]{2})$ ]] && ping -w 1 $ipaddr && echo "该主机通信正常!" || echo "该主机通信有故障!"
升级版
[root@localhost test]# cat hostip.list
172.16.1.254
172.16.1.9
123.123.3.4
www.baidu.com
asd.awd.asdzx.
#!/bin/bash
DIR=/date
success="seccess.log"
error="error.log"
function access() {
cmd='echo `date +%F` $i >>$DIR/$1'
[[ -e $DIR ]] && eval $cmd || (mkdir $DIR && eval $cmd)
}
for i in `cat /test/hostip.list`
do
ping -w 1 $i
if [ $? -eq 0 ];then
access $success
else
access $error
fi
done
范例:写出身体质量指数测试脚本
#!/bin/bash
clear
echo "欢迎使用傻妞侧重仪器!"
cat <<EOF
---------
*
* *
*******
* *
* *
---------
EOF
read -p "请输入你的身高(m):" height
read -p "请输入你的体重(kg):" weight
BMI=`echo ${weight}/${height}^2 | bc`
if [[ $BMI -ge 18 && $BMI -le 25 ]]
then
echo "太瘦了,多吃点吧,不然要死掉了"
elif [[ $BMI -ge 26 && $BMI -le 30 ]]
then
echo "哇塞,这就是玉树临风的你吗,终于见到你了,你一点都不胖呢,你是最帅的!"
elif [[ $BMI -ge 31 && $BMI -le 35 ]]
then
echo "你TM为什么这么胖呢!!"
elif [[ $BMI -ge 36 && $BMI -le 40 ]]
then
echo "疾病将伴你而来"
else
echo "神仙下凡,抱歉,我测不出来"
fi
范例:史上最牛逼用户管理软件
#!/bin/bash
function view() {
clear
echo "恭喜你开启了新大门,你的成人之路从现在开始!come on baby!"
cat <<EOF
---1 useradd
---2 vive user
---3 deluser
---4 quit
EOF
read -p "Please is num" num
}
function image() {
cat <<EOF
┏┓ ┏┓
┏┛┻━━━┻┗┓
┃ ┃
┃ ━ ┃
┃ ┳┛ ┗┳ ┃
┃ ┃
┃ ┻ ┃
┗━┓ 新 ┏━┛
┃ 东 ┃
┏━━━┛ 方 ┃
┏┫ 神 ┃
┗┓ *** 兽 ┃
┗┓┏┳━┓┏┏┛
┣┣┃ ┣┣┃
┗┻┛ ┗┻┛
EOF
}
function judgment() {
read -p "y/y/y/y/y/y/y/y not found no:" useridea
[[ $useridea =~ ^([yY])$ ]] && (echo "再给你一次机会,重新来,就知道你舍不得我!"; break)
}
read -p "Please username " username
while true
do
id $username &> /dev/null
if [ $? -eq 0 ]
then
clear
echo "该用户已经存在啦,你还想干嘛,不要对我有非分之想!"
read -p "go out (Y/N)" nnn
[[ $nnn =~ ^([Nn]|[Nn][oO])$ ]] && view || ([[ $nnn =~ ^([Yy]|[Yy][Ee][Ss])$ ]] && echo "呸,给你机会你不重用啊!")
case $num in
1)
clear
image
useradd $username
read -p "请输入你进入成人之路的钥匙,美眉在等着你:" passwd
echo $username:$passwd | chpasswd $username
[[ $? -eq 0 ]] && echo "创建成功" || echo "失败了呀,行不行啊,就这???"
echo -n "再给你一次机会,不要墨迹!"
judgment
;;
2)
clear
image
read -p "请问你要使用哪种方式 insert /大写“I” 小写“i” : " viewuser
[[ $viewuser =~ ^([I]|[i])$ ]] && id $username || echo "重新执行吧,不给你机会了 go out"
exit
;;
3)
clear
image
echo -n "你真的要把我删掉了吗?"
judgment
;;
4)
clear
image
read -p "跟我说声谢谢,快点!" Thks
[[ $Thks == "谢谢" ]] && echo "滚吧!渣男!";exit 999;
;;
esac
else
echo "没有就没有吧,你再看看其他地方,爱有没有,滚犊子"
fi
done
面试题:1-100 的和
[root@DNS test]# seq 100|paste -s|tr -s '\t' '+'|bc
[root@DNS test]# seq 100|paste -sd +|bc
[root@DNS test]# sum=0;for i in {1..100};do let sum+=$i; done;echo $sum
5050
面试题:1-100 中的偶数和
[root@DNS test]# echo {2..100..2} | tr ' ' '+'|bc
2550
[root@DNS test]# seq 2 2 100|paste -sd +|bc
2550
[root@DNS test]# sum=0;for i in {1..100};do ((i%2 == 0)) && let sum+=$i; done;echo $sum
2550
面试题:1-100 中的奇数和
[root@DNS test]# echo {1..100..2}|tr ' ' '+'|bc
2500
[root@DNS test]# seq 1 2 100|paste -sd +|bc
2500
[root@DNS test]# sum=0;for i in {1..100};do ((i%2 == 0 )) || let sum+=$i; done; echo $sum
2500
面试题:创建 1…10user 十个用户,并设置随机密码
#!/bin/bash
for i in {1..10}
do
passwd=`cat /dev/urandom|tr -dc "[:alnum:]"|head -c12`
useradd user$i
echo user${i}:${passwd} |tee -a 1.txt |chpasswd user${i}
done
面试题:批量修改文件后缀名
#!/bin/bash
DIR=/test/demo/
cd $DIR
for i in *
do
filename=`echo $i | grep -Eo '.*\.'`;
mv $i ${filename}bak
done
面试题:要将YY-MM-DD下的所有文件移动到YY-MM/DD目录中
mkdir file.sh
#!/bin/bash
for i in {1..365}
do
DIRNAME=`date -d "-$i day" +%F`
[ -d /test/test ] && mkdir -p /test/test/$DIRNAME || mkdir -p /test/test/
cd /test/test/$DIRNAME
touch ${RANDOM}.log
done
cat mvfile.sh
#!/bin/bash
for i in *
do
DIR=`echo $i | cut -d '-' -f1,2`
DD=`echo $i | cut -d '-' -f3`
[ -d /data/demo/$DIR/$DD ] || mkdir -p /data/demo/$DIR/$DD/
mv ${i}/* /data/demo/$DIR/$DD/
done
例题:判断 /var 下面所有文件类型
#!/bin/bash
for filename in `du -a /var/|grep -Eo "/.*[a-z]$"`
do
[[ -d $filename ]] && echo "$filename is directory" >>/data/directory.list
[[ -f $filename ]] && echo "$filename is file" >> /data/file.list
[[ -b $filename ]] && echo "$filename is block_file" >> /data/block.list
[[ -L $filename ]] && echo "$filename is link_file" >> /data/link_file.list
done
例题:更改 /etc/rc.d/rc3.d 目录下有多个文件,将 K 开头的加上 stop 以 S 开头的加上 start
#!/bin/bash
for i in *
do
[[ `echo $i|head -c 1` -eq 'S' ]] && `mv $i $i'start'`
[[ `echo $i|head -c 1` -eq 'K' ]] && `mv $i $i'start'`
done
例题:每隔 3 秒钟到系统上获取已经登陆的用户的信息,如果发现用户 chending 登录,那么就记录登陆的时间并记录到日志 /var/log/login.log 中
#!/bin/bash
while true
do
[[ $(who | grep chen) && $(eval "date '+%F %H:%M:%S':chending已经登录" >>/var/log/login.log;) ]]
sleep 3
done
例题:随机生成 10 以内的数字,实现猜字游戏,提示比较大小,相等就退出
#!/bin/bash
while true
do
random=$[RANDOM%10]
read -p "请输入你的幸运数字:" num
[[ random -eq num ]] && ( echo "你是真 TM 牛逼,猜中了" ) && exit || ( echo "重来吧姐们/哥们!" )
done