(一)Shell编程概述

1.1 shell简述

  Shell编程和JavaScript非常相似,Shell和JavaScript都是弱类型语言,同时也都是解释型语言。解释型语言需要解释器,JavaScript的解释器是浏览器,Shell脚本的解释器时bash,是一个shell、一个命令行用户接口。

1.2 bash简述

  bash在执行或者解释脚本的时候,此bash非彼bash。用户登录进来的时候就用一个bash。通过敲一个命令来解释脚本的时候,是在当前bash中打开一个新的bash,这两个bash是父子关系。其实在空行敲回车也是从当前bash打开新的bash。

  在命令行中输入$ps -aux |grep bash可以看见当前只有一个bash。这个bash就是系统和用户进行交互的一个用户接口。

  在命令行中输入命令bash回车。这就是打开了一个新的bash。再次输入ps -aux |grep bash可以看见当前有两个bash。因为敲一个命令就是一个程序的入口,这个命令就被启动,这个程序就是bash。这两个bash就是父子关系。这种有层次结构的bash可以使用exit退出。

(二)变量

2.1 变量的类型  

  shell脚本的变量实际上也是bash的变量,共有四种类型:环境变量、本地变量(或称局部变量,但略有差别)、位置变量、特殊变量。环境变量,作用在当前bash和所有子bash,与本地变量的区别在于作用域不同;本地变量(局部变量,当前代码段),作用在当前bash,所有子bash都不能用;位置变量和特殊变量,是bash内置的用来保存某些特殊数据的变量,也不存在作用域的问题(也叫系统变量)。

2.2 环境变量

  环境变量,$export 变量名=值,其作用域为当前的shell和其子shell。

  注意:脚本在执行时都会启动一个子shell进程,命令行中启动的脚本会继承当前shell环境变量;系统自动启动脚本(非命令行启动),则需要自我定义环境变量。

2.3 本地变量(局部变量)

  本地变量是只属于某一个bash的变量。例如,$var_name=值,其作用域是整个bash进程。

  局部变量类似,例如$local var_name =值,其作用域为当前代码段。

2.4 位置变量

  位置变量是指用于脚本执行的参数,$1表示第一个参数,以此类推$1,$2…。

2.5 特殊变量

  特殊变量是每一个bash进程中特有的一些变量,无需声明。总共就几个,$?和$#j较为常用。

2.5.1 $?

  $? 表示上一个命令的执行状态返回值。事实上,任何一个程序执行的结果只有两类,即程序有两类返回值:

  第一类是命令的执行结果。例如输入$ls显示的目录下的所有目录和文件就是执行结果,再如输入$id -u root看见的0就是执行结果。

  第二类是命令的执行状态。任何命令的执行状态都被$?这个变量存储起来。$?: 0表示正确,1-255表示错误。例如使用$echo “$?”查看上一个命令的执行状态,打印上一个命令的执行状态0表示正确,非0表示错误。注意不能查看上上个命令的执行状态,因为被覆盖了。

2.5.2 $#

  $# 表示传递到脚本的参数个数。

2.5.3 $*

  $* 表示传递到脚本的参数,与位置变量不同,此选项参数可超过9个。

2.5.4 $$

  $$ 表示脚本运行时当前进程的ID号,常用作临时变量的后缀。

2.5.5 $!

  $! 表示后台运行的(&)最后一个进程的ID号 。

2.5.6 $@

  $@ 与$#相同,使用时加引号,并在引号中返回参数个数。

2.5.7 $-

  $- 表示上一个命令的最后一个参数 。

2.6  变量的声明、撤销、查看与引用

2.6.1 声明变量

  环境变量的声明必须加export;本地变量和局部变量的声明不需写export,直接写变量名后面接值即可。

2.6.2 撤销变量

  使用unset 变量名,撤销变量。

2.6.3 查看变量

  查看shell中变量:set

  查看shell中的环境变量:printenv或env

2.6.4 引用变量

a) 引用变量

  引用变量格式为:${变量名},一般可以省略{}。只有特殊情况下不能省略,具体效果见下图:

b) 单引号:强引用

  单引号:强引用,不作变量替换,引用字符串常量(单引号的内容都是字符串)。

