第一步:使用文本编辑器来创建文本文件
第一行:必须包括shell 声明序列:#!
示例:#!/bin/bash
第二步:加执行权限
给予执行权限,在命令行上指定脚本的绝对或相对路径
第三步:运行脚本
直接运行解释器,将脚本作为解释器程序的参数运行
范例:第一个shell 脚本 hello world
#!/bin/bash
echo "Hello,world!"
执行:
bash hell.sh
shell 脚本调试
只检测脚本中的语法错误,但无法检查出命令错误,但不真正执行脚本
bash -n /path/some_script.sh
调试并执行
bash -x /path/some_script.sh
cat -A test.sh
#!/bin/bash
echo line1$
hostnam$
cat > test.txt <<EOF $
aaa$
bbb$
ccc$
EOF $
$
echo line22$
[root@centos8 scripts]#bash -n test.sh
f1.sh: line 20: warning: here-document at line 14 delimited by end-of-file
(wanted `EOF')
总结:脚本错误常见的有三种
1)语法错误。会导致后续的命令不继续执行,可以用bash -n 检查错误,提示的出错行数不一定是准确的
2)命令错误。默认后续的命令还会继续执行,用bash -n 无法检查出来,可以使用 bash -x 进行观察
3)逻辑错误。只能使用 bash -x 进行观察
1,变量
变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据
2,变量类型
1)内置变量:如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE
2)用户自定义变量
不同的变量存放的数据不同,决定了以下
1)数据存储方式
2)参与的运算
3)表示的数据范围
变量数据类型
字符
数值:整型,浮点型,bash不支持浮点数
静态和动态语言
1)静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如:java,c
2)动态编译语言:不用事先声明,可随时改变类型,如:bash,Python
强类型和弱类型语言
1)强类型语言:不同类型数据操作,必须经过强制转换才同一类型才能运算,如:java,c#,Python
如:参考以下Python代码
print('long'+10) 提示出错,不会自动转换类型
print('long'+str(10)) 结果为long10,需要显示转换类型
2)弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用。如:bash,php,JavaScript
3,shell 中变量命名法则
1)不能使用程序中的保留字:如 :if,for等
2)只能使用数字,字母及下划线,且不能以数字开头,注意:不支持短横线“-”,和主机名相反
3)见名知意,用英文单词命名,并体现出实际作用,不要用简写, 如:ATM
4)统一命名规则:驼峰命名法;大驼峰 StudentName,小驼峰studentName
5)变量名大写:STUDENT_NAME
6)局部变量小写
7)函数名小写
4,变量的生效范围等标准划分变量类型
1)普通变量:生效范围为当前shell 进程;对当前shell 之外的其它shell 进程,包括当前shell 的子shell 进程均无效
2)环境变量:生效范围为当前shell 进程及其子进程
3)本地变量:生效范围为当前shell 进程中某代码片断,通常指函数
变量赋值
name='value'
value 可以是以下多种形式
直接字串:name='root'
变量引用:name='$USER'
命令引用:name=`COMMAND` 或 name=$(COMMAND)
注意:变量赋值是临时生效。当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚本结束,也会自动删除
变量引用:
$name
${name}
弱引用和强引用
1)"$name" 弱引用,其中的变量引用会被替换为变量值
2)'$name' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串
范例:变量的各种赋值方式和引用
[root@centos8 ~]#TITLE='cto'
[root@centos8 ~]#echo $TITLE
cto
[root@centos8 ~]#echo I am $TITLE
I am cto
[root@centos8 ~]#echo "I am $TITLE"
I am cto
[root@centos8 ~]#echo 'I am $TITLE'
I am $TITLE
[root@centos8 ~]#NAME=$USER
[root@centos8 ~]#echo $NAME
root
[root@centos8 ~]#USER=`whoami`
[root@centos8 ~]#echo $USER
root
[root@centos8 ~]#FILE=/etc/*
[root@centos8 ~]#echo $FILE
/etc/adjtime /etc/aliases /etc/alternatives /etc/anacrontab /etc/at.deny
/etc/audit /etc/authselect /etc/autofs.conf /etc/autofs_ldap_auth.conf
[root@centos8 ~]#seq 10
1
2
3
4
5
6
7
8
9
10
[root@centos8 ~]#NUM=`seq 10`
[root@centos8 ~]#echo $NUM
1 2 3 4 5 6 7 8 9 10
[root@centos8 ~]#NAMES="wang
> zhang
> zhao
> li"
[root@centos8 ~]#echo $NAMES
wang zhang zhao li
[root@centos8 ~]#echo "$NAMES"
wang
zhang
zhao
li
范例:变量引用
[root@centos8 data]#NAME=long
[root@centos8 data]#AGE=20
[root@centos8 data]#echo $NAME
long
[root@centos8 data]#echo $AGE
20
[root@centos8 data]#echo $NAME $AGE
long 20
[root@centos8 data]#echo $NAME$AGE
long20
[root@centos8 data]#echo $NAME_$AGE
20
[root@centos8 data]#echo ${NAME}_$AGE
long_20
范例:变量的间接赋值和引用
[root@centos8 ~]#TITLE=cto
[root@centos8 ~]#NAME=wang
[root@centos8 ~]#TITLE=$NAME
[root@centos8 ~]#echo $NAME
wang
[root@centos8 ~]#echo $TITLE
wang
[root@centos8 ~]#NAME=long
[root@centos8 ~]#echo $NAME
long
[root@centos8 ~]#echo $TITLE
wang
范例:变量追加值
[root@centos8 ~]#TITLE=CTO
[root@centos8 ~]#TITLE+=:wang
[root@centos8 ~]#echo $TITLE
CTO:wang
范例:利用变量实现动态命令
[root@centos8 ~]#CMD=hostname
[root@centos8 ~]#$CMD
centos8.longwang.com
显示已定义的所有变量:set
删除变量:unset <name>
[root@centos8 ~]#NAME=long
[root@centos8 ~]#TITLE=ceo
[root@centos8 ~]#echo $NAME $TITLE
long ceo
[root@centos8 ~]#unset NAME TITLE
[root@centos8 ~]#echo $NAME $TITLE
范例:显示系统信息
#!/bin/bash
#
#********************************************************************
#Author: xuanlv
#QQ: 360956175
#Date: 2020-06-15
#FileName: system_info.sh
#URL: http://www
#Description: The test script
#Copyright (C): 2020 All rights reserved
#********************************************************************
RED="E[1;31m"
GREEN="echo -e E[1;32m"
END="E[0m"
$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 "OSVERSION: $RED`cat /etc/redhat-release`$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 '^nv' |tr -s ' ' |cut -d " " -f4`$END"
$GREEN------------------------------------------------------------$END
5,环境变量
1)可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量
2)一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程
3)一般只在系统配置文件中使用,在脚本中较少使用
变量声明和赋值
#声明并赋值
export name=VALUE
declare -x name=VALUE
#或者分两步实现
name=VALUE
export name
显示所有环节变量
env
printenv
export
declare -x
bash 内建的环境变量
PATH
SHELL
USER
UID
HOME
PWD
SHLVL # shell 的嵌套层数,即深度
LANG
HOSTNAME
HISTIZE
_ # 下划线 表示前一个命令的最后一个参数
只读变量:只能声明定义,但后续不能修改和删除,即常量
声明只读变量:
readonly name
declare -r name
查看只读变量
readonly [-p]
declare -r
[root@centos8 ~]#readonly PI=3.14159
[root@centos8 ~]#echo $PI
3.14159
[root@centos8 ~]#PI=3.14
-bash: PI: readonly variable
[root@centos8 ~]#unset PI
-bash: unset: PI: cannot unset: readonly variable
[root@centos8 ~]#echo $PI
3.14159
[root@centos8 ~]#exit
logout
6,位置变量
位置变量:在bash shell 中内置的变量,在脚本代码中调用通过命令行传递给脚本的参数
$1,$2,... 对应第1个,第2个等参数, shift [n]换位置
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
注意:$@ $* 只在被双引号包起来的时候才会有差异
清空所有位置变量
set --
[root@centos8 ~]#cat /data/scripts/arg.sh
#!/bin/bash
#
#********************************************************************
#Author: xuanlv
#QQ: 360956175
#Date: 2020-06-20
#FileName: arg.sh
#URL: http://www.
#Description: The test script
#Copyright (C): 2020 All rights reserved
#********************************************************************
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${10}"
echo "11st arg is ${11}"
echo "The number of arg is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@centos8 ~]#bash /data/scripts/arg.sh {a..z}
1st arg is a
2st arg is b
3st arg is c
10st arg is j
11st arg is k
The number of arg is 26
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
The scriptname is arg.sh
范例:删库跑路之命令rm的安全实现
#!/bin/bash
WARNING_COLOR="echo -e E[1;31m]"
END="E[0m"
DIR=/tmp/`date +%F_%H-%M-%S`
mkdir $DIR
mv $* $DIR
${WARNING_COLOR}Move $* to $DIR $END
[root@centos8 ~]#chmod a+x /data/scripts/rm.sh
[root@centos8 ~]#alias rm='/data/scripts/rm.sh'
[root@centos8 ~]#touch {1..10}.txt
[root@centos8 ~]#rm *.txt
Move 10.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt to /tmp/2020-
06-20_15-15-28
范例:$* 和$@ 的区别
[root@centos8 scripts]#cat f1.sh
#!/bin/bash
echo "f1.sh:all args are $@"
echo "f1.sh:all args are $*"
./file.sh "$*"
[root@centos8 scripts]#cat f2.sh
#!/bin/bash
echo "f2.sh:all args are $@"
echo "f2.sh:all args are $*"
./file.sh "$@"
[root@centos8 scripts]#cat file.sh
#!/bin/bash
echo "file.sh:1st arg is $1"
[root@centos8 scripts]#./f1.sh a b c
f1.sh:all args are a b c
f1.sh:all args are a b c
file.sh:1st arg is a b c
[root@centos8 scripts]#./f2.sh a b c
f2.sh:all args are a b c
f2.sh:all args are a b c
file.sh:1st arg is a
范例: 利用软链接实现同一个脚本不同功能
[root@centos8 ~]#cat test.sh
#!/bin/bash
#********************************************************************
echo $0
[root@centos8 ~]#ln -s test.sh a.sh
[root@centos8 ~]#ln -s test.sh b.sh
[root@centos8 ~]#./a.sh
./a.sh
[root@centos8 ~]#./b.sh
./b.sh
7,退出状态码变量
进程执行后,将使用变量$? 保存状态码的相关数字,不同的值反应成功或失败,
$? 的值为 0 # 代表成功
$? 的值是1 到 255 # 代表失败
用户可以在脚本中使用以下命令自定义退出状态码
exit [n]
注意:
1)脚本中一旦遇到exit 命令,脚本会立即终止;终止退出状态取决于 exit 命令后的数字
2)如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
8,展开命令执行顺序
把命令行分成单个命令词
展开别名
展开大括号的声明 {}
展开波浪符声明 ~
命令替换 $() 和 ``
再次把命令行分成命令词
展开文件通配*,?,[abc]等等
准备I/O重定向 < ,>
运行命令
防止扩展
反斜线 ()会使随后的字符按原意解释
范例:
echo Your cost: $5.00
Your cost: $5.00
加引号来防止扩展
单引号(' ')防止所有扩展
双引号(" ")也可防止扩展,但是以下情况例外:$(美元符号)
变量扩展
`` :反引号,命令替换
:反斜线,禁止单个字符扩展
! :叹号,历史命令替换
脚本安全和 set
$- 变量
h:hashall,打开选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选
项关闭
i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的 shell。所谓的交互式shell,
在脚本中,i选项是关闭的
m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等
B:braceexpand,大括号扩展
H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的
一个历史命令,“!n”返回第 n 个历史命令
范例:
[root@centos8 ~]#echo $-
himBHs
[root@centos8 ~]#set +h
[root@centos8 ~]#echo $-
imBHs
[root@centos8 ~]#hash
-bash: hash: hashing disabled
[root@centos8 ~]#echo {1..10}
1 2 3 4 5 6 7 8 9 10
[root@centos8 ~]#echo $-
imBHs
[root@centos8 ~]#set +B
[root@centos8 ~]#echo $-
imHs
[root@centos8 ~]#echo {1..10}
{1..10}
set 命令实现脚本安全
-u :在扩展一个没有设置的变量时,显示错误信息,等同set -o nounset
-e :如果一个命令返回一个非0 退出状态值(失败)就退出,等同 set -o errexit
-o :option 显示,打开或者关闭选项
显示选项:set -o
打开选项:set -o 选项
关闭选项:set +o 选项
-x :当执行命令时,打印命令及其参数,类似 bash -x
9,格式化输出 printf
格式:printf "指定的格式" "文本1" "文本2"...
常用格式替换符
替换符: 功能
%s :字符串
%f :浮点格式
%b :相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义
%c :ASCII字符,即显示对应参数的第一个字符
%d,%i :十进制整数
%o :八进制值
%u :不带正负号的十进制值
%x :十六进制值(a-f)
%X :十六进制值(A-F)
%% :表示% 本身
说明:%s 中的数字代表此替换符中的输出字符宽度,不足补空格,默认是右对齐,%-10s表示10个字
符宽,- 表示左对齐
常用转义字符:
转义符: 功能
a :警告字符,通常为ASCII 的BEL字符
:后退
f :换页
:换行
:回车
:水平制表符
v :垂直制表符
:表示 本身
范例:
[root@centos8 ~]#printf "%s
" 1 2 3 4
1
2
3
4
[root@centos8 ~]#printf "%s %s
" 1 2 3 4
1 2
3 4
[root@centos8 ~]#printf "%f
" 1 2 3 4
1.000000
2.000000
3.000000
4.000000
#.2f 表示保留两位小数
[root@centos8 ~]#printf "%.2f
" 1 2 3 4
1.00
2.00
3.00
4.00
[root@centos8 ~]#printf "(%s)" 1 2 3 4;echo
(1)(2)(3)(4)
[root@centos8 ~]#printf " (%s) " 1 2 3 4;echo ""
(1) (2) (3) (4)
[root@centos8 ~]#printf "(%s)
" 1 2 3 4
(1)
(2)
(3)
(4)
[root@centos8 ~]#printf "%s %s
" 1 2 3 4
1 2
3 4
[root@centos8 ~]#printf "%s %s %s
" 1 2 3 4
1 2 3
4
#%-10s 表示宽度10个字符,左对齐
[root@centos8 ~]#printf "%-10s %-10s %-4s %s " 姓名 性别 年龄 体重 小明 男 20 70小红 女 18 50
姓名 性别 年龄 体重
小明 男 20 70
小红 女 18 50
#将十进制的17转换成16进制数
[root@centos8 ~]#printf "%X" 17
11
[root@centos8 ~]#
#将十六进制C转换成十进制
[root@centos8 ~]#printf "%d
" 0xC
12
[root@centos8 ~]#VAR="welcome to dd";printf " 33[1;31m%s 33[0m
" $VAR
welcome
to
dd
[root@centos8 ~]#VAR="welcome to dd";printf " 33[1;31m%s 33[0m
" "$VAR"
welcome to dd