Linux-bash脚本编程

来源:互联网 发布:mysql查看锁表情况 编辑:程序博客网 时间:2024/05/18 03:54
静态语言:编译型语言
强类型(变量)
必须先编译完成后,才能使用
如:C、C++、JAVA、C#
动态语言:解释型语言
解释完一条就可以执行一条,边解释边执行
如:PHP、shell(面向过程)、python(面向对象)、perl
shell:弱类型编程语言


强类型:变量在使用前,必须实现声明,甚至还需要初始化
弱类型:变量用时声明




变量赋值:[set] VAR_NAME=VALUE,set通常省略


bash变量类型:
环境变量
本地变量(局部变量)
位置变量
特殊变量(系统变量)




本地变量:VAR_NAME=VALUE:整个bash进程
局部变量:local VAR_NAME=VALUE,作用域为当前代码段
环境变量:作用域为当前shell进场及其子进程
export VAR_NAME=VALUE "导出"
位置变量:$1,$2,...
特殊变量:$?(上一个命令的执行状态返回值)


程序执行,可能有两类返回值
1、程序执行结果
2、程序状态返回代码(0-255)
0:正确执行
1-255:错误执行,1,2,127系统预留
/dev/null:软件设备,bit bucket
eg:id student &> /dev/null,什么也不输出


撤销变量:
unset VAR_NAME


查看当前shell中变量(环境、本地变量):set
查看当前shell中环境变量:pringenv,env,export


脚本:命令的堆砌,按照实际需要,结合命令流程控制机制实现的源程序


#!/bin/bash(可执行执行的路径) --》shebang,魔数 


如何执行脚本?
1、将脚本的权限改为可执行,然后直接写脚本名执行
2、bash 脚本名


注意:
脚本在执行时会启动一个当前shell的一个子shell进程,如果是命令行中启动的脚本会
继承当前shell环境变量,如果是系统自动执行的脚本(非命令行启动,另外登陆一个shell)就需要自我定义
需要各环境变量


引用变量:${VAR_NAME},{}通常可以省略,但有时不可以省略


bash中如何实现条件判断?
条件测试类型:
正数测试
字符测试
文件测试
条件测试的表达式:
[ expression ]
[[ expression ]]
test expression
正数比较:
-eq:测试两个正数是否相等:$A -eq $B
-ne:测试两个正数是否不等
-gt:>
-lt:<
-ge:>=
le:<=
命令间的逻辑关系:
逻辑与:&&
逻辑或:||
eg:如果用户不存,就添加,否则,显示其已经存在
! id user1 && useradd user1 || echo "user1 exists"


这样写不太方便,shell提供了专门的条件判断结构
if 判断条件;then
statement1
statement2
...
fi




if 判断条件;then
  statement1
else
statement2
fi




if 判断条件;then
statement1
..
elif 判断条件2;then
statement2
...
else
statement3
...
fi



NAME=user1
if [ id $NAME ];then
echo "admin"
else
echo "common users"
fi




练习,写一个脚本,完成以下要求:
1、添加3个用户user1, user2, user3;但要先判断用户是否存在,不存在而后再添加;
2、添加完成后,显示一共添加了几个用户;当然,不能包括因为事先存在而没有添加的;
3、最后显示当前系统上共有多少个用户;






练习,写一个脚本,完成以下要求:
给定一个用户:
1、如果其UID为0,就显示此为管理员;
2、否则,就显示其为普通用户;


如果 UID为0;那么
  显示为管理员
否则
  显示为普通用户
  
NAME=user16
USERID=`id -u $NAME`
if [ $USERID -eq 0 ]; then
  echo "Admin"
else
  echo "common user."
fi






NAME=user16
if [ `id -u $NAME` -eq 0 ]; then
  echo "Admin"
else
  echo "common user."
fi




if id $NAME; then
  
练习:写一个脚本
判断当前系统上是否有用户的默认shell为bash;
   如果有,就显示有多少个这类用户;否则,就显示没有这类用户;
grep "bash$" /etc/passwd &> /dev/null
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
   
if grep "bash$" /etc/passwd &> /dev/null; then

提示:“引用”一个命令的执行结果,要使用命令引用;比如: RESAULTS=`wc -l /etc/passwd | cut -d: -f1`;
      使用一个命令的执行状态结果,要直接执行此命令,一定不能引用;比如: if id user1一句中的id命令就一定不能加引号;
 如果想把一个命令的执行结果赋值给某变量,要使用命令引用,比如USERID=`id -u user1`;
      如果想把一个命令的执行状态结果保存下来,并作为命令执行成功与否的判断条件,则需要先执行此命令,而后引用其状态结果,如
id -u user1
RETVAL=$?
此句绝对不可以写为RETVAL=`id -u user1`;