c) 双引号:弱引用

  双引号:弱引用,做变量替换。

d) 反引号:命令替换

  反引号:``命令替换。当字符串表示一个命令,需要执行时,则要用反引号。

(三)输出重定向

3.1 命令执行结果保存在一个文件中

3.1.1 >覆盖重定向

  $ls >/path/file。不会重定向错误结果

3.1.2 >> 追加重定向

  $cat file1 file2 >> file3 //将file1和file2的文件内容追加重定向到file3后面。

3.1.3 2> 错误覆盖重定向

  程序执行出错的结果放到文件中。

3.1.4 2>>错误追加重定

  程序执行出错的结果放到文件中。

3.1.5 &> 全部覆盖重定向

  无论命令执行对错,都会覆盖重定向到文件。

3.1.6 &>> 全部追加重定向

  无论命令执行对错,都会追加重定向到文件。

3.2 命令执行结果直接丢弃

  /dev/null文件,dev是设备,null是一个设备文件,称之为数据黑洞,所有数据放到这里都无法恢复。

  $ls >> /dev/null

(四) 脚本

  通过组织命令及变量来完成具有某种业务逻辑的功能称之为脚本。

4.1 简单脚本案例

4.1.1 案例一(添加用户)

a) 业务描述

  添加6个用户,每个用户的密码同用户名,不显示添加密码的信息,并给显示添加用户成功信息。

b) 编写脚本

  首先,创建脚本。

  1. mkdir /opt/shell
  2. cd /opt/shell
  3. vim test1.sh 

  其次,编写脚本主体。

  1. #!/bin/bash#脚本的第一行一定是这个脚本的声明,这里声明的是脚本的解释器。
  2. # 用户可以有参数传过来,也可以直接定义一个变量。
  3. U=’user1 #声明变量,本地变量的声明直接变量名=值,无需export。这里选择单引号,因为user1是个字
  4. 符串,只需强引就行,双引号也行。
  5. useradd $U #引用变量
  6. #设置密码和用户名相同,但是设置完之后不显示passwd的执行结果。但是这个passwd会在控制台出现信息
  7. ,所以需要使用管道传入数据,并且重定向。
  8. echo "$U" | passwd --stdin $U &>/dev/null #前一个命令的输出传给后一个命令的输入。
  9. echo "success." #打印成功信息。 

  以上是添加1个给定名称的用户,可以稍作修改,变为参数传入的方式动态添加用户:  

  1. cp test1.sh test2.sh
  2. vim test2.sh
  1. #!/bin/bash
  2. #
  3. useradd $ #这里是使用传递参数的方式,1代表1个参数,实际上$1是特殊变量$*的特例
  4. echo "$1" | passwd --stdin $ &>/dev/null
  5. echo "Add user $1 success."

c) 执行脚本

  这个两个脚本当前没有执行权限。有两种方法使该脚本有执行权限:一是添加执行权限;二是,使用命令sh /path/脚本名(sh是一个执行脚本的命令)。

  脚本1的执行:

  $sh test1.sh

  脚本2的执行:

  $sh test2.sh username

  查看是否添加成功:

  $cat /etc/passwd

d) 查看脚本执行程度

  $bash -x /opt/shell/test2.sh

4.1.2 案例二(删除用户)

a) 业务描述

  写一个脚本,完成以下任务:第一,使用一个变量保存一个用户名;第二,删除此变量中的用户,且一并删除其家目录;第三,显示“用户删除成功”信息。

b) 编写脚本

  首先,创建脚本。

  1. vim /opt/shell/test3.sh 

  其次,编写脚本主体。

  1. #!/bin/bash
  2. U=$* #使用一个变量存储用户名
  3. userdel $* #删除这个用户
  4. rm -rf /home/$* #删除该用户的家目录
  5. echo "delete user and $*home success." #打印成功信息

c) 执行脚本

  1. sh test3.sh username #注意一定要有参数(要删除的用户),不然会删除整个/home目录
  2. cat /etc/passwd #查看用户是否删除成功。
  3. ls /home #查看用户的家目录是否删除成功

4.2 条件判断

  条件判断是布尔类型,而shell是弱类型的语言,也即没有类型,所以表达式只有真或假。

