一、Shell概述
Shell是一个命令行解释器,它接收应用程序/用户命令,然后调用操作系统内核。
Shell还是一个功能相当强大的编程语言,易编写,易调试,灵活性强。
二、Shell脚本入门
脚本格式:脚本以#!/bin/bash开头(指定解析器)
创建脚本,输出hello world
# !/bin/bash echo "hello world"
脚本执行方式
第一种:采用bash或sh+脚本的相对路径或绝对路径(不用赋予脚本执行权限)
sh hello.txt # 相对路径
第二种:采用输入脚本的绝对路径或相对路径执行脚本(需要赋予脚本执行权限)
chmod +x scripts/hello,sh scripts/hello.sh
如果执行的目录下面就有要执行的脚本,不能直接写脚本名字,需要输入 ./hello.sh
第三种:在脚本的路径前加上”.“或者”source“
source /root/scripts/hello.sh . /root/scripts/hello.sh
前两种方式都是在当前shell中打开一个子shell来执行脚本内容,当脚本内容结束,则子shell关闭,回到父shell中。
第三种可以使脚本内容在当前shell里执行,而无需打开子shell,这也是我们每次修改完/etc/profile文件以后,需要source一下的原因。
开子shell与不开子shell的区别在于环境变量的继承关系,如子shell中设置的当前变量,父shell是不可见的。
三、Shell变量
3.1系统预定义变量
常用系统变量:$HOME,$PWD,$SHELL,$USER等
查看系统变量的值:
[root@localhost scripts]# echo $HOME /root
显示当前Shell中所有变量:set
set | less ABRT_DEBUG_LOG=/dev/null BASH=/bin/bash BASHOPTS=checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath BASH_ALIASES=() BASH_ARGC=() BASH_ARGV=() BASH_CMDS=() BASH_COMPLETION_ ....
3.2自定义变量
1.基本语法
定义变量:变量名=变量值
,=前后不能有空格
撤销变量:unset 变量名
声明静态变量:readonly 变量
,不能unset
2.规则
变量名称可以由字母、数字和下划线组成,但不能以数字开头,环境变量建议大写
在bash中,变量默认类型为字符串类型,无法直接进行数值运算
变量的值如果有空格,需要使用双引号或单引号括起来
使用export导出,可以将局部变量变成全局变量
3.3特殊变量
3.3.1 $n
n为数字,$0代表该脚本名称,$1-$9代表第一到第九个参数,十以上的参数需要用大括号包含,如${10}
#! /bin/bash echo '=======================$n==========================' echo script name:$0 echo 1st parameter:$1 echo 2st parameter:$2
[root@localhost scripts]# chmod +x parameter.sh [root@localhost scripts]# ./parameter.sh abc def =======================$n========================== script name:./parameter.sh 1st parameter:abc 2st parameter:def [root@localhost scripts]#
3.3.2 $#
获取所有输入参数的个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性
echo '=======================$n==========================' echo script name:$0 echo 1st parameter:$1 echo 2st parameter:$2 echo '=======================$#==========================' echo parameter numbers: $# parameter numbers: 2
3.3.3 $@,$*
$*
:代表命令行所有的参数,$*
把所有参数看成一个整体
$@
:也代表命令行所有的参数,$@
把每个参数区别对待
#! /bin/bash echo '=======================$n==========================' echo script name:$0 echo 1st parameter:$1 echo 2st parameter:$2 echo '=======================$#==========================' echo parameter numbers: $# echo '=======================$*==========================' echo $* echo '=======================$@==========================' echo $@ =======================$*========================== abc def =======================$@========================== abc def
#!/bin/bash echo '=====================$*===========================' for para in "$*" do echo $para done echo '=====================$@===========================' for para in "$@" do echo $para done [root@localhost scripts]# ./parameter_for_test.sh a b c d e =====================$*=========================== a b c d e =====================$@=========================== a b c d e
3.3.4 $?
最后一次执行命令的返回状态,如果这个变量的值为0,证明上一个命令的正确执行;如果这个变量的值为非0(具体哪个数由命令自己决定),证明上个命令执行不正确
[root@localhost scripts]# echo $? 0 [root@localhost scripts]# parameter.sh bash: parameter.sh: 未找到命令... [root@localhost scripts]# echo $? 127
四、Shell运算符
$((运算符))
或$[运算符]
双小括号里面可以用数学上的运算式
[root@localhost ~]# echo $[5*2] 10 [root@localhost ~]# a=$[6+8] [root@localhost ~]# echo $a 14 [root@localhost ~]# a=$(expr 5 \* 2) [root@localhost ~]# echo $a 10 [root@localhost ~]# a=`expr 5 \* 4` [root@localhost ~]# echo $a 20
五、流程控制
5.1条件判断
5.1.1基本语法:
(1)test condition
(2) [ condition ] (condition前后要有空格)
[root@localhost scripts]# a=hello [root@localhost scripts]# test $a = Hello [root@localhost scripts]# echo $? 1 [root@localhost scripts]# [ $a = Hello ] [root@localhost scripts]# echo $? 1
注:判断书写时等号两边必须空格,否则测试一直结果为0,除非是[ ]情况下
[root@localhost scripts]# [ $a=Hello ] [root@localhost scripts]# echo $? 0 [root@localhost scripts]# [ ] [root@localhost scripts]# echo $? 1
5.1.2 两个整数比较
-eq 等于
-ne 不等于
-lt 小于
-le 小于等于
-gt 大于
-ge大于等于
5.1.3 文件权限判断
-r 是否有读的权限
-w 是否有写的权限
-x 是否有执行的权限
[root@localhost scripts]# [ -r hello.sh ] [root@localhost scripts]# echo $? 0 [root@localhost scripts]# [ -w hello.sh ] [root@localhost scripts]# echo $? 0
5.1.4 文件类型判断
-e 文件存在
-f 文件存在并是一个常规文件
-d 文件存在并且是一个目录
5.1.5 多条件判断
&&表示前一条命令执行成功时,才执行后一条命令;||表示前一条命令执行失败后,才执行下一条命令
[root@localhost scripts]# [ $a -lt 20 ] && echo "$a<20" || echo "$a>=20" 15<20
5.2 if判断
单分支
if [ 条件判断式 ]; then 程序 fi
多分支
if [ 条件判断式 ] then 程序 elif [ 条件判断式 ] then 程序 else 程序 fi
演示:
[root@localhost scripts]# a=25 [root@localhost scripts]# if [ $a -gt 18 ]; then echo OK;fi OK [root@localhost scripts]# a=15 [root@localhost scripts]# if [ $a -gt 18 ]; then echo OK;fi
5.3 case语句
case $变量名 in "值1") 程序1 ;; "值2") 程序2 ;; ...... *) 变量都不是以上的值,则执行此程序 ;; esac
注:
case行尾必须为单词”in“,每一个模式匹配必须以右括号”)“结束;
双分号”;;“表示命令序列结束,相当于java中break;
最后的”*)“表示默认模式,相当于java中default;
5.4 for循环
语法一:
for ((初始值;循环控制条件;变量变化)) do 程序 done
语法二:
for 变量 in 值1 值2 值3.. do 程序 done
[root@localhost scripts]# for i in {1..100}; do sum=$[$sum+$i]; done; echo $sum 5050
5.5 while循环
while [ 条件判断式 ] do 程序 done
六、read读取控制台输入
read (-p -t)(参数)
-p:指定读取值时的提示符
-t:指定读取值时等待的时间,如果-t不加表示一直等待
参数:指定读取值的变量名
#!/bin/bash read -t 10 -p "please tell me your name:" name echo "welcome,$name" [root@localhost scripts]# ./read_test.sh please tell me your name:welcome, [root@localhost scripts]# ./read_test.sh please tell me your name:高鑫 welcome,高鑫
七、函数
7.1系统函数
7.1.1 basename
basename [string/pathname] [suffix]
basename命令会删掉所有的前缀包括最后一个('/')字符,然后将字符写出来
basename可以理解为取路径里的文件名称
suffix为后缀,如果suffix被指定了,basename会将pathname或string中的suffix去掉
[root@localhost scripts]# basename /home/scripts/hello.sh hello.sh [root@localhost scripts]# basename /home/scripts/hello.sh sh hello.
7.1.2 dirname
dirname 文件绝对路径
从给定的包含绝对路径的文件名中去除文件名(非目录部分),然后返回剩下的路径(目录的部分)
[root@localhost scripts]# dirname /root/scripts/parameter.sh /root/scripts [root@localhost scripts]# dirname ../scripts/parameter.sh ../scripts [root@localhost scripts]# dirname ../sdjajts/parameter.sh ../sdjajts
7.2 自定义函数
[ function ]funname[()] { Action; [return int;] } # []代表可省略
必须在调用函数之前,先声明函数,shell脚本是逐行运行,不会像其他语言一样先编译
函数返回值,只能通过$?系统变量获得,可以显示:return返回,如果不加,将以最后一条命令的运行结果,作为返回值。return后跟数值n(0-255)
#!/bin/bash function add(){ s=$[ $1 + $2 ] echo "result:" $s } read -p "please input first integer:" a read -p "please input second integer:" b add $a $b [root@localhost scripts]# vim fun_test.sh [root@localhost scripts]# chmod +x fun_test.sh [root@localhost scripts]# ./fun_test.sh please input first intege:r443 please input second integer:333 result: 776
八、正则表达式
正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。在很多文本编辑器里,正则表达式通常用来检索、替换那些符合某个模式的文本。在Linux中,grep,sed,awk等文本处理工具都支持通过正则表达式进行模式匹配。
常规匹配:一串不包含特殊字符的正则表达式匹配它自己,例如:
grep gaoxin
,就会匹配所有包含gaoxin的行特殊字符:
^匹配一行的开头,例如:
[root@localhost ~]# cat /etc/passwd | grep ^a adm:x:3:4:adm:/var/adm:/sbin/nologin abrt:x:173:173::/etc/abrt:/sbin/nologin avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin [root@localhost ~]# cat /etc/passwd | grep ^ad # 匹配所有以a开头的行
$匹配一行的结尾,例如:
[root@localhost ~]# cat /etc/passwd | grep bash$ root:x:0:0:root:/root:/bin/bash chen:x:1000:1000:cchen:/home/chen:/bin/bash
.匹配任意的字符,例如:
[root@localhost ~]# cat /etc/passwd | grep r.t operator:x:11:0:operator:/root:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin # 会匹配包含"r_t"的所有行
*不单独使用,它和上一个字符连用,表示匹配上一个字符0次或多次
[root@localhost ~]# cat /etc/passwd | grep ro*t root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin abrt:x:173:173::/etc/abrt:/sbin/nologin rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin # 会匹配包含rt、rot、root...的字符
[]表示某个范围内的一个字符,例如:
[6,8]---------匹配6或者8
[0-9]------------匹配一个0-9的数字
[0-9]*--------------匹配任意长度的数字字符串
[a-z]---------------匹配一个a-z的之间的字符串
[a-z]*----------------匹配任意长度的字母字符串
[a-c,e-f]--------------匹配a-c或者e-f之间的任意字符
\表示转义,并不会单独使用。由于所有单独特殊字符都有其特定匹配模式,当我们想匹配某一特殊字符本身时,就会碰到困难,此时需要将转义字符和特殊字符连用,来表示特殊字符本身。
九、文本处理工具
9.1 cut
cut的工作是在文件中负责剪切数据用的。cut命令从文件的每一行剪切字节、字符和字段并将这些字节、字段、字符输出。
cut [-f -d -c] filename
-f:列号,提取第几列
-d:分隔符,按照指定分隔分割列,默认是制表符"\t"
-c:按字符进行分割,后加n表示第几列-c 1
9.2 awk
把文件逐行的读入,以空格为默认分隔符将每行切片,切片的部分在进行分析处理
awk [ -F -v] '/pattern1/{action1} /pattern{action2}...' filename
pattern: 表示awk在数据中查找的内容,就是匹配模式
action:在找到匹配内容时执行的一系列命令
-F:指定输入文件分隔符
-v:赋值一个用户定义变量
# 搜索passwd文件以root关键字开头的所有行 [root@localhost scripts]# cat /etc/passwd |grep ^root | cut -d ":" -f 7 /bin/bash [root@localhost scripts]# cat /etc/passwd | awk -F ":" '/^root/ {print $7}' /bin/bash
awk内置变量:
FILENAME:文件名
NR:已读的记录号(行号)
NF:浏览记录的域的个数(切割后列的个数)
归档文件
#!/bin/bash # 首先判段输入参数个数是否为1 if [ $# -ne 1 ] then echo "参数个数错误!应该输入一个参数,作为归档目录名" exit fi # 从参数中获取目录名称 if [ -d $1 ] then echo else echo echo "目录不存在!" echo exit fi DIR_NAME=$(basename $1) DIR_PATH=$(cd $(dirname $1); pwd) # 获取当前日期 DATE=$(date+ %y%m%d) # 定义生成的归档文件名称 FILE=archive_${DIR_NMAE}_$DATE.tar.gz DEST=/root/archive/$FILE # 开始归档 echo "start" echo tar -czf $DEST $DIR_PATH/$DIR_NAME if [ $?o "result:" $s -eq 0 ] then echo echo "归档成功!" echo "归档文件为:$DEST" echo else echo "归档出现问题!" echo fi exit
[root@localhost scripts]# mkdir /root/archive [root@localhost scripts]# ./daily_archive.sh ../scripts [root@localhost scripts]# crontab -e 0 2 * * * /root/scripts/daily_archive.sh /root/scripts
发送消息
#!/bin/bash # 查看用户是否登录 login_user=$(who | grep -i -m 1 $1 | awk '{print $1}') if [ -z $login_user ] then echo "$1 不在线!" echo "脚本退出.." exit fi # 查看用户是否开启消息功能 is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}') if [ $is_allowed != "+" ] then echo "$1 没有开启消息功能" echo "脚本退出..." exit fi # 确认是否有消息发送 if [ -z $2 ] then echo "没有消息发送" echo "脚本退出" exit fi # 从参数中获取要发送的信息 whole_msg=$(echo $* | cut -d " " -f 2-) # 获取用户登录的终端 user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}') # 写入要发送的消息 echo $whole_msg | write $login_user $user_terminal if [ $? != 0 ] then echo "发送失败!" else echo "发送成功!" fi exit
[root@localhost scripts]# ./send_msg.sh root hi 发送成功! Message from root@localhost.localdomain on pts/3 at 06:04 ... hi EOF