快速掌握Shell编程
(一)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) 编写脚本
首先,创建脚本。
- mkdir /opt/shell
- cd /opt/shell
- vim test1.sh
其次,编写脚本主体。
- #!/bin/bash#脚本的第一行一定是这个脚本的声明,这里声明的是脚本的解释器。
- # 用户可以有参数传过来,也可以直接定义一个变量。
- U=’user1’ #声明变量,本地变量的声明直接变量名=值,无需export。这里选择单引号,因为user1是个字
- 符串,只需强引就行,双引号也行。
- useradd $U #引用变量
- #设置密码和用户名相同,但是设置完之后不显示passwd的执行结果。但是这个passwd会在控制台出现信息
- ,所以需要使用管道传入数据,并且重定向。
- echo "$U" | passwd --stdin $U &>/dev/null #前一个命令的输出传给后一个命令的输入。
- echo "success." #打印成功信息。
以上是添加1个给定名称的用户,可以稍作修改,变为参数传入的方式动态添加用户:
- cp test1.sh test2.sh
- vim test2.sh
- #!/bin/bash
- #
- useradd $ #这里是使用传递参数的方式,1代表1个参数,实际上$1是特殊变量$*的特例
- echo "$1" | passwd --stdin $ &>/dev/null
- 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) 编写脚本
首先,创建脚本。
- vim /opt/shell/test3.sh
其次,编写脚本主体。
- #!/bin/bash
- U=$* #使用一个变量存储用户名
- userdel $* #删除这个用户
- rm -rf /home/$* #删除该用户的家目录
- echo "delete user and $*home success." #打印成功信息
c) 执行脚本
- sh test3.sh username #注意一定要有参数(要删除的用户),不然会删除整个/home目录
- cat /etc/passwd #查看用户是否删除成功。
- 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) 案例(命令逻辑关系的条件判断)
业务描述:添加用户前先判断是否存在,如果存在就打印该用户已存在,并且退出。
编写脚本:
- vim /opt/shell/test4.sh
- #!/bin/bash
- #
- id $ &>/dev/null && echo "User $1 exist" && exit #使用命令的执行状态作为逻辑判断。id $ 如果用户$1存在,则为0(真),需要继续判断后面的,所以执行echo,并且退出脚本,不再
- 创建该用户。退出,3是非0,当前脚本执行不成功。
#上一行可以使用逻辑或的代替,见下两行
#!id $1 &>/dev/null || echo "User $1 exist" #如果用户存在id $1则为0,前面加了!就是非0,就为假。前面为假,对于逻辑或来说需要继续判断后面的,所以执行echo 打印用户存在。
#id $1 &>/dev/null && exit 3 #用户存在就退出- useradd $ #添加用户
- id $ &>/dev/null && echo "$1" | passwd --stdin $ &>/dev/null #如果用户创建成功了>,就执行后面的echo passwd添加密码。
- echo "Add user $1 success."
执行脚本:
- sh test4.sh user1
4.2.4 if条件判断
a) 语法结构
if条件表达式的语法结构为:
- if [ 条件 ]; then
- 语句
- elif [ 条件 ]; then
- 语句
- else
- 语句
- fi
b) if的逻辑与或
-a :并且
-o :或者
例如:
- if [ $# -gt -a $# -lt -o $# -eq ] ; then
如果,上一个命令传入的参数个数大于1,并且小于3,或者等于2。&&是离散数学中的逻辑与,-a是一个逻辑运算符,并且的意思,不同于&&。
c) 案例(if语句)
业务描述:给定一个用户,如果他的UID为0则显示为管理员,否则显示其他普通用户。
编写脚本:
- vim /opt/shell/test5.sh
- #!/bin/bash
- USER_ID=`id -u $`&> /dev/null || exit #注意这里需要用反引号,这里也不能将变量名取为UID,会和环境变量冲突,这样声明的变量是只读的。
- if [ $USER_ID -eq ] ; then
- echo "admin."
- else
- echo "other."
- fi
执行脚本:
- sh test5.sh root
4.3 算术运算符
4.3.1 算术运算符的类型
a) let 算术运算表达式
- let C=$A + $B
b) $[算术表达式]
- C = $[$A+$B]
c) $((算术表达式))
- C=$(($A+$B))
d) expr 算术表达式
注意:表达式中各操作数及运算符之间要有空格。而且要使用命令引用。
- C=`expr $A + $B`
4.3.2 案例(算术运算符的使用)
a) 业务描述
给定一个用户,获取其密码警告期限,然后判断用户密码使用期限是否已经小于警告期限,如果小于,则是显示“WARN”,否则显示密码还有多少天到期。
密码警告期限:距离警告的天数;密码使用期限:密码有效期减去使用的时间。
b) 编写脚本
首先,需要知道:
$date +%s 可以获得今天的秒数
$cat /etc/shadow 可以获得密码使用时间。
- cat /etc/shadow | grep root
观察上图,这一行数据是以冒号隔开的。其中的17021是指,创建密码的那天距离1970年1月1日的天数;0表示密码有效期最短0天;99999表示密码最多99999天有效;7表示有效期还剩7天时警告。
然后,编辑脚本如下:
- vim /opt/shell/test6.sh
- #!/bin/bash
- #
- UN=$ #根据传入的数据作为变量UN的值
- C_D=`grep "^$UN" /etc/shadow | awk -F: '{print $3}' ` #grep "^$UN" /etc/shadow是从shadow中找到到以$UN这个用户名开头的,print $3是密码的创建时间,这里使用-F来指定:分隔符。加反引号是一个命令。
- M_D=`grep "^$UN" /etc/shadow | awk -F: '{print $5}' ` #'{print $5}'是找到最长有效期
- W_D=`grep "^$UN" /etc/shadow | awk -F: '{print $6}'` #'{print $6}'是警告时间
- NOW_D=$[`date +%s`/] #`date +%s`获得当前系统时间(秒),使用数学运算符[]相当于数>学的括号,除以一天的秒数
- U_D=$[$NOW_D-$C_D] #算出了使用的天数
- L_D=$[$M_D-$U_D] #算出剩余的天数
- if [ $L_D -le $W_D ];then #判断注意空格,剩余时间小于等于警告时间输出warn.
- echo "warn."
- else
- echo "left day is $L_D" #其他情况输出剩余天数
- fi
c) 执行脚本
- 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) 编写脚本
- vim /opt/shell/test7.sh
- #!/bin/bash
- #
- if !id $ &>/dev/null ; then
- echo "No such user."
- exit
- fi
- if [ $ == `id -n -g $` ] ;then #`id -n -g $`是指输入的用户的组的名称(-g是组id)
- echo "相同"
- else
- echo "不相同"
- fi
c) 执行脚本
- sh test7.sh root
4.5 循环语句
4.5.1 for循环
a) for循环的语法结构
- for 变量 in 列表; do
- 语句
- done
b) for循环中的列表
for循环中的列表尤为重要,列表可以是以空格隔开的一组数,可以理解为数组;也可以动态生成列表。
静态数组列表
以下是个静态列表的实例:
- for I in ; do
- 语句
- done
动态生成列表
动态生成列表有三种方式,分别为:
第一,{1..100}。中间两个点,1-10可以成{1..10}
第二,seq [起始数] [跨度数] 结束数。seq相当于oracle中sequence,递增的序列,[起始数]带有中括号意为可以不给,默认为1;[跨度数]带有中括号也可以不给,默认值为1;结束数必须给,不然会造成死循环。
第三,ls /PATH/ 文件列表。这个实际上是通过for循环递归目录下的文件。
c) for循环案例一
业务描述:
将那些可以登录的用户查询出来,并且将用户的帐号信息提取出来,后放入/tmp/test.txt文件中,并在行首给定行号。
编写脚本:
- vim /opt/shell/test8.sh
- #!/bin/bash
- #
- J=
- count=`wc -l /etc/passwd | cut -d' ' -f1` #wc -l /etc/passwd 可以获得passwd文件的行数;cut -d' ' -f1中-d是指定切割符,-f1是取第一个。
- #count=`wc -l /etc/passwd | awk -F " " '{print $1}'` #也可以使用awk
- for I in `seq $count`;do #这里使用seq生成列表,以1开始,跨度为1,到$count结束。
- u_shell=`head -$I /etc/passwd | tail - | cut -d: -f7` #head取前I行,然后管道到tail -1拿到最后一行,实际上就是第I行,然后管道到cut按:切割,取第七个(shell)。
- u_n=`head -$I /etc/passwd | tail - | cut -d: -f1` #取用户
- if [ $u_shell == '/bin/bash' ];then #判断字符串是否相等,相等表示可以登录。可以登录>就将/tmp/users.txt
- echo "$J $u_n " >> /tmp/users.txt #输出时加上编号,这里不能使用I,因为I是行号,业务要求是新的序号,需要连续的。
- J=$[$J+] #相当于J++
- fi
- done
执行脚本:
- sh test8.sh root
d) for循环案例二
业务描述:
计算100以内所有能被3整除的整数的和。
编写脚本:
- vim test9.sh
- #!/bin/bash
- #
- sum=
- for I in {..};do
- S=$[$I%] #对3取模(求余数)
- if [ $S -eq ];then #数字比较只能用-eq,不能使用等号
- sum=$[$sum+$I]
- fi
- done
- echo "$sum"
执行脚本:
- sh test9.sh
e) for循环案例三
业务描述:
传给脚本一个参数:目录,输出该目录中文件最大的,文件名和文件大小。
编写脚本:
- vim test10.sh
- #!/bin/bash
- #
- #首先判断参数是否正确
- if [ -f $ ];then #-f 判断传过来的目录是否是文件
- echo "Arg is error."
- exit
- fi
- if [ -d $ ];then #-d 是判断$1是目录
- c=`du -a $ | sort -nr | wc -l` #du -a $1是列出$1目录下的所有文件和目录; sort -nr是按数值降序排列; wc -l获取行数。但是这样把目录页选出来了
- for I in `seq $c`;do
- f_size=`du -a $ | sort -nr | head -$I | tail - | awk '{print $1}'` #取到第I行,拿到文件大小
- f_path=`du -a $ | sort -nr | head -$I | tail - | awk '{print $2}'` #取到第I行,拿到文件路径
- if [ -f $f_path ];then #判$f_path是否是文件,是文件就直接输出,并退出循环
- echo -e "the biggest file is $f_path \t $f_size"
- break
- fi
- done
- fi
执行脚本:
- sh test10.sh /path/
4.5.2 while循环
a) 格式一
- while 条件;do
- 语句
- [break]
- done
b) 格式二(死循环)
- while true
- do
- 语句
- done
c) 格式三(死循环)
- while :
- do
- 语句
- done
d) 格式四(死循环)
- while [ ]
- do
- 语句
- done
e) 格式五(死循环)
- while [ ]
- do
- 语句
- done
f) while循环案例
业务描述:
使用echo输出10个随机数。
编写脚本:
- vim /opt/shell/test11.sh
- #!/bin/bash
- #
- I=
- while [ $I -le ] ; do
- echo -n "$RANDOM"
- I=$[$I+]
- done
执行脚本:
- sh test11.sh
快速掌握Shell编程的更多相关文章
- linux shell 基本语法之快速上手shell编程
从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁.用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操 ...
- Shell 编程快速上手
Shell 编程快速上手 test.sh #!/bin/sh cd ~ mkdir shell_tut cd shell_tut for ((i=0; i<10; i++)); do touch ...
- 30分钟快速学习Shell脚本编程
什么是Shell脚本 示例 看个例子吧: #!/bin/sh cd ~ mkdir shell_tut cd shell_tut for ((i=0; i<10; i++)); do touch ...
- shell编程快速入门及实战
shell编程:对于hadoop程序员,通常需要熟悉shell编程,因为shell可以非常方便的运行程序代码. 1.shell文件格式:xxx.sh #!/bin/sh ---shell文件第一行必须 ...
- ****CodeIgniter使用cli模式运行,把php作为shell编程
shell简介 在计算机科学中,Shell俗称壳(用来区别于核).而我们常说的shell简单理解就是一个命令行界面,它使得用户能与操作系统的内核进行交互操作. 常见的shell环境有:MS-DOS.B ...
- [转]Windows Shell 编程 第二章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987893】
第二章Shell的结构 “Shell 编程”的大伞之下有大量的API函数和COM接口.这个种类繁多的‘命令’集允许你用不同的方法对Windows Shell进行编程.函数和接口并不是两种提供相同功能 ...
- Shell编程笔记
Shell编程笔记与Windows下熟悉的批处理类似,也可以将一些重复性的命令操作写成一个脚本方便处理. 修改别人的脚本,运行后遇到个问题 setenv: command not found 查证 ...
- linux —— shell 编程(整体框架与基础笔记)
导读 关于shell编程基础的学习,网上有很多资源,如果在校图书馆应该也有一些教程,所以这里对于零碎的基础不做详细记录,而只是对一些常用的概念.命令与操作做一个简要的记录,以备方便查找. (本文所有语 ...
- Linux 与 unix shell编程指南——学习笔记
第一章 文件安全与权限 文件访问方式:读,写,执行. 针对用户:文件属主,同组用户,其它用户. 文件权限位最前面的字符代表文件类型,常用的如 d 目录:l 符号链 ...
随机推荐
- 一个HTTP Basic Authentication引发的异常
这几天在做一个功能,其实很简单.就是调用几个外部的API,返回数据后进行组装然后成为新的接口.其中一个API是一个很奇葩的API,虽然是基于HTTP的,但既没有基于SOAP规范,也不是Restful风 ...
- CU社区shell板块awk十三问整理
CU社区shell板块awk十三问整理 一.RS="" 当 RS="" 时,会将\n强制加入到FS变量中,因为RS为空时,是将连续多空行作为分隔符,近似于\n\ ...
- awk说明书(转)
ref:http://blog.chinaunix.net/uid-429659-id-122573.html awk使用手册 作者:awk使用手册什么是awk? 你可能对UNIX比较熟悉,但你可能对 ...
- 理解javascript模块化(转)
模块化是一个通用的编程最佳实践.程序的模块化使我们可以更方便地使用别人的代码,想要什么功能,就加载什么模块,从而提高代码的利用效率,增加开发速度. 模块就像积木,有了它,我们可以搭出各种各种功能样式的 ...
- 浅谈ES6
ECMAScript6.0(简称ES6)是javaScript语言的下一代标准,已经在2015年6月正式发布了.它的目标,使得javaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言 ...
- jquery中ajax跨域提交的时候会有2次请求
我们平时在同域中请求页面什么的时候不会有这种情况,这种情况大多发生在移动端的跨域请求中发生的. 解决方法就是在服务端中加一层过滤HTTP请求的类型,把OPTION等不用的类型过滤掉.就是当请求为非 H ...
- 简单的.editconfig文件
root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_n ...
- Yii框架中使用mongodb扩展
前提条件:安装了mongodb数据库 安装了mongo的php驱动 下载Yii的mongo扩展:这是YiiMongoDbSuite的1.3.6版本支持PHP Mongo驱动的版本为1.0.5及以下 下 ...
- ABP官方文档翻译 8.1 通知系统
通知系统 介绍 发送模型 通知类型 通知数据 通知严重性 关于通知持久化 订阅通知 发布通知 用户通知管理 实时通知 客户端 通知存储 通知定义 介绍 在系统中通知用来基于特定的事件告知用户.ABP提 ...
- ABP官方文档翻译 3.1 实体
实体 实体类 聚合根类 领域事件 常规接口 审计 软删除 激活/失活实体 实体改变事件 IEntity接口 实体是DDD(领域驱动设计)的核心概念之一.Eric Evans描述它为"An o ...