4.2.1 条件表达式

  条件表达式有两种形式:一是[ expression ],注意中括号和表达式之间一定要加空格隔开;二是,test expression,test+空格+表达式。

4.2.2 整数的比较

  =:-eq  等于

  !=:-ne  不等于

  >:-gt  大于

  <:-lt  小于

  >=:-ge  大于或等于

  <=:-le  小于或等于

4.2.3 逻辑运算 

a) 命令的逻辑关系

  在linux中,命令执行状态:0为真,其他为假。

b) 逻辑与或非

  可以使用离散数学的逻辑与或非来进行逻辑判断。

  逻辑与:&&。当第一个条件为假时,第二条件不用再判断,最终结果已经有;当第一个条件为真时,第二条件必须得判断。

  逻辑或:||。

  逻辑非:!。

c) 案例(命令逻辑关系的条件判断) 

  业务描述:添加用户前先判断是否存在,如果存在就打印该用户已存在,并且退出。

  编写脚本: 

  1. vim /opt/shell/test4.sh
  1. #!/bin/bash
  2. #
  3. id $ &>/dev/null && echo "User $1 exist" && exit #使用命令的执行状态作为逻辑判断。id $ 如果用户$1存在,则为0(真),需要继续判断后面的,所以执行echo,并且退出脚本,不再
  4. 创建该用户。退出,3是非0,当前脚本执行不成功。
    #上一行可以使用逻辑或的代替,见下两行
    #!id $1 &>/dev/null || echo "User $1 exist" #如果用户存在id $1则为0,前面加了!就是非0,就为假。前面为假,对于逻辑或来说需要继续判断后面的,所以执行echo 打印用户存在。
    #id $1 &>/dev/null && exit 3 #用户存在就退出
  5. useradd $ #添加用户
  6. id $ &>/dev/null && echo "$1" | passwd --stdin $ &>/dev/null #如果用户创建成功了>,就执行后面的echo passwd添加密码。
  7. echo "Add user $1 success." 

  执行脚本:

  1. sh test4.sh user1

4.2.4 if条件判断

a) 语法结构

  if条件表达式的语法结构为:

  1. if [ 条件 ]; then
  2. 语句
  3. elif [ 条件 ]; then
  4. 语句
  5. else
  6. 语句
  7. fi

b) if的逻辑与或

  -a :并且

  -o :或者

  例如:

  1. if [ $# -gt -a $# -lt -o $# -eq ] ; then

  如果,上一个命令传入的参数个数大于1,并且小于3,或者等于2。&&是离散数学中的逻辑与,-a是一个逻辑运算符,并且的意思,不同于&&。

c) 案例(if语句)

  业务描述:给定一个用户,如果他的UID为0则显示为管理员,否则显示其他普通用户。

  编写脚本:

  1. vim /opt/shell/test5.sh 
  1. #!/bin/bash
  2. USER_ID=`id -u $`&> /dev/null || exit #注意这里需要用反引号,这里也不能将变量名取为UID,会和环境变量冲突,这样声明的变量是只读的。
  3. if [ $USER_ID -eq ] ; then
  4. echo "admin."
  5. else
  6. echo "other."
  7. fi 

  执行脚本:

  1. sh test5.sh root

4.3 算术运算符

4.3.1 算术运算符的类型

a) let 算术运算表达式

  1. let C=$A + $B

b) $[算术表达式]

  1. C = $[$A+$B] 

c) $((算术表达式))

  1. C=$(($A+$B))

d) expr 算术表达式

  注意:表达式中各操作数及运算符之间要有空格。而且要使用命令引用。

  1. C=`expr $A + $B`

4.3.2 案例(算术运算符的使用)

a) 业务描述

  给定一个用户,获取其密码警告期限,然后判断用户密码使用期限是否已经小于警告期限,如果小于,则是显示“WARN”,否则显示密码还有多少天到期。

  密码警告期限:距离警告的天数;密码使用期限:密码有效期减去使用的时间。

