欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SHELL脚本编程基础

程序员文章站 2022-07-10 09:29:11
...
  • shell脚本编程是过程式、解释执行的编程语言。
  • shell编程语言的基本结构:
    各种系统命令的组合
    数据存储:变量、数组
    表达式:a+b
    语句:if
    过程式:以指令为中心,数据服务于指令
    解释执行:解释:高级语言-->执行-->解释器-->机器代码 即边解释代码,边执行。而编译型编程语言是通过高级语言通过编译器将代码全部编译完成,再执行代码。
  • shell脚本: 包含一些命令或声明,并符合一定格式的文本文件
  • 格式要求:首行shebang机制
    #!/bin/bash #shell脚本首行格式
    #!/usr/bin/python
    #!/usr/bin/perl
  • shell脚本的用途有:
    自动化常用命令
    执行系统管理和故障排除
    创建简单的应用程序
    处理文本或文件
  • 脚本代码开头约定
    1、第一行一般为调用使用的语言
    2、程序名,避免更改文件名为无法找到正确的文件
    3、版本号
    4、更改后的时间
    5、作者相关信息
    6、该程序的作用,及注意事项
    7、最后是各版本的更新简要说明
  • 脚本调试
    1、检测脚本的语法错误
     bash -n /path/to/some_script
    2/调试执行
     bash -x /path/to/some_script
    *shell脚本中的变量
    shell脚本中的变量属于弱类型的,无需事先制定变量类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用
  • bash中变量的种类
    局部变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
    环境变量:生效范围为当前shell进程及其子进程
    本地变量:生效范围为当前shell进程中某代码片断,通常指函数
    位置变量:$1, $2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数
    特殊变量:$?, $0, $*, [email protected], $#,$$
  • 局部变量
    1、变量赋值: name= 'value'
    2、可以引用的value
     2.1 可以是直接字串: name= "root"
     2.2 变量引用: name="$USER"
     2.3 命令引用: name=`COMMANDD`
            name=$(COMMAND)
    3、变量引用:${name}或者$name
     3.1 " " 弱引用,其中的变量引用会被替换为变量值
     3.2 ' ' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串
    示例:
[[email protected] script36]#name=jiang
[[email protected] script36]#echo "$name" 弱引用
jiang
[[email protected] script36]#echo '$name'  强引用
$name

4、显示已定义的所有变量: set
5、删除变量: unset name

[[email protected] script36]#name=jiang
[[email protected] script36]#echo $name
jiang
[[email protected] script36]#unset name
[[email protected] script36]#echo $name

  • 环境变量
    1、变量声明、赋值:
     export name=VALUE
     declare -x name=VALUE
    2、变量引用:
     $name, ${name}
    3、显示所有环境变量:
     env
     printenv
     export
     declare -x
    4、删除变量:
     unset name

  • 只读和位置变量
    1、只读变量:只能声明,但不能修改和删除
    1.1 声明只读变量:
    readonly name
    declare -r name
    1.2查看只读变量:
    readonly -p
    2、位置变量:在脚本代码中调用通过命令行传递给脚本的参数
    2.1 $1, $2, ... 对应第1、第2等参数,shift [n]换位置
    2.2 $0 命令本身
    2.3 $* 传递给脚本的所有参数,全部参数合为一个字符串
    2.4 [email protected] 传递给脚本的所有参数,每个参数为独立字符串
    2.5 $# 传递给脚本的参数的个数
    注意:[email protected] $* 只在被双引号包起来的时候才会有差异
    2.6 set -- 清空所有位置变量
    示例

[[email protected] script36]#vim test.sh
#!/bin/bash
echo '$1' is : $1
echo '$2' is : $2
echo '$3' is : $3
echo '$4' is : $4
echo '$0' is : $0
echo '$#' is : $#
echo '$*' is : $*
echo '[email protected]' is : [email protected]
:wq

[[email protected] script36]#vim test.sh 
[[email protected] script36]#test.sh 1 2 3 4 5 6
$1 is : 1
$2 is : 2
$3 is : 3
$4 is : 4
$0 is : /data/script36/test.sh
$# is : 6
$* is : 1 2 3 4 5 6  注:$*
[email protected] is : 1 2 3 4 5 6


  • 退出状态
    1、进程使用退出状态来报告成功或失败
    0 代表成功,1-255代表失败
    $? 变量保存最近的命令退出状态
[[email protected] script36]#name=jiang
[[email protected] script36]#echo $name
jiang
[[email protected] script36]#echo $?
0

  • 退出状态码
    1、bash自定义退出状态码
    exit [n]:自定义退出状态码
    注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
    注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

  • 算术运算
    1、bash中的算术运算:help let
    +,-, *, /, %取模(取余),**(乘方),乘法符号有些场景中需要转义
    实现算术运算:
    (1) let var=算术表达式
    (2) var=((算术表达式))
    (4) var=$(expr arg1 arg2 arg3 ...)
    (5) declare –i var = 数值
    (6) echo ‘算术表达式’ | bc
    示例:

[[email protected] script36]#let var=1+2
[[email protected] script36]#echo $var
3
[[email protected] script36]#var=$[2+3]
[[email protected] script36]#echo $var
5
[[email protected] script36]#var=$((10-2))
[[email protected] script36]#echo $var
8

2、bash有内建的随机数生成器变量:$RANDOM(0-32767)
示例:生成 0 - 99之间随机数

[[email protected] script36]#echo $[$RANDOM%100]
7
[[email protected] script36]#echo $[$RANDOM%100]
14
[[email protected] script36]#echo $[$RANDOM%100]
6

  • 赋值
    1、增强型赋值: 增强型赋值,要配合let命令使用
     +=,-=,*=,/=,%=
    2、自增,自减:
     let var+=1
     let var++
     let var-=1
     let var--
    示例
[[email protected] script36]#i=100
[[email protected] script36]#let j=i++; echo j=$j,i=$i  注:先赋值,再自增
j=100,i=101
[[email protected] script36]#i=100
[[email protected] script36]#let j=++i; echo j=$j,i=$i 注:先自增,再赋值
j=101,i=101
  • 逻辑运算
    1、true,false
    true:1
    false: 0
    2、与
    1 与 1 = 1
    1 与 0 = 0
    0 与 1 = 0
    0 与 0 = 0
    3、或
    1 或 1 = 1
    1 或 0 = 1
    0 或 1 = 1
    0 或 0 = 0
    4、非:!
    ! 1 = 0  ! true
    ! 0 = 1  ! false

    5、短路运算
    5.1 短路与
    第一个为0,结果必定为0 (短路与,遇到0,后面表达式不执行)
    第一个为1,第二个必须要参与运算(短路与,遇到1,后面表达式执行)
    5.2 短路或
    第一个为1,结果必定为1(短路或,遇到0,后面表达式执行)
    第一个为0,第二个必须要参与运算(短路或,遇到1,后面表达式不执行)
    6、异或:^
    异或的两个值,相同为假,不同为真

10011001  第一个数
11001100  第二个数
01010101  第三个数通过第一个数和第二个数异或运算得出的结果
这三个数任意2个通过异或运算可以得出第三个数
  • 条件测试
    1、判断某需求是否满足,需要由测试机制来实现
    专用的测试表达式需要由测试命令辅助完成测试过程
    2、评估布尔声明,以便用在条件性执行中
    若真,则返回0
    若假,则返回1
    3、测试命令:
    test EXPRESSION
    [ EXPRESSION ]
    [[ EXPRESSION ]]
    注意:EXPRESSION前后必须有空白字符

  • bash的数值测试
    1、-v VAR
    变量VAR是否设置
    注:-v 判断变量是否被定义,不判断变量是否有值

[[email protected] data]#name=""
[[email protected] data]#[ -v name ]; echo $?
0
[[email protected] data]#unset name
[[email protected] data]#[ -v name ]; echo $?
1

2、数值测试:
-gt 是否大于
-ge 是否大于等于
-eq 是否等于
-ne 是否不等于
-lt 是否小于
-le 是否小于等于

[[email protected] script36]#[ $num1 -gt $num2 ] && echo true || echo false
false
[[email protected] script36]#[ $num1 -le $num2 ] && echo true || echo false
true

  • bash的字符串测试:
    注:比较符号前后要有一个空格,没有空格,被认为是赋值
    1.1 = 是否等于
[[email protected] ~]#string1=haha
[[email protected] ~]#string2=yaya
[[email protected] ~]#[ ${string1} = ${string2} ] && echo true || echo false
[[email protected] ~]#string2=haha
[[email protected] ~]#[ ${string1} = ${string2} ] && echo true || echo false
true

1.2 > ascii码是否大于ascii码
1.3 < 是否小于
1.4 != 是否不等于
1.5 =~ 左侧字符串是否能够被右侧的PATTERN所匹配
注意: 此表达式一般用于[[ ]]中;扩展的正则表达式

[[email protected] script36]#[[ "sdfas9078" =~ .*[0-9]+$ ]] && echo true || echo false
true
注:=~符号的左边字符串需要用引号引起来,符号右边正则表达式不需要引号,**此表达式要用于[[ ]]中**

1.6 -z "STRING“ 字符串是否为空,空为真,不空为假

[[email protected] script36]#string=""
[[email protected] script36]#[ -z "$string" ] && echo true || echo false
true   注:空为真
[[email protected] script36]#string=haha
[[email protected] script36]#[ -z "$string" ] && echo true || echo false
false   注:不空为假

