shell脚本学习
协程,协程可以同时做两件事。它在后台生成一个子shell,并在这个子shell中执行命令。要进行协程处理,得使用coproc命令,还要有在子shell中执行的命令.当外部命令执行时,会创建一个子进程。这种操作被称为衍生(forking)。
break:中断整个循环
continue:将控制转移到下一段代码,但是会继续执行循环
exit:退出整个循环
return:用于在函数中将数据返回,返回一个结果或代码给调用脚本
shell脚本中由于安全原因不支持执行suid,sgid,sticky bit
命令行参数$1,$2,$3...$9是位置参数,$0指向实际的命令、程序、shell脚本或函数在一个函数中出现的$0,$2等由函数本身使用
$,不加双引号指定了所有的命令行参数,加上双引号,$将整个参数列表作为一个参数来获取,获取整个参数列表,并将其分割成不同的参数
当shell脚本运行时,它会先查找系统环境变量ENV,该变量指定了环境文件(加载顺序通常是/etc/profile、~/bash_profile、~/bash_rc、/etc/bashrc)
常见的全局变量配置文件: /etc/profile /etc/bashrc /etc/profile.d/ 诺要在登陆后初始化或显示加载内容,则把脚本文件放在/etc/profile.d/下即可(无需加执行权限)
```设置登录提示的两种方式:
1.在/etc/motd里增加提示的字符串
2.在/etc/profile.d/下面增加脚本
> \n:换行 \r:回车 \t:制表符 > chmod 4755 设置某个程序总是做为所有者执行(suid) > chmod 2755 设置某个程序总是做为文件所有者所属的组成员执行(sgid) > chmod 6755 设置某个程序总是做为文件所有者和文件所有者的组成员执行 > echo -n :不换行输出 > 诺某个程序在正常退出前被终止,通常情况下可以捕获到一个退出信号,该退出信号成为一个陷阱(trap),退出信号: 数字 | 信号 | 含义 | :--:|:---------:|:---------:| 0 | - | 正常终止,脚本结束| 1 | SIGHUP | 挂起,线路断开 2 | SIGINT | 终端中断,通常是Ctrl+C 3 | SIGQUIT | 退出键,子进程在终止前死掉 9 | SIGKILL | kill -9 命令,不能捕获这种推出状态 15 | SIGTERM | kill命令的默认操作 19 | SIGSTOP | 停止通常为Ctrl+Z > **trap命令可以捕获信号** > ps -ef :除内核进程外所有进程的完整列表 > ps -aux :根据CPU的使用时间%CPU排序后显示的进程列表 > /etc/motd/文件在每次用户登录时显示 > tr :大小写形式转换 tr ‘[a-z]‘ ‘[A-Z]‘ 或者 tr ‘[A-Z]‘ ‘[a-z]‘ 注意:需要使用单引号将[]扩起来 > typeset -u 变量名 :用于大写,设置了变量的属性之后,每次为变量赋值文本字符串时,都自动转换为大写字符 > typeset -l 变量名:用于小写,设置了变量的属性之后,每次为变量赋值文本字符串时,都自动转换为小写字符 > cron 表是一个系统文件,系统每分钟读取一次,而且将执行安排在该时间段执行的所有条目. > crontab -e 创建一个cron表,crontab -l 列出当前用户的cron表内容 > at 根据时间来执行命令,比如十分钟后执行 at now + 10 minutes > 2>&1 /dev/null :将文件描述符2指定的标准错误(stderr)重定向到文件描述符1指定的标准输出(stdout) > if [ -s file ]:文件大小非零时为真 ***
#!/bin/bash
out_file="/root/out_file"
in_file="/etc/passwd"
${out_file}
exec 4<&1 #4文件描述符从标准输出读入数据
exec 1>${out_file} #标准输出重定向到${out_file}
while read LINE #read LINE 读取文件中的每一行
do
echo "$LINE"
:
done < ${in_file} #while循环从${in_file}读入数据
exec 1<&4 #将重定向到${out_file}的标准输出重新指向STDOUT
exec 4>&- #关闭文件描述符4linux系统将每个对象当作文件处理。这包括输入和输出进程。linux用文件描述符(file descriptor)来标示每个文件对象。文件描述符是一个非负整数,可以唯一标示会话中打开的文件。每个进程一次最多可以有9个文件描述符。
文件描述符 | 缩写 | 描述 |
---|---|---|
0 | STDIN | 标准输入 |
1 | STDOUT | 标准输出 |
2 | STDERR | 标准错误 |
#!/bin/bash out_file="/root/out_file" in_file="/root/in_file" exec 3>&1 exec 1>${out_file} while read line do echo "$line" done < ${in_file} exec 1>&3 exec 3>&-
这个例子首先,脚本将文件描述符3重定向到文件描述符1的当前位置,也就是STDOUT。这意味着任何发送给文件描述符3的输出都将出现在显示器上。第二个exec命令将STDOUT重定向到文件,shell现在会将发送给STDOUT的输出直接重定向到输出文件中。但是,文件描述符3仍然指向STDOUT原来的位置,也就是显示器。如果此时将输出数据发送给文件描述符3,它仍然会出现在显示器上,尽管STDOUT已经被重定向了。
在向STDOUT现在指向一个文件发送一些输出之后,脚本将STDOUT重定向到文件描述符3的当前位置(现在仍然是显示器)。这意味着现在STDOUT又指向了它原来的位置:显示器。
逐行处理文件2
#!/bin/bash out_file="/root/out_file" in_file="/etc/profile" >${out_file} exec 4>&1 exec 1>${out_file} for line in `cat ${in_file}` do echo "${line}" done exec 1>&4 exec 4>&-
逐行处理文件3
#!/bin/bash while read line do echo "$line" done < <(ps)
done后面的<与<之间必须有空格,执行脚本使用bash执行sh执行脚本会报错
打印色彩进度条
#!/bin/bash num=0 str=‘#‘ max=100 pro=(‘|‘ ‘/‘ ‘-‘ ‘\‘) while [ $num -le $max ] do ((color=30+num%8)) echo -en "\e[1;"$color"m" let index=num%4 printf "[%-100s %d%% %c]\r" "$str" "$num" "${pro[$index]}" let num++ sleep 0.1 str+=‘#‘ done printf "\n" echo -e "\e[1;30;m"expect实现免交互
#!/bin/bash function expect_mianmi() { /usr/bin/expect <<EOF spawn ssh ${i} "需要执行的命令" expect { "yes/no" {exp_send "yes\r";exp_continue} "*password" {exp_send "需要登陆的主机密码\r"} } expect eof EOF } for i in `cat ip_list` #ip_list是需要登录的主机列表 do expect_mianmi done捕获命令输出
#!/bin/bash out_file="/tmp/outfile.out" cat /dev/null >> ${out_file} until [ -s ${out_file}] then echo "test" >> ${out_file} fi more ${out_file}生成随机数
#!/bin/bash ran_dom=$$ #脚本执行后产生的PID upper_limit=$1 #脚本传递的第一个参数 random_number=$((${ran_dom} % ${upper_limit} + 1)) #用PID除以传递的参数求模+1 echo "$$" echo "${random_number}"这个脚本只能生成4位数的随机数
高亮显示文本
cat /etc/hosts | sed s#‘km‘#$(tput smso)‘km‘$(tput rmso)#g
删除文件中的重复的行
如果有一个有重复行的文件my_list,想要将my_list删除重复行保存到my_list_no_repeats,可以使用如下命令: uniq mylist my_list_no_repeats 只查看一次重复的行可以使用: cat my_list | uniq
删除文本中的空行
cat my_file | sed ‘/^$/d‘ sed ‘/^$/d‘ my_file
测试空变量
#!/bin/bash VAL= #设置一个空变量 if [[ -z "$VAL" && "$VAL" = ‘‘ ]] then echo "VAL is null" fi VAL=25 #设置一个有值的变量 if [[ ! -z "$VAL" && "$VAL" != ‘‘ ]] then echo "VAL is not NULL" fi #双中括号进行测试,unix脚本中说变量必须使用双引号扩起来,实际双中括号更多的用来进行字符串比较,使用"[[]]"可以在判断中使用&&、||而不是-a,-o #"["和test是bash的内嵌命令,而"[[]]"是关键字
简单发送邮件告警
#!/bin/bash mail_file="/tmp/mailfile.out" #定义邮件的内容 mail_list="" #收件人地址 > ${mail_file} #清空邮件内容 function check_system { if [ -s ${mail_file} ] #如果邮件内容不为空则执行 then mail -s "Filesystem Full" ${mail_list} > ${mail_file} #发送邮件给收件人,filesystem full是邮件主题 fi file_sys="/" #测试,定义邮件的内容 file_size="98%" this_host=`uname -n` echo "${this_host} ${file_sys} is use ${file_size}" | tee -a ${mail_file} #tee命令将echo同时输出到显示器和指定的文件中 } check_system
“.”作为进度条显示进度
#!/bin/bash while true do echo -e ".\c" #在同一行打印"."不换行 sleep 1 #很重要,不指定时间循环将输出很多"." done & #让循环在后台运行,会有运行pid bg_pid=$! #"#!表示上一个后台进程的pid"用变量bg_pid来保存循环的运行pid source /root/shell/while_read_line.sh kill "$!" #在实际脚本执行完毕后杀死进度条循环
旋转线进度条
#!/bin/bash rcount_num=0 #设置一个递增的变量 while : do ((rcount_num = rcount_num + 1)) #让变量的值递增 case ${rcount_num} in "1") echo -e "-\b\c" #"\b"退回光标 sleep 1 #"\c"继续在统一行显示 ;; "2") echo -e ‘\‘"\b\c" #"\"是特殊字符需要转义 sleep 1 ;; "3" echo -e "|\b\c" sleep 1 ;; "4") echo -e "/\b\c" sleep 1 ;; *) rcount_num=0 #还原变量的值,让循环继续 esac done & #循环后台运行产生pid bg_pid=$! #"#!"保存上一个后台进程的pid sh /root/shell/while_read_line.sh kill $!
date:20190304
env或者set显示默认的环境变量; unset消除本地变量和环境变量; 按天打包网站的站点目录程序 CMD=$(date +%F); 按时间打包/etc/目录:tar czf $(date +%F).tar.gz /etc ;
位置变量 | 作用说明 |
---|---|
$0 | 获取当前执行脚本的路径名,如果执行脚本包含了路径,那么就包括脚本路径 |
$n | 获取当前执行shell脚本的第n个参数,n=1..9,当n为0时,表示脚本的文件名;如果n>9,则用大括号括起来,例如${10},接的参数以空格隔开 |
$* | 获取当前shell脚本所有传参的参数,不加引号和相同;如果给$加上双引号,例如"$\",则表示将所有的参数视为单个字符串,相当于"$1 $2 $3" |
获取当前shell脚本所有传参的参数,不加引号和$*相同;如果给加上双引号,例如:"",则表示将所有的参数视为不同的独立字符串,相当于"$1" "$2" "$3" "$..."。 | |
$? | 获取上一个指令执行状态的返回值, |
$$ | 获取当前执行的shell脚本的进程号(pid) |
$! | 获取上一个在后台工作进程的进程号 |
$_ | 获取在此之前执行的命令或脚本的最后一个参数 |
date:20190305
#位置参数大于9需要用大括号括起来 cat n.sh echo $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} sh n.sh {a..z}
echo $0 ,如果不带脚本路径,输出的就是脚本的名称,带上路径执行脚本,$0也会带着路径 set -- "I am" km boy #通过set设置3个字符串参数,"--"表示清除所有的参数变量,重新设置后面的参数变量
实现系统中多次执行某一个脚本,同一时间运行的只有一个。
#!/bin/bash pid_path=/tmp/a.pid if [ -f ${pid_path} ] then kill -9 `cat ${pid_path}` rm -f ${pid_path} fi echo $$ > ${pid_path} sleep 300 &
date:20190307
exec 命令能够在不创建新的子进程的前提下,转去执行指定的命令,当指定的命令执行完毕后,该进程(也就是最初的shell)就终止了。
当使用exec打开文件后,read 命令每次都会将文件指针移动到文件的下一行进行读取,直到文件末尾,利用这个可以实现处理文件功能。
seq 10 > /tmp/tmp.log #!/bin/bash exec < /tmp/tmp.log while read line do echo $line done echo ok
shift:shift命令主要是将$1 $2等位置参数进行左移 #!/bin/bash echo $1 $2 if [ $# -eq 2 ] then shift fi echo $1
变量子串说明
id | 表达式 | 说明 |
---|---|---|
1 | ${parameter} | 返回变量$parameter的内容 |
2 | ${#parameter} | 返回变量$parameter内容的长度(按字符),也适用于特殊变量 |
3 | ${parameter:offset} | 在变量${parameter}中,从位置offset之后开始提取子串到结尾 |
4 | ${parameter:offset:length} | 在变量${parameter}中,从位置offset之后开始提取长度为length的字串 |
5 | ${parameter#word} | 从变量${parameter}开头开始删除最短匹配的word子串 |
6 | ${parameter##word} | 从变量${parameter}开头开始删除最长匹配的word子串 |
7 | ${parameter%word} | 从变量${parameter}结尾开始删除最短匹配的word子串 |
8 | ${parameter%%word} | 从变量${parameter}结尾开始删除最长匹配的word子串 |
9 | ${parameter/pattern/string} | 使用string代替第一个匹配的pattern |
10 | ${parameter//pattern/string} | 使用string代替所有匹配的pattern |
km_laoge=‘I am huge‘ echo ${#km_laoge} #返回变量值的长度 echo ${km_laoge:2} #截取km_laoge变量的内容,从第2个字符之后开始截取,默认截取后面字符的全部,第二个字符不包含在内。 echo ${km_laoge:2:2} #截取km_laoge变量的内容,从第2个字符之后开始截取,截取2个字符 oldboy="I‘m oldboy,oldboy" echo ${oldboy/oldboy/oldgirl} #"/"表示替换匹配的第一个字符串 echo ${oldboy//oldboy/oldgirl} #"//"表示替换匹配的所有字符串
touch stu_1029999_{1..5}_finished.jpg for i in `ls *finished.jgp`; do mv $i `echo {$i/_finished/}` ; done
表达式 | 说明 |
---|---|
${parameter:-word} | 如果parameter的变量的值为空或未赋值,则会返回word字符串并替代变量的值 |
${parameter:=word} | 如果parameter的变量的值为空或未赋值,则设置这个变量值为word,并返回其值。位置变量和特殊变量不适用 |
${parameter:?word} | 如果parameter的变量的值为空或未赋值,那么word字符串将被作为标准错误输出,否则输出变量的值 |
${parameter:+word} | 如果parameter变量值为空或未赋值,则什么都不做,否则word字符串将代替变量的值 |
变量计算
a=10 echo ${a++} #如果a在运算符(++或者--)的前面,那么在输出整个表达式时,会输出a的值 echo ${++a} #如果a在运算符(++或者--)的后面,那么在输出整个表达式时,先进行自增或自减计算 i=1 expr $i + 6 > /dev/null 2>&1 echo $? #利用expr计算来判断变量是否为整数 echo `seq -s ‘+‘ 10`=`seq -s ‘+‘ 10|bc` declare -i A=30 B=8 #<==declare -i参数可以将变量定义为整形 A=A+B #<==因为已声明是整型,因此可以直接进行运算
shell脚本的条件测试与比较
- test命令和[]是等价的,[[]]为扩展的test命令,()常用于计算
- 在[[]]中可以使用通配符等进行模式匹配,这是其区别于其他几种语法格式的地方
- && 、||、>、<等操作符可以应用于[[]]中,但不能应用于[]中,在[]中一般用-a、-o、-gt、-lt代替上述操作符
-z 如果测试字符串的长度为0,则表达式成立 -d 文件存在且为目录为真 -f 文件存在且为普通文件为真 -e 文件存在则为真 -r 文件存在且可读则为真 -s 文件存在且文件大小不为0则为真 -w 文件存在且文件可写则为真 -x 文件存在且可执行则为真 -L 文件存在且为链接文件则为真 f1 -nt f2 文件f1比文件f2新则为真 f1 -ot f2 文件f1比文件f2旧则为真
-n "字符串" 若字符串的长度不为0,则为真 -z "字符串" 若字符串的长度为0,则为真 "串1" = "串2" 若字符串1等于字符串2,则为真,可使用"=="代替"=" "串1" != "串2" 若字符串1不等于字符串2,则为真,但不能用"!=="代替"!="
- 比较符(例如=和!=)的两端一定要有空格
- "!="和"="可用于比较两个字符串是否相同