b) 编写脚本

  首先,需要知道:

  $date +%s 可以获得今天的秒数

  $cat /etc/shadow 可以获得密码使用时间。

  1. cat /etc/shadow | grep root

  观察上图,这一行数据是以冒号隔开的。其中的17021是指,创建密码的那天距离1970年1月1日的天数;0表示密码有效期最短0天;99999表示密码最多99999天有效;7表示有效期还剩7天时警告。

  然后,编辑脚本如下:

  1. vim /opt/shell/test6.sh
  1. #!/bin/bash
  2. #
  3. UN=$ #根据传入的数据作为变量UN的值
  4. C_D=`grep "^$UN" /etc/shadow | awk -F: '{print $3}' ` #grep "^$UN" /etc/shadow是从shadow中找到到以$UN这个用户名开头的,print $3是密码的创建时间,这里使用-F来指定:分隔符。加反引号是一个命令。
  5. M_D=`grep "^$UN" /etc/shadow | awk -F: '{print $5}' ` #'{print $5}'是找到最长有效期
  6. W_D=`grep "^$UN" /etc/shadow | awk -F: '{print $6}'` #'{print $6}'是警告时间
  7. NOW_D=$[`date +%s`/] #`date +%s`获得当前系统时间(秒),使用数学运算符[]相当于数>学的括号,除以一天的秒数
  8. U_D=$[$NOW_D-$C_D] #算出了使用的天数
  9. L_D=$[$M_D-$U_D] #算出剩余的天数
  10. if [ $L_D -le $W_D ];then #判断注意空格,剩余时间小于等于警告时间输出warn.
  11. echo "warn."
  12. else
  13. echo "left day is $L_D" #其他情况输出剩余天数
  14. fi

c) 执行脚本

  1. sh test6.sh root

4.4 文件与字符串测试

4.4.1 文件与字符串测试语法

a) 文件测试

  文件测试需要中括号 [ ]

  -e FILE:测试文件是否存在

  -f FILE:测试文件是否为普通文件

  -d FILE:测试文件是否为目录

  -r 当前用户有没有读的权限

  -w 当前用户有没有写的权限

  -x 当前用户有没有执行的权限

b) 字符串测试

  == 等号两端需要空格

  !=

  -n string : 判断字符串是否为空

  -s string : 判断字符串是否不空

4.4.2 案例(文件与字符串测试)

a) 业务描述

  指定一个用户名,判断此用户的用户名和它的基本组,组名是否相同。

b) 编写脚本 

  1. vim /opt/shell/test7.sh
  1. #!/bin/bash
  2. #
  3. if !id $ &>/dev/null ; then
  4. echo "No such user."
  5. exit
  6. fi
  7. if [ $ == `id -n -g $` ] ;then #`id -n -g $`是指输入的用户的组的名称(-g是组id)
  8. echo "相同"
  9. else
  10. echo "不相同"
  11. fi

c) 执行脚本

  1. sh test7.sh root

4.5 循环语句

4.5.1 for循环

a) for循环的语法结构

  1. for 变量 in 列表; do
  2.   语句
  3. done

b) for循环中的列表  

  for循环中的列表尤为重要,列表可以是以空格隔开的一组数,可以理解为数组;也可以动态生成列表。

  静态数组列表

  以下是个静态列表的实例:

  1. for I in ; do
  2. 语句
  3. done

  动态生成列表

  动态生成列表有三种方式,分别为:

  第一,{1..100}。中间两个点,1-10可以成{1..10}

  第二,seq [起始数] [跨度数] 结束数。seq相当于oracle中sequence,递增的序列,[起始数]带有中括号意为可以不给,默认为1;[跨度数]带有中括号也可以不给,默认值为1;结束数必须给,不然会造成死循环。

  第三,ls /PATH/ 文件列表。这个实际上是通过for循环递归目录下的文件。