练习:写一个脚本
判断当前系统上是否有用户的默认shell为bash;
   如果有,就显示其中一个的用户名;否则,就显示没有这类用户;


练习:写一个脚本
给定一个文件,比如/etc/inittab
判断这个文件中是否有空白行;
如果有,则显示其空白行数;否则,显示没有空白行。
#!/bin/bash
A=`grep '^$' /etc/inittab | wc -l`
if [ $A -gt 0 ]; then
 echo "$A"
else
 echo "meiyoukongbaihang"
fi
                 —— by 张帅
 
#!/bin/bash
FILE=/etc/inittab
if [ ! -e $FILE ]; then
  echo "No $FILE."
  exit 8
fi


if grep "^$" $FILE &> /dev/null; then
  echo "Total blank lines: `grep "^$" $FILE | wc -l`."
else
  echo "No blank line."
fi


练习:写一个脚本
给定一个用户,判断其UID与GID是否一样
如果一样,就显示此用户为“good guy”;否则,就显示此用户为“bad guy”。
#!/bin/bash
USERNAME=user1
USERID=`id -u $USERNAME`
GROUPID=`id -g $USERNAME`
if [ $USERID -eq $GROUPID ]; then
  echo "Good guy."
else
  echo "Bad guy."
fi


进一步要求:不使用id命令获得其id号;


#!/bin/bash
#
USERNAME=user1
if ! grep "^$USERNAME\>" /etc/passwd &> /dev/null; then
  echo "No such user: $USERNAME."
  exit 1
fi


USERID=`grep "^$USERNAME\>" /etc/passwd | cut -d: -f3`
GROUPID=`grep "^$USERNAME\>" /etc/passwd | cut -d: -f4`
if [ $USERID -eq $GROUPID ]; then
  echo "Good guy."
else
  echo "Bad guy."
fi












练习:写一个脚本
给定一个用户,获取其密码警告期限;
而后判断用户密码使用期限是否已经小于警告期限;
提示:计算方法,最长使用期限减去已经使用的天数即为剩余使用期限;

如果小于,则显示“Warning”;否则,就显示“OK”。


圆整:丢弃小数点后的所有内容


#!/bin/bash
W=`grep "student" /etc/shadow | cut -d: -f6`
S=`date +%s`
T=`expr $S/86400`
L=`grep "^student" /etc/shadow | cut -d: -f5`
N=`grep "^student" /etc/shadow | cut -d: -f3`
SY=$[$L-$[$T-$N]]


if [ $SY -lt $W ]; then
  echo 'Warning'
else
  echo 'OK'
fi


—— by 董利东
shell中如何进行算术运算:
A=3
B=6
1、let 算术运算表达式
let C=$A+$B
2、$[算术运算表达式]
C=$[$A+$B]
3、$((算术运算表达式))
C=$(($A+$B))
4、expr 算术运算表达式,表达式中各操作数及运算符之间要有空格,而且要使用命令引用
C=`expr $A + $B`








测试方法:
[ expression ]
[[ expression ]]
test expression


bash中常用的条件测试有三种:
整数测试:
-gt
-le
-ne
-eq
-ge
-lt


INT1=63
INT2=77
[ $INT1 -eq $INI2 ]
[[ $INT1 -eq $INT2 ]]
test $INT1 -eq $INT2  


if grep "^$USERNAME\>" /etc/passwd ;then
注意:这里的条件判断没有加[],if可以自动的判定命令执行完成后的
返回状态码,如果是0就表示执行成功,然后执行then


文件测试:
-e FILE:测试文件是否存在
-f FILE:测试文件是否为普通文件
-d FILE:测试指定路径是否为目录
-r FILE: 测试当前用户对指定文件是否有读取权限;
-w 
-x


定义脚本退出状态码


exit: 退出脚本
exit #
如果脚本没有明确定义退出状态码,那么,最后执行的一条命令的退出码即为脚本的退出状态码;




if [ -e /etc/passwd ];then


测试脚本是否有语法错误:
bash -n 文件
bash -x 脚本:单步执行


bash命令的类型:
本地变量(局部变量)
环境变量
位置变量:
$1,$2,...(在脚本中引用我们输入的变量)
shift:参数切换
shift的用法:


特殊变量:
$?:上一条命令的退出命令码
$#:参数的个数(下面的红色就是2个参数)
$*:参数列表
$@:参数列表
#执行脚本
./first.sh /etc/passwd /etc/inittab 
$1:/etc/passwd
$2:/etc/inittab


