shell脚本基础知识
author:headsen chen 2017-10-19 10:22:15
个人原创,根据老男孩教学视频及老男孩老师的shell编程书籍整理出来的,转载请注明。否则依法追究法律责任
1,清除/VAR/LOG/MESSAGES日志的脚本
#!/bin/bash
# erase /var/log/message
LOG_DIR=/var/log
ROOT_UID=0
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "must be root can run this scripts"
exit 1 #退出脚本
fi
#如果切换目录不成共则给出错误提示并退出脚本
cd $LOG_DIR || {
echo "cannot change to nessesary directory"
exit 1
}
cat /dev/null >messages && {
echo "logs cleaned up"
exit 0 #退出之前返回0表示成功,返回1表示失败
}
echo "logs cleaned up fail." #当上一步执行成功后就exit了,就不会执行到这一步了,上一步没有成功,才会到这里。
exit 1
~
2,查看系统支持的SHELL
[root@paris a]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/bin/dash
/bin/tcsh
/bin/csh
3,CENTOS LINUX默认的shell是什么:bash
[root@paris a]# echo $SHELL
/bin/bash
4,脚本的第一行:#!/bin/bash 确定由哪个程序来解释这个脚本,这里就是bash.
[root@paris a]# ll /bin/sh
lrwxrwxrwx. 1 root root 4 7月 29 04:50 /bin/sh -> bash
所以,第一行写成 #!/bin/sh 也可以,如果使用这个出现异常,就改用#!/bin/bash
[root@paris a]# bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
升级shell
[root@paris a]# yum -y update bash
[root@paris a]# rpm -qa bash
bash-4.1.2-48.el6.x86_64
centos系统中默认就是以/bin/bash作为解释器,
脚本中尽量不要用中文
5,shell脚本执行过程
shell脚本运行时,先加载环境变量,顺序是:/etc/profile ---> ~/.bash_profile -----> /etc/bashrc等,完了后再加载脚本内容
shell执行顺序:从上至下,从左到右,如果碰到子脚本(脚本嵌套),先执行子脚本,等待子脚本执行完成后再执行父脚本。
6,shell脚本执行方法
6.1 bash/sh xxx.sh 当xxx.sh 本身没有+x的权限时也可以执行,这时用.或source 执行不了。---推荐方法
6.2 chmod +x xxx.sh 才可以:path/xxx.sh
./xxx.sh
例:[root@paris a]# chmod +x erase_log.sh
[root@paris a]# ll
总用量 4
-rwxr-xr-x 1 root root 478 7月 29 19:16 erase_log.sh
[root@paris a]# /tmp/a/erase_log.sh
logs cleaned up
[root@paris a]# ./erase_log.sh
logs cleaned up
6.3 source xxx.sh 和 . xxx.sh 用途完全相同,在运行时会将脚本中的变量值和函数传递到当前的父shell中。
source和.也可以执行没有+x权限的脚本。
例:source /etc/profile
source /home/oracle/.bash_profile ------ 加载oracle的环境变量
6.4 cat xxx.sh |bash --------- 将这个文件中的内容交给bash处理,例如处理开机命令 = cat xxx.sh |sh
bash <xxx.sh -----也是用bash来处理这个文件。
7,脚本常识定义:
#!/bin/bash
# author:chen
# date: 2017-07-29
# description: this scripts function is ...
# version: 1.1
...
尽量不要用中文:防止切换系统后中文乱码
有中文的情况下,对系统的字符集调整:
#export LANG="zh_CN.UTF-8"
脚本中的单引号,双引号,反引号必须是英文状态下的符号。
8,特殊的参数变量
$# --- shell脚本后接的参数个数
$0 --- shell脚本的文件路径和文件名
$n --- 获取当前执行的shell脚本的第n个参数
$* --- 获取当前脚本中所有的传参参数
$@ --- ............................
总结:$* = $@
"$*" ="$1 $2 $3 ..."
"$@" = "$1" "$2" "$3" ...
$? --- 获取执行上一个指令的状态返回值 (0:成功,非0:失败)
$$ --- 获取当前shell的pid号
$?的应用:源码包安装时判断过程是否成功:
#./configure
#echo $? -------------- 0 即成功
#make
#echo $? --------------- ...
#make install
#echo $? --------------- ...
例:cat a.sh
[ $# -ne 2 ] && {
echo "must be two args." # arg:元素
exit 119 #终止程序运行,并以119的状态值退出程序,将119赋值给shell的$?变量
}
9,参考脚本:cat /etc/init.d/rpcbind
[root@paris a]# cat /etc/init.d/rpcbind
#! /bin/sh
#
# rpcbind Start/Stop RPCbind
#
# chkconfig: 2345 13 87
# description: The rpcbind utility is a server that converts RPC program \
# numbers into universal addresses. It must be running on the \
# host to be able to make RPC calls on a server on that machine.
#
# processname: rpcbind
# probe: true
# config: /etc/sysconfig/rpcbind
# This is an interactive program, we need the current locale # 这是一个交互程序,locale:场所,这里指目录
[ -f /etc/profile.d/lang.sh ] && . /etc/profile.d/lang.sh # .执行lang.sh这个脚本
# We can't Japanese on normal console at boot time, so force LANG=C.
if [ "$LANG" = "ja" -o "$LANG" = "ja_JP.eucJP" ]; then
if [ "$TERM" = "linux" ] ; then
LANG=C
fi
fi
# Source function library.
. /etc/init.d/functions
# Source networking configuration.
[ -f /etc/sysconfig/network ] && . /etc/sysconfig/network
prog="rpcbind"
[ -f /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
RETVAL=0
uid=`id | cut -d\( -f1 | cut -d= -f2`
start() {
# Check that networking is up.
[ "$NETWORKING" = "yes" ] || exit 6
[ -f /sbin/$prog ] || exit 5
# Make sure the rpcbind is not already running.
if status $prog > /dev/null ; then
exit 0
fi
# Only root can start the service
[ $uid -ne 0 ] && exit 4
echo -n $"Starting $prog: "
daemon $prog $1 "$RPCBIND_ARGS"
RETVAL=$?
echo
if [ $RETVAL -eq 0 ] ; then
touch /var/lock/subsys/$prog
[ ! -f /var/run/rpcbind.pid ] &&
/sbin/pidof $prog > /var/run/rpcbind.pid
fi
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
killproc $prog
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && {
rm -f /var/lock/subsys/$prog
rm -f /var/run/rpcbind*
}
return $RETVAL
}
# See how we were called.
case "$1" in
start)
start
RETVAL=$?
;;
stop)
stop
RETVAL=$?
;;
status)
status $prog
RETVAL=$?
;;
restart | reload| force-reload)
stop
start
RETVAL=$?
;;
condrestart | try-restart)
if [ -f /var/lock/subsys/$prog ]; then
stop
start -w
RETVAL=$?
fi
;;
*)
echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart|try-restart}"
RETVAL=2
;;
esac
exit $RETVAL #如果返回值不等于0,则跳过条件表达式的判断,在这里直接作为返回值传给执行stop函数的脚本。
........................................................................................................................
10,echo的特殊参数应用实例
-n 不换行输出内容,默认是换行输出内容
-e 解析转义字符
常见转义字符: \n 换行
\r 回车
\t 制表符tab
\b 退格
[root@paris a]# echo oldboy;echo oldgirl
oldboy
oldgirl
[root@paris a]# echo -n oldboy;echo oldgirl
oldboyoldgirl
[root@paris a]# echo -e "oldboy\toldgril\noldboy\toldgirl"
oldboy oldgril
oldboy oldgirl
[root@paris a]# echo -e "1\b23" -------------- \b就是这个符号前面的这个字符不显示
23
11,exec
exec 能够在不创建新的子进程的前提下,转去执行指定的命令,指定的命令完成后,该进程(最初的shell)就终止了
[root@paris 桌面]# useradd chen
[root@paris 桌面]# su - chen
[chen@paris ~]$ exec date
2017年 07月 29日 星期六 21:28:49 CST
[root@paris 桌面]#
12,shift
在shell中,将参数的位置向左移动,原来的$3变成$2,$1就消失了
[root@paris 桌面]# vim b.sh
echo $1 $2
if [ $# -eq 2 ];then
shift
echo $1
fi
[root@paris 桌面]# sh b.sh 1 2
1 2
2
13,exit
退出shell脚本,后面可以指定一个数位作为返回状态,该数值赋值给$?
#cat b.sh
echo $1 $2
if [ $# -eq 2 ];then
shift
echo $1
fi
exit 5
[root@shiyan tmp]# sh b.sh 5 9
5 9
9
[root@shiyan tmp]# echo $?
5
14,打印shell变量的长度
[root@paris a]# OLDBOY="I am oldboy"
[root@paris a]# echo ${OLDBOY}
I am oldboy
[root@paris a]# echo OLDBOY
OLDBOY
[root@paris a]# echo $OLDBOY
I am oldboy
[root@paris a]# echo ${#OLDBOY} ------------------- { }大括号内的必需是变量,不能是具体的字符
11
[root@paris a]# echo $OLDBOY |wc -L
11
[root@paris a]# expr length "$OLDBOY" -------------------- 引号内必需是变量。不能是具体的字符
11
[root@paris a]# expr length "asdfadgagahasf" --------------- 引号内可以是具体的字符
14
[root@paris a]# echo "$OLDBOY" |awk '{print length($0)}'
11
[root@paris a]# echo "$OLDBOY" |awk '{print length}'
11
wc -l用于统计行数
wc -L用于统计单行的字符数
[root@paris a]# echo lsafjajsf |wc -l
1
[root@paris a]# echo lsafjajsf |wc -L
9
awk可以统计每行的字符数
[root@paris a]# cat b.sh
echo $1 $2
if [ $# -eq 2 ];then
shift
echo $1
fi
[root@paris a]# cat b.sh |awk '{print length}'
10
20
8
9
2
[root@shiyan tmp]# cat b.sh |awk '{print length $0}'
10echo $1 $2
20if [ $# -eq 2 ];then
8 shift
10 echo $1
2fi
6exit 5
15,将下列字符串中的字符数小于等于6的单词打印出来:i am oldboy linux,welcome to our training
[root@paris a]# echo "i am oldboy linux,welcome to our training" |tr "," " "
# vim count_words.sh
#!/bin/bash
# print words where counts less than 6
for i in i am oldboy linux welcome to our training
do
a=`expr length "$i"`
if [ $a -le 6 ];then
echo "$i"
fi
done
16,截取变量的字符,从第二个字符之后开始
[root@paris a]# echo $OLDBOY
I am oldboy
[root@paris a]# echo ${OLDBOY:2}
am oldboy
[root@paris a]# echo ${OLDBOY:2:2}
am
17,替换变量中的内容
[root@paris a]# OLDBOY="I am oldboy,yes,oldboy"
[root@paris a]# echo ${OLDBOY/oldboy/oldgirl} ----------- / 匹配行中的第一个字符
I am oldgirl,yes,oldboy
[root@paris a]# echo ${OLDBOY//oldboy/oldgirl} ----------- // 匹配行中的全部字符串
I am oldgirl,yes,oldgirl
[root@paris a]#
18,去掉下面文件中的_finished字符串
[root@paris a]# touch 20170730_{1..10}_finished.jpg
[root@paris a]# ls
20170730_10_finished.jpg 20170730_5_finished.jpg b.sh
20170730_1_finished.jpg 20170730_6_finished.jpg b.txt
20170730_2_finished.jpg 20170730_7_finished.jpg count_words.sh
20170730_3_finished.jpg 20170730_8_finished.jpg erase_log.sh
20170730_4_finished.jpg 20170730_9_finished.jpg
[root@paris a]# rename _finished. . *.jpg ---------------------rename a b filename (a换成b)
[root@paris a]# ls
20170730_10.jpg 20170730_3.jpg 20170730_6.jpg 20170730_9.jpg count_words.sh
20170730_1.jpg 20170730_4.jpg 20170730_7.jpg b.sh erase_log.sh
20170730_2.jpg 20170730_5.jpg 20170730_8.jpg b.txt
19, 删除7天前的过期备份数据
vim del.sh
#!/bin/bash
find ${path-/tmp} -name "*.tar.gz" -type f -mtime +7 |xargs rm -f
#=find ${path:-/tmp} -name "*.tar.gz" -type f -mtime +7 |xargs rm -f
当系统里定义了$path时,就去$path里找文件,当系统没有定义$path时,就去/tmp下去找(用/tmp代替当前的变量值)
20,shell中的算术运算
= 赋值
+= 累积加 a+=1 -----> a=a+1
-= 累积减 a-=1 -----> a=a-1
shell中的算术运算符号
(()) 整数运算符,效率高
let 和(())一样,整数运算符
expr 整数运算
$[]整数运算
bc 整数和小数运算
awk 整数和小数
21,(())应用举例:
[root@paris a]# echo $((1+1))
2
[root@paris a]# echo $((6-3))
3
[root@paris a]# ((i=5)) --------------变量放在了括号的里边
[root@paris a]# ((i=i*2))
[root@paris a]# echo $i
10
[root@paris a]# ((a=1+2**3-4%3))
[root@paris a]# echo $a
8
[root@paris a]# echo $((1+2**3-4%3)) ----------- 注意:$不要少了。少了就没有值了
8
[root@paris a]# a=$((1+2**3-4%3)) -------------变量放在了括号的外边
[root@paris a]# echo $a
8
[root@paris a]# echo $((100*(100+1)/2)) --------- 计算1+2+3...+100的和,括号外加$
5050
[root@paris a]# echo ((1+2**3-4%3)) -------- 少了$,所以会报错
bash: syntax error near unexpected token `('
[root@paris a]# ((1+2**3-4%3)) ---------没有任何结果出来的
[root@paris a]# a=8
[root@paris a]# echo $((a=a+1)) ------------括号中的a可以加$,也可以不加$,都可以。
9
[root@paris a]# echo $((a=a-1))
8
[root@paris a]# echo $((a+=1))
9
比较运算:
[root@paris a]# echo $((3<8)) ------------- 正确就返回1,错误就返回0(和shell的上个进程执行结果$?相反)
1
[root@paris a]# echo $((3>8))
0
[root@paris a]# echo $((8=8)) --------- = 在Linux中表示赋值的意思,此时会报语法错误
bash: 8=8: attempted assignment to non-variable (error token is "=8")
[root@paris a]# echo $((8==8)) ---------- == 判断是否相等
1
20,脚本基础内容补充 :
read variable #读取变量给variable
read x y
#可同时读取多个变量
read
#自动读给REPLY
read –p “Please input: ”
#自动读给REPLY[root@localhost shell]# cat d.sh
#!/bin/bash
read -p "three variable: " x y z
echo $x
echo $y
echo $z
read
echo $REPLY
read -p "please input: "
echo $REPLY