c) for循环案例一

  业务描述:

  将那些可以登录的用户查询出来,并且将用户的帐号信息提取出来,后放入/tmp/test.txt文件中,并在行首给定行号。

  编写脚本:

  1. vim /opt/shell/test8.sh
  1. #!/bin/bash
  2. #
  3. J=
  4. count=`wc -l /etc/passwd | cut -d' ' -f1` #wc -l /etc/passwd 可以获得passwd文件的行数;cut -d' ' -f1中-d是指定切割符,-f1是取第一个。
  5. #count=`wc -l /etc/passwd | awk -F " " '{print $1}'` #也可以使用awk
  6. for I in `seq $count`;do #这里使用seq生成列表,以1开始,跨度为1,到$count结束。
  7. u_shell=`head -$I /etc/passwd | tail - | cut -d: -f7` #head取前I行,然后管道到tail -1拿到最后一行,实际上就是第I行,然后管道到cut按:切割,取第七个(shell)。
  8. u_n=`head -$I /etc/passwd | tail - | cut -d: -f1` #取用户
  9. if [ $u_shell == '/bin/bash' ];then #判断字符串是否相等,相等表示可以登录。可以登录>就将/tmp/users.txt
  10. echo "$J $u_n " >> /tmp/users.txt #输出时加上编号,这里不能使用I,因为I是行号,业务要求是新的序号,需要连续的。
  11. J=$[$J+] #相当于J++
  12. fi
  13. done

  执行脚本:

  1. sh test8.sh root

d) for循环案例二

  业务描述:

  计算100以内所有能被3整除的整数的和。

  编写脚本:

  1. vim test9.sh
  1. #!/bin/bash
  2. #
  3. sum=
  4. for I in {..};do
  5. S=$[$I%] #对3取模(求余数)
  6. if [ $S -eq ];then #数字比较只能用-eq,不能使用等号
  7. sum=$[$sum+$I]
  8. fi
  9. done
  10. echo "$sum"

  执行脚本:

  1. sh test9.sh

e) for循环案例三

  业务描述:

  传给脚本一个参数:目录,输出该目录中文件最大的,文件名和文件大小。

  编写脚本:

  1. vim test10.sh
  1. #!/bin/bash
  2. #
  3. #首先判断参数是否正确
  4. if [ -f $ ];then #-f 判断传过来的目录是否是文件
  5. echo "Arg is error."
  6. exit
  7. fi
  8.  
  9. if [ -d $ ];then #-d 是判断$1是目录
  10. c=`du -a $ | sort -nr | wc -l` #du -a $1是列出$1目录下的所有文件和目录; sort -nr是按数值降序排列; wc -l获取行数。但是这样把目录页选出来了
  11. for I in `seq $c`;do
  12. f_size=`du -a $ | sort -nr | head -$I | tail - | awk '{print $1}'` #取到第I行,拿到文件大小
  13. f_path=`du -a $ | sort -nr | head -$I | tail - | awk '{print $2}'` #取到第I行,拿到文件路径
  14. if [ -f $f_path ];then #判$f_path是否是文件,是文件就直接输出,并退出循环
  15. echo -e "the biggest file is $f_path \t $f_size"
  16. break
  17. fi
  18. done
  19. fi

  执行脚本:

  1. sh test10.sh /path/

4.5.2 while循环

a) 格式一

  1. while 条件;do
  2. 语句
  3. [break]
  4. done

b) 格式二(死循环)

  1. while true
  2. do
  3.   语句
  4. done 

c) 格式三(死循环)

  1. while :
  2. do
  3. 语句
  4. done

d) 格式四(死循环)

  1. while [ ]
  2. do
  3. 语句
  4. done 

e) 格式五(死循环)

  1. while [ ]
  2. do
  3. 语句
  4. done

f) while循环案例 

  业务描述:

  使用echo输出10个随机数。

  编写脚本:

  1. vim /opt/shell/test11.sh
  1. #!/bin/bash
  2. #
  3. I=
  4. while [ $I -le ] ; do
  5. echo -n "$RANDOM"
  6. I=$[$I+]
  7. done

  执行脚本:

  1. sh test11.sh