练习:写一个脚本
给脚本传递两个参数(整数);
显示此两者之和,之乘积;
#!/bin/bash
#
if [ $# -lt 2 ]; then
  echo "Usage: cacl.sh ARG1 ARG2"
  exit 8
fi


echo "The sum is: $[$1+$2]."
echo "The prod is: $[$1*$2]."




必须要学好的三个文本处理工具
grep,sed(流编辑器),awk


sed基本用法:
sed:Stream  EDitor


默认不编辑原文件,仅对模式空间中的数据做处理,而后,处理结束后,将模式空间打印至屏幕
sed [options] 'AddressCommand' file ...
-n: 静默模式,不再默认显示模式空间中的内容
-i: 直接修改原文件
-e SCRIPT -e SCRIPT:可以同时执行多个脚本,就是AddressCommand
-f /PATH/TO/SED_SCRIPT,从文件中读取脚本(脚本写在scripts文件中,一个一个读取然后对file进行处理)
sed -f /path/to/scripts  file
-r: 表示使用扩展正则表达式

Address:
1、StartLine,EndLine
比如1,100
$:最后一行
2、/RegExp/
/^root/
3、/pattern1/,/pattern2/
第一次被pattern1匹配到的行开始,至第一次被pattern2匹配到的行结束,这中间的所有行
4、LineNumber
指定的行
5、StartLine, +N
从startLine开始,向后的N行;

Command:
d: 删除符合条件的行;
p: 打印命令,显示符合条件的行;注意:这会重复打印两次,因为sed默认会打印模式空间中的内容,可以在前面加上-n
a \string: 在指定的行后面追加新行,内容为string
\n:可以用于换行
i \string: 在指定的行前面添加新行,内容为string
r FILE: 将指定的文件的内容添加至符合条件的行处
w FILE: 将地址指定的范围内的行另存至指定的文件中; 
s/pattern/string/修饰符: 查找并替换,默认只替换每行中第一次被模式匹配到的字符串(替换的是字符串而不是整行)
加修饰符
g: 全局替换,(整行被匹配的字符串全部都要替换)
i: 忽略字符大小写
s///: s###, s@@@(可以使用这样的字符来替换前面的/,功能一样)
eg:sed 's@/@#@' /etc/fstab
将/替换为#
grep里的后向引用:\(\), \1, \2在这里同样使用


l..e:  like-->liker
 love-->lover
 
 

&: 引用模式匹配整个串
eg:sed 's#l..e#&r#' sed.txt
  sed 's#\(l..e\)#\1r#g' sed.txt


 like-->Like
 love-->Love
eg:sed 's#l\(..e\)#L\1#g' sed.txt
可以不匹配整个串


sed练习:
1、删除/etc/grub.conf文件中行首的空白符;
sed -r 's@^[[:spapce:]]+@@g' /etc/grub.conf
2、替换/etc/inittab文件中"id:3:initdefault:"一行中的数字为5;
sed 's@\(id:\)[0-9]\(:initdefault:\)@\15\2@g' /etc/inittab
3、删除/etc/inittab文件中的空白行;
sed '/^$/d' /etc/inittab
4、删除/etc/inittab文件中开头的#号; 
sed 's@^#@@g' /etc/inittab
5、删除某文件中开头的#号及后面的空白字符,但要求#号后面必须有空白字符;
sed -r 's@^#[[:space:]]+@@g' /etc/inittab
6、删除某文件中以空白字符后面跟#类的行中的开头的空白字符及#
sed -r 's@^[[:space:]]+#@@g' /etc/inittab
7、取出一个文件路径的目录名称;
echo "/etc/rc.d/" | sed -r 's@^(/.*/)[^/]+/?@\1@g'
文件名:
echo "/etc/rc.d/" | sed -r 's@^/.*/([^/]+)/?@\1@g'




字符测试:
==:测试是否相等,相等为真,不等为假
!=: 测试是否不等,不等为真,等为假
>
<
-n string: 测试指定字符串是否为空,空则真,不空则假
-z string: 测试指定字符串是否不空,不空为真,空则为假


eg:
A=hello
B=hi
[[ "$A" == "$B" ]]
看清用法,==两边也有空格,一个=也行,但也要有空格


练习:
传递三个参数给脚本,第一个为整数,第二个为算术运算符,第三个为整数,将计算结果显示出来,要求保留两位精度。形如:
./calc.sh 5 / 2


eg:
echo "scale=2;100/22;" | bc
bc <<< "scale=2;111/22;" 特殊用法






循环结构:
1、for
for 变量 in 列表;do(如果do写在下一行就不用在前面加;)
循环体
done
如何生成列表?
{1..100} 
`seq [起始数[步进长度]] 结束数`
2、while
3、until






declare -i SUM=0
integer
-x声明为环境变量


写一个脚本:
1、设定变量FILE的值为/etc/passwd
2、依次向/etc/passwd中的每个用户问好,并显示对方的shell,形如:  
Hello, root, your shell: /bin/bash
3、统计一共有多少个用户


for I in `seq 1 $LINES`; do echo "Hello, `head -n $I /etc/passwd | tail -1 | cut -d: -f1`"; done






测试:
1、整数测试
2、字符测试
3、文件测试
组合测试条件
-a:与关系
-o:或关系
!:非关系
if [ $# -gt 1 -a $# -le 3 ]
if [ $# -gt 1 ] && [ $# -le 3 ]












写一个脚本,利用RANDOM生成10个随机数,并找出其中的最大值,和最小值;
#!/bin/bash
#
declare -i MAX=0
declare -i MIN=0


for I in {1..10}; do
  MYRAND=$RANDOM
  [ $I -eq 1 ] && MIN=$MYRAND
  if [ $I -le 9 ]; then
    echo -n "$MYRAND,"
  else
    echo "$MYRAND"
  fi
  [ $MYRAND -gt $MAX ] && MAX=$MYRAND
  [ $MYRAND -lt $MIN ] && MIN=$MYRAND
done


echo $MAX, $MIN




面向过程
控制结构
顺序结构
选择结构
循环结构


选择结构:
if: 单分支、双分支、多分支
if CONDITION; then
  statement
  ...
fi


if CONDITION; then
  statement
  ...
else
  statement
  ...
fi


if CONDITION1; then
  statement
  ...
elif CONDITION2; then
  statement
  ...
esle
  statement
  ...
fi








case语句:选择结构


case SWITCH in 
value1)
  statement
  ...
  ;;
value2)
  statement
  ...
  ;;
*)
  statement
  ...
  ;;