1.7 -n "STRING“ 字符串是否不空,不空为真,空为假
注意:用于字符串比较时的用到的操作数都应该使用引号

  • Bash的文件测试
    1、存在性测试
    -a FILE:同-e
    -e FILE: 文件存在性测试,存在为真,否则为假
[[email protected] script36]#[ -a /etc/fstab ] && echo true || echo false
true
[[email protected] script36]#[ -a /etc/fstabsdff ] && echo true || echo false
false

2、存在性及类别测试
-b FILE:是否存在且为块设备文件
-c FILE:是否存在且为字符设备文件
-d FILE:是否存在且为目录文件
-f FILE:是否存在且为普通文件
-h FILE 或 -L FILE:存在且为符号链接文件
-p FILE:是否存在且为命名管道文件
-S FILE:是否存在且为套接字文件

[[email protected] ~]#[ -d /etc/ ] && echo true || echo false
true
[[email protected] ~]#[ -d /etcsd/ ] && echo true || echo false
false
  • Bash的文件权限测试
    1、文件权限测试:
    -r FILE:是否存在且可读
    -w FILE: 是否存在且可写
    -x FILE: 是否存在且可执行
[[email protected] ~]#cd /data/
[[email protected] data]#touch file1
[[email protected] data]#ll file1
-rw-r--r-- 1 root root 0 Mar 20 18:05 file1
[[email protected] data]#su tong
[[email protected] data]$[ -r /data/file1 ] && echo true || echo false
true
[[email protected] data]#chmod a-r file1
[[email protected] data]#ll file1
--w------- 1 root root 0 Mar 20 18:05 file1 
[[email protected] data]#[ -r /data/file1 ] && echo true || echo false
true   注:判断的是实际权限,不是表面上的,对于root,root对file1没有读权限,但是对于他来说是可读的

2、文件特殊权限测试:
-u FILE:是否存在且拥有suid权限
-g FILE:是否存在且拥有sgid权限
-k FILE:是否存在且拥有sticky权限

  • Bash的文件属性测试
    1、文件大小测试:
    -s FILE: 是否存在且非空
    2、文件是否打开:
    -t fd: fd 文件描述符是否在某终端已经打开
    -N FILE:文件自从上一次被读取之后是否被修改过
    -O FILE:当前有效用户是否为文件属主
    -G FILE:当前有效用户是否为文件属组
[[email protected] tong]#ll f1
-rw-rw-r-- 1 tong tong 0 Mar 20 14:01 f1
[[email protected] tong]#[ -O f1 ] && echo true || echo false
false

3、双目测试:
FILE1 -ef FILE2: FILE1是否是FILE2的硬链接
FILE1 -nt FILE2: FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2: FILE1是否旧于FILE2

[[email protected] data]#ln f11 link_f11
[[email protected] data]#[ f11 -ef link_f11 ] && echo true || echo false
true
  • Bash的组合测试条件
    1、第一种方式:
    EXPRESSION1 -a EXPRESSION2 并且
    EXPRESSION1 -o EXPRESSION2 或者
    ! EXPRESSION
[[email protected] data]#ll file1
--w------- 1 root root 0 Mar 20 18:05 file1
[[email protected] data]#[ -f file1 -a -w file1 ] && echo true || false
true
[[email protected] data]#[ -f file1 -a -u file1 ] && echo true || echo false
false
[[email protected] data]#[ ! \( -f file1 -a -u file1 \) ] && echo true || echo false
true

必须使用测试命令进行,[[ ]] 不支持
2、第二种方式:
COMMAND1 && COMMAND2 并且,短路与,代表条件性的AND THEN
COMMAND1 || COMMAND2 或者,短路或,代表条件性的OR ELSE
! COMMAND 非
如:[ -f “FILE”=~ .*.sh$ ]]

[[email protected] data]#[ $A -eq $B ] && [ -f file1 ] && echo true || false
true
  • 使用read命令来接受输入
    使用read来把输入值分配给一个或多个shell变量
    -p 指定要显示的提示
    -s 静默输入,一般用于密码
    -n N 指定输入的字符长度N
    -d ‘字符’ 输入结束符
    -t N TIMEOUT为N秒
    read 从标准输入中读取值,给每个单词分配一个变量
    所有剩余单词都被分配给最后一个变量
    read -p “Enter a filename: “ FILE
[[email protected] data]#read -p "please input you name: " name
please input you name: limeimei
[[email protected] data]#echo $name
limeimei

[[email protected] data]#read -dL -p "input you name: " name
input you name: wangL[[email protected] data]# 注:设定L为输入结束符

  • if条件选择if语句
    选择执行:
    注意:if语句可嵌套
    1、单分支
    if 判断条件;then
     条件为真的分支代码
    fi
    2、双分支
    if 判断条件; then
     条件为真的分支代码
    else
     条件为假的分支代码
    fi