快速掌握Shell编程的更多相关文章

  1. linux shell 基本语法之快速上手shell编程

    从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁.用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操 ...

  2. Shell 编程快速上手

    Shell 编程快速上手 test.sh #!/bin/sh cd ~ mkdir shell_tut cd shell_tut for ((i=0; i<10; i++)); do touch ...

  3. 30分钟快速学习Shell脚本编程

    什么是Shell脚本 示例 看个例子吧: #!/bin/sh cd ~ mkdir shell_tut cd shell_tut for ((i=0; i<10; i++)); do touch ...

  4. shell编程快速入门及实战

    shell编程:对于hadoop程序员,通常需要熟悉shell编程,因为shell可以非常方便的运行程序代码. 1.shell文件格式:xxx.sh #!/bin/sh ---shell文件第一行必须 ...

  5. ****CodeIgniter使用cli模式运行,把php作为shell编程

    shell简介 在计算机科学中,Shell俗称壳(用来区别于核).而我们常说的shell简单理解就是一个命令行界面,它使得用户能与操作系统的内核进行交互操作. 常见的shell环境有:MS-DOS.B ...

  6. [转]Windows Shell 编程 第二章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987893】

    第二章Shell的结构  “Shell 编程”的大伞之下有大量的API函数和COM接口.这个种类繁多的‘命令’集允许你用不同的方法对Windows Shell进行编程.函数和接口并不是两种提供相同功能 ...

  7. Shell编程笔记

    Shell编程笔记与Windows下熟悉的批处理类似,也可以将一些重复性的命令操作写成一个脚本方便处理.   修改别人的脚本,运行后遇到个问题 setenv: command not found 查证 ...

  8. linux —— shell 编程(整体框架与基础笔记)

    导读 关于shell编程基础的学习,网上有很多资源,如果在校图书馆应该也有一些教程,所以这里对于零碎的基础不做详细记录,而只是对一些常用的概念.命令与操作做一个简要的记录,以备方便查找. (本文所有语 ...

  9. Linux 与 unix shell编程指南——学习笔记

    第一章    文件安全与权限 文件访问方式:读,写,执行.     针对用户:文件属主,同组用户,其它用户.     文件权限位最前面的字符代表文件类型,常用的如         d 目录:l 符号链 ...

随机推荐

  1. 一个HTTP Basic Authentication引发的异常

    这几天在做一个功能,其实很简单.就是调用几个外部的API,返回数据后进行组装然后成为新的接口.其中一个API是一个很奇葩的API,虽然是基于HTTP的,但既没有基于SOAP规范,也不是Restful风 ...

  2. CU社区shell板块awk十三问整理

    CU社区shell板块awk十三问整理 一.RS="" 当 RS="" 时,会将\n强制加入到FS变量中,因为RS为空时,是将连续多空行作为分隔符,近似于\n\ ...

  3. awk说明书(转)

    ref:http://blog.chinaunix.net/uid-429659-id-122573.html awk使用手册 作者:awk使用手册什么是awk? 你可能对UNIX比较熟悉,但你可能对 ...

  4. 理解javascript模块化(转)

    模块化是一个通用的编程最佳实践.程序的模块化使我们可以更方便地使用别人的代码,想要什么功能,就加载什么模块,从而提高代码的利用效率,增加开发速度. 模块就像积木,有了它,我们可以搭出各种各种功能样式的 ...

  5. 浅谈ES6

    ECMAScript6.0(简称ES6)是javaScript语言的下一代标准,已经在2015年6月正式发布了.它的目标,使得javaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言 ...

  6. jquery中ajax跨域提交的时候会有2次请求

    我们平时在同域中请求页面什么的时候不会有这种情况,这种情况大多发生在移动端的跨域请求中发生的. 解决方法就是在服务端中加一层过滤HTTP请求的类型,把OPTION等不用的类型过滤掉.就是当请求为非 H ...

  7. 简单的.editconfig文件

    root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_n ...

  8. Yii框架中使用mongodb扩展

    前提条件:安装了mongodb数据库 安装了mongo的php驱动 下载Yii的mongo扩展:这是YiiMongoDbSuite的1.3.6版本支持PHP Mongo驱动的版本为1.0.5及以下 下 ...

  9. ABP官方文档翻译 8.1 通知系统

    通知系统 介绍 发送模型 通知类型 通知数据 通知严重性 关于通知持久化 订阅通知 发布通知 用户通知管理 实时通知 客户端 通知存储 通知定义 介绍 在系统中通知用来基于特定的事件告知用户.ABP提 ...

  10. ABP官方文档翻译 3.1 实体

    实体 实体类 聚合根类 领域事件 常规接口 审计 软删除 激活/失活实体 实体改变事件 IEntity接口 实体是DDD(领域驱动设计)的核心概念之一.Eric Evans描述它为"An o ...