esac




eg:
case $1 in 
[0-9])
echo "it's a digist"
;;
[a-z])
echo "is's a char"
;;
*)
echo "it's others"
;;
esac














#!/bin/bash
#
DEBUG=0
ADD=0
DEL=0


for I in `seq 0 $#`; do
  if [ $# -gt 0 ]; then
 case $1 in
 -v|--verbose)
DEBUG=1
shift ;;  ###将刚刚引用的变量踢掉,下一个就变成了第一个参数
 -h|--help)
echo "Usage: `basename $0` --add USER_LIST --del USER_LIST -v|--verbose -h|--help"
exit 0
;;
 --add)
ADD=1
ADDUSERS=$2
shift 2
;;
 --del)
DEL=1
DELUSERS=$2
shift 2
;;
 *)
echo "Usage: `basename $0` --add USER_LIST --del USER_LIST -v|--verbose -h|--help"
exit 7
;;
    esac
  fi
done


if [ $ADD -eq 1 ]; then
  for USER in `echo $ADDUSERS | sed 's@,@ @g'`; do
    if id $USER &> /dev/null; then
      [ $DEBUG -eq 1 ] && echo "$USER exists."
    else
      useradd $USER
      [ $DEBUG -eq 1 ] && echo "Add user $USER finished."
    fi
  done
fi


if [ $DEL -eq 1 ]; then
  for USER in `echo $DELUSERS | sed 's@,@ @g'`; do
    if id $USER &> /dev/null; then
      userdel -r $USER
      [ $DEBUG -eq 1 ] && echo "Delete $USER finished."
    else
      [ $DEBUG -eq 1 ] && echo "$USER not exist."
    fi
  done
fi






练习:写一个脚本showlogged.sh,其用法格式为:
showlogged.sh -v -c -h|--help
其中,-h选项只能单独使用,用于显示帮助信息;-c选项时,显示当前系统上登录的所有用户数;如果同时使用了-v选项,则既显示同时登录的用户数,又显示登录的用户的相关信息;如
Logged users: 4. 


They are:
root     tty2         Feb 18 02:41
root     pts/1        Mar  8 08:36 (172.16.100.177)
root     pts/5        Mar  8 07:56 (172.16.100.177)
hadoop   pts/6        Mar  8 09:16 (172.16.100.177)


#!/bin/bash
#
declare -i SHOWNUM=0
declare -i SHOWUSERS=0


for I in `seq 1 $#`; do
  if [ $# -gt 0 ]; then
    case $1 in
    -h|--help)
      echo "Usage: `basename $0` -h|--help -c|--count -v|--verbose"
      exit 0 ;;
    -v|--verbose)
      let SHOWUSERS=1
      shift ;;
    -c|--count)
      let SHOWNUM=1
      shift ;;
    *)
      echo "Usage: `basename $0` -h|--help -c|--count -v|--verbose"
      exit 8 ;;
    esac
  fi
done


if [ $SHOWNUM -eq 1 ]; then
  echo "Logged users: `who | wc -l`."
  if [ $SHOWUSERS -eq 1 ]; then
    echo "They are:"
    who
  fi
fi


0 0
原创粉丝点击