3、多分支
if 判断条件1; then
 条件1为真的分支代码
elif 判断条件2; then
 条件2为真的分支代码
elif 判断条件3; then
 条件3为真的分支代码
else
以上条件都为假的分支代码
fi
示例:

编写脚本 createuser.sh,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息
#!/bin/bash 
if [ ! $# -eq 1 ]; then
    echo "please Enter a user name"
    exit 2
fi

if grep "^$1\>" /etc/passwd &> /dev/null; then
    echo "user is exist"
else
    useradd $1
    echo "add user $1 finished"
fi

4、条件判断:case语句
case 变量引用 in
PAT1)
 分支1
;;
PAT2)
 分支2
;;
...
*)
 默认分支
;;
esac

注: case :PAT)支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或b

编写脚本/root/bin/filetype.sh,判断⽤户输⼊⽂件路径,显⽰其⽂件类型(普通,⽬录,链接,其它⽂件
类型)
read -p "please inpu file path: " path
a=`ls -l $path | grep -o "^."`
case $a in
-) 
    echo "common file"
    ;;
d)
    echo "directory file"
    ;;
l)
    echo "linked file"
    ;;
*)
    echo "other file"
    ;;
esac

  • bash的配置文件
    1、按生效范围划分,存在两类:
    2、全局配置:
    /etc/profile
    /etc/profile.d/*.sh
    /etc/bashrc
    3、个人配置:
    ~/.bash_profile
    ~/.bashrc

  • shell登录的两种方式
    1、交互式登录
    (1)直接通过终端输入账号密码登录
    (2)使用“su - UserName” 切换的用户
    执行顺序:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
    2、非交互式登录:
    (1)su UserName
    (2)图形界面下打开的终端
    (3)执行脚本
    (4)任何其它的bash实例
    执行顺序: /etc/profile.d/*.sh --> /etc/bashrc -->~/.bashrc

  • Profile类
    1、按功能划分,存在两类:
     profile类和bashrc类
    2、profile类:为交互式登录的shell提供配置
    全局:/etc/profile, /etc/profile.d/*.sh
    个人:~/.bash_profile
    功用:
    (1) 用于定义环境变量
    (2) 运行命令或脚本
    3、bashrc类:为非交互式和交互式登录的shell提供配置
    全局:/etc/bashrc
    个人:~/.bashrc
    功用:
    (1) 定义命令别名和函数
    (2) 定义本地变量

  • 编辑配置文件生效
    修改profile和bashrc文件后需生效
    两种方法:
    1 重新启动shell进程
    2 . 或source
    例:
    . ~/.bashrc

  • Bash 退出任务
    1、保存在~/.bash_logout文件中(用户)
    2、在退出登录shell时运行
    3、用于
    •创建自动备份
    •清除临时文件

  • set 命令
    set 命令
    1、$- 变量
    h:hashall,打开这个选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选项关闭,set -h将选项关闭

[[email protected] script36]#echo $-
himBH
[[email protected] script36]#set +h
[[email protected] script36]#echo $-
imBH
[[email protected] script36]#set -h
[[email protected] script36]#echo $-
himBH

i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的 shell。所谓的交互式shell,在脚本中,i选项是关闭的。
m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。
B:braceexpand,大括号扩展
H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的一个历史命令,“!n”返回第 n 个历史命令
2、set 命令
-u 在扩展一个没有设置的变量时,显示错误信息,等同set –o nounset
注:如果一个变量没有赋值,后续相关命令不执行,最好在脚本开头写入set -u启用此功能
-e 如果一个命令返回一个非0退出状态值(失败)就退出,等同set –o errexit
注:如果脚本中的命令或语法出现错误,就退出,后续命令不执行,最好在脚本开头写入set -u启用此功能
`注:set -ue 可以合写在一起

  • 扩展
    一串的命令执行()和{}
    ()和{}都是对一串的命令进行执行,但有所区别:
    1、相同点:
    ()和{}都是把一串的命令放在括号里面,并且命令之间用;号隔开
    2、不同点
    ()只是对一串命令重新开一个子shell进行执行,{}对一串命令在当前shell执行
    ()最后一个命令可以不用分号,{}最后一个命令要用分号
    ()里的第一个命令和左边括号不必有空格,{}的第一个命令和左括号之间必须要有一个空格
    ()和{}中括号里面的某个命令的重定向只影响该命令,但括号外的重定向则影响到括号里的所有命令
[[email protected] ~]#echo $var
test
[[email protected] ~]#(var=notest;echo $var)
notest
[[email protected] ~]#echo $var
test
[[email protected] ~]#{ var=notest; echo $var; }
notest
[[email protected] ~]#echo $var
notest