文章目录
shell脚本结构
shell脚本编程:是基于过程式、解释执行的语言,解释一行执行一行
高级编程语言:
编译:高级语言–>编译器–>机器代码文件–>执行,如:C,C++
解析:高级语言–>执行–>解释器–>机器代码,如:shell,python,php,JavaScript
java比较特殊,java是编译和解析结合性的高级编程语言。
shell脚本执行的三种方式
1.给shell脚本赋予执行权限,再通过路径执行
chmod +x hello.sh #赋予执行权限
chmod 777 hello.sh #赋予最高的权限
“chmod +x hello.sh”后绝对路径,相对路径都可执行脚本
2.将shell脚本移动到“/usr/local/bin”目录下,便可任意目录下执行脚本
3.使用bash命令执行
[root@centos8 ~]# bash ./hi.sh
This is first shell
[root@centos8 ~]# cat hi.sh |bash
This is first shell
[root@centos8 ~]#
脚本的语法检查和逐行执行
bash -n xxx.sh
bash -x xxx.sh
脚本常见的错误有三种
1.语法错误:会导致后续的命令不继续执行,可以用bash -n 检查错误,提示的出错行不一定是准确的。
2.命令错误:默认后续的命令还会继续执行,用bash -n 无法检查出来,可以使用bash -x 进行观察。
3.逻辑错误:只能使用bash -x 进行观察。
bash -x hello.sh一行一行的执行分析错误
上图是第11行的if有错,却提示13行有语法错误。
自动生成脚本头部注释行信息
每次编写shell脚本时,自动生成/bin/bash、作者、时间等信息。将以下配置保存至后缀名为.vimrc的文件中即可。之后每次新建shell脚本时使用vim创建脚本哦(vim xxxx.sh),使用(touch xxx.sh)创建脚本就不能调用.vimrc里的函数(切记!切记!切记!)。
[root@centos8 ~]# vim .vimrc
set ignorecase
set cursorline
set autoindent
autocmd BufNewFile *.sh exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1,"#!/bin/bash")
call setline(2,"#********************************************************************")
call setline(3,"#Author: leilei")
call setline(4,"#Date: ".strftime("%Y-%m-%d"))
call setline(5,"#FileName: ".expand("%"))
call setline(6,"#Description: Annotated script")
call setline(7,"#********************************************************************")
call setline(8,"")
endif
endfunc
autocmd BufNewFile * normal G
远程执行网上的脚本资源
curl http://www.wangxiaochun.com/testdir/hello.sh | bash
# 加-s选项可以隐藏下载信息
curl -s http://www.wangxiaochun.com/testdir/hello.sh | bash
curl -s http://www.wangxiaochun.com/testdir/system_info.sh | bash
[root@centos8 ~]# curl http://www.wangxiaochun.com/testdir/hello.sh | bash
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 420 100 420 0 0 1126 0 --:--:-- --:--:-- --:--:-- 1126
hello, world
Hello, world!
[root@centos8 ~]#
[root@centos8 ~]# curl -s http://www.wangxiaochun.com/testdir/hello.sh | bash
hello, world
Hello, world!
[root@centos8 ~]#
变量
变量类型
按变量的生效范围标准来划分变量类型
变量类型
1.普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
2.环境变量:生效范围为当前shell进程及其子进程
3.本地变量:生效范围为当前shell进程中某代码片断,通常指函数
按变量的定义类型又分
1.内置变量,如:PS1, PATH, UID, HOSTNAME, $$, BASHPID, PPID, $?, HISTSIZE
2.用户自定义变量
普通变量
普通变量不具备传递性,也就是父进程的变量无法在子进程中引用。如果需要变量在子进程或者子子进程中传递则需要使用到环境变量。
变量赋值
变量的值可以来自于多个渠道
1.可以直接是一个字符串
2.也可来自于另外一个变量
3.也可以是一个linux命令的执行结果
变量赋值是临时生效(存在内存中的),当退出终端后,变量会自动删除(unset name 手动删除),无法持久保存。脚本中的变量会随着脚本结束,也会自动删除。
变量赋值:
name='value' 直接字符串
name="$USER" 变量引用
name=`COMMAND` 或者 name=$(COMMAND) 命令引用,将某个linux命令的执行结果赋值给name这个变量,使用反向单引号``和双引号“” 包裹命令都可以
删除变量
unset name
变量的垃圾回收
变量TITLE的值来自于$NAME,那么此时TITLE和NAME的值是一样的,但当NAME被赋予新值之后,TITLE的值仍然是原来的旧值,这个旧值过一段时候后会被垃圾回收。
Shell中变量命名法则
命名要求
·区分大小写
·不能使程序中的保留字和内置变量:如:if, for...
·只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线“-”,和主机名相反,主机名不能使用下划线却可以使用短横
命名习惯
·见名知义,用英文单词命名,并体现出实际作用,不要用简写,如:ATM
·变量名大写
·局部变量小写
·函数名小写
·大驼峰StudentFirstName, 由多个单词组成,且每个单词的首字母是大写,其它小写
·小驼峰studentFirstName, 由多个单词组成,第一个单词的首字母小写,后续每个单词的首字母是大写,其它小写
·下划线: student_name
while循环
写一个扫描某个主机开放哪些端口的脚本
#! /bin/bash
i=1
host=10.0.0.210
while [ $i -le 65535 ];do
if nc -z $host $i &> /dev/null; then
echo $i | tee -a port.txt
fi
let i++
done
编写脚本从1加到100
#! /bin/bash
sum=0;
i=1;
while ((i<=100));do
let sum+=i;
let i++;
done;
echo $sum
[root@rocky8 ~]# help while
while: while COMMANDS; do COMMANDS; done
Execute commands as long as a test succeeds.
Expand and execute COMMANDS as long as the final command in the
`while' COMMANDS has an exit status of zero.
Exit Status:
Returns the status of the last command executed.
不使用变量给字符串上色
#!/bin/bash
echo -e "\E[1;32mBackup is Starting...\E[0m"
tar zcf /backup/data-`date +%F_%s`.tar.gz /data/ &> /dev/null
echo -e "\E[1;32mBackup is finished!\E[0m"
使用变量给字符串上色
#!/bin/bash
COLOR='echo -e \E[1;32m'
END='\E[0m'
$COLOR"Backup is Starting..."$END
#echo -e "\E[1;32mBackup is Starting...\E[0m"
tar zcf /backup/data-`date +%F_%s`.tar.gz /data/ &> /dev/null
#echo -e "\E[1;32mBackup is finished!\E[0m"
$COLOR"Backup is finished!"$END
环境变量
1.环境变量可以使子进程(包含孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量;
2.一旦子进程修改从父进程继承的变量,会将新的值传递给孙子进程;
3.一般环境变量只在系统配置文件中使用,在脚本中较少使用。
原来如此,原来“/etc/profile”文件都是通过export来设置环境变量的(配置java环境等…)
#声明并赋值
export name=value
declare -x name=value
#或者分两步实现
name=value
export name
位置变量
脚本后面跟的字符串会赋值给系统变量$1,$2,
3....10
以上的数字需要使用
括起来即
3.... 10以上的数字需要使用{}括起来即
3....10以上的数字需要使用括起来即{10},否则会被系统识别为1和0的字符串。
#脚本中自带的变量
$1,$2,...对应第1个,第2个参数
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
注意:$@ $* 只在被双引号包起来的时候才会有差异
echo 1st is $1
echo 2st is $2
echo 3st is $3
echo 10st is ${10}
echo all args are $*
echo all args are $@
echo the number is $#
echo the scriptName is $0
应用:编写一个脚,本根据输入不同的数字参数,输出不同颜色的“color”字符串
#!/bin/bash
color=$1
echo -e "\E[1;${color}mcolor\E[0m"
$?变量
$?存放的是前一个命令的最终执行的状态值,这个状态值是一个数字。
前一个命令执行的结果如果是成功的将存在的是数字0,如果失败则存放非0,非零的范围是1~255之间。
进程执行后,将使用变量$?保存状态码的相关数字,不同的值反应成功或失败,$?取值范例0-255
$?的值为0 代表成功 (成功只有一种)
$?的值是1到255 代表失败 (失败却有很多种)
将来拿它$?来判断上一条命令是否执行成功,0就成功,非0就失败
应用:想知道某个用户是否存在,但是我不想通过眼睛去观察存不存,就执行的结果重定向(&>)到垃圾箱里
人为定制状态码
·脚本执行后的状态值是由脚本里最后一条命令决定的。以下脚本里的“exit 200”就导致脚本执行后的状态码为200.
·脚本中一旦遇到exit命令,脚本会立即终止,后续的命令将不在执行,整个脚本就结束了,终止退出状态取决于exit命令后面的数字。
·如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果。
for循环的两种写法
查看两种写法的帮助文档各个版本有所差异
centos7的帮助文档都两种写法都列出来了
centos8和rocky都只列出了一种“for: for NAME [in WORDS … ] ; do COMMANDS; done”
第一种
写一个shell脚本实现判断10.0.0.0网络里,当前在线的IP有哪些,能ping通则认为在线。
[root@rocky8 scripts]# for i in 1 2 3 4; do echo i=$i;done
i=1
i=2
i=3
i=4
求和1加到100
[root@rocky8 scripts]# sum=0;for i in {1..100};do let sum+=i;done;echo sum=$sum
sum=5050
[root@rocky8 scripts]#
打印九九乘法表
#! /bin/bash
for i in {1..9};do
for j in `seq $i`;do
echo -e "${j}X${i}=$[i*j]\t\c"
done
echo
done
第二种
类似于Java的for循环
字符串测试
test和[]字符串测试用法
-z string 字符串是否为空,没定义或空为真,不空为假,
-n string 字符串是否不空,不空为真,空位假
String1 = String2 是否相等,注意 = 前后有空格
String1 != String2 是否不等于
条件测试
test expression
[ expression ] #和test等价,建议使用[]
[[ experssion ]] 相当于增强版的[],支持[]的用法,且支持扩展正则表达式和通配符
注意expression前后必须有空白字符
脚本案例
system_info.sh
#!/bin/bash
RED="\E[1;34m"
GREEN="echo -e \E[1;35m"
END="\E[0m"
. /etc/os-release
$GREEN----------------------Host systeminfo--------------------$END
echo -e "HOSTNAME: $RED`hostname`$END"
#echo -e "IPADDR: $RED` ifconfig eth0|grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' |head -n1`$END"
echo -e "IPADDR: $RED` hostname -I`$END"
echo -e "OSVERSION: $RED$PRETTY_NAME$END"
echo -e "KERNEL: $RED`uname -r`$END"
echo -e "CPU: $RED`lscpu|grep '^Model name'|tr -s ' '|cut -d : -f2`$END"
echo -e "MEMORY: $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"
#echo -e "DISK: $RED`lsblk |grep '^sd' |tr -s ' ' |cut -d " " -f4`$END"
echo -e "DISK: $RED`lsblk |grep '^nv' |tr -s ' ' |cut -d " " -f4`$END"
$GREEN---------------------------------------------------------$END