shell脚本编程
export PS1='\n\e[1;37m[\e[m\e[1;32m\u\e[m\e[1;33m@\e[m\e[1;35m\H\e[m \e[4m`pwd`\e[m\e[1;37m]\e[m\e[1;36m\e[m\n\$'
当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/下面增加脚本
shell脚本
协程,协程可以同时做两件事。它在后台生成一个子shell,并在这个子shell中执行命令。要进行协程处理,得使用coproc命令,还要有在子shell中执行的命令.当外部命令执行时,会创建一个子进程。这种操作被称为衍生(forking)。
break:中断整个循环
continue:将控制转移到下一段代码,但是会继续执行循环
exit:退出整个循环
return:用于在函数中将数据返回,返回一个结果或代码给调用脚本
shell脚本中由于安全原因不支持执行suid,sgid,sticky bit
命令行参数2,9是位置参数,$0指向实际的命令、程序、shell脚本或函数
在一个函数中出现的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>&- #关闭文件描述符4
linux系统将每个对象当作文件处理。这包括输入和输出进程。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 <
捕获命令输出
#!/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="[email protected]" #收件人地址
> ${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 3" |
$@ | 获取当前shell脚本所有传参的参数,不加引号和@加上双引号,例如:"1" "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内容的长度(按字符),也适用于特殊变量 |
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,则为真,但不能用"!=="代替"!="
- 比较符(例如=和!=)的两端一定要有空格
- "!="和"="可用于比较两个字符串是否相同
wget --spider --timeout=10 --tries=2 www.baidu.com 模拟爬取
sort -u :依次排序,且路径名只出现一次