Shell简介

概述

Shell是一种具备特殊功能的程序,它提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令,并把它送入内核去执行。内核是Linux系统的心脏,从开机自检就驻留在计算机的内存中,直到计算机关闭为止,而用户的应用程序存储在计算机的硬盘上,仅当需要时才被调入内存。Shell是一种应用程序,当用户登录Linux系统时,Shell就会被调入内存去执行。Shell独立于内核,它是连接内核和应用程序的桥梁,并由输入设备读取命令,再将其转为计算机可以理解的机械码,Linux内核才能执行该命令。

优势

Shell脚本语言的好处是简单、易学、易用,适合处理文件和目录之类的对象,以简单的方式快速完成某些复杂的事情通常是创建脚本的重要原则,脚本语言的特性可以总结为以下几个方面:

  1. 语法和结构通常比较简单。
  2. 学习和使用通常比较简单,
  3. 通常以容易修改程序的“解释”作为运行方式,而不需要“编译。
  4. 程序的开发产能优于运行效能。

Shell脚本语言是Linux/Unix系统上一种重要的脚本语言,在Linux/Unix领域应用极为广泛,熟练掌握Shell脚本语言是一个优秀的Linux/Unix开发者和系统管理员必经之路。利用Shell脚本语言可以简洁地实现复杂的操作,而且Shell脚本程序往往可以在不同版本的Linux/Unix系统上通用。

Shell编程

基本格式

Shell脚本的文件名后缀通常是.sh (当然你也可以使用其他后缀或者没有后缀,.sh是为了规范)

程序编写格式:

  1. #!/bin/bash
  2. #  注释使用#号

代码示例:

  1. //使用vi编辑器编写shell脚本(a.sh不存在则会新建)
  2. vi a.sh

进入vi编辑模式后编写执行代码

  1. //固定格式,记住就可以了
  2. #!/bin/bash
  3. //执行的代码
  4. echo Hello World

赋予权限并执行:

  1. //赋予可执行权限
  2. chmod +x a.sh
  3. //执行(调用/bin/bash执行a.sh脚本)
  4. ./a.sh

执行结果:

下面是几种运行情况:

  1. a.sh

这样的话需要保证脚本具有执行权限并且在环境变量PATH中有(.),这样在执行的时候会先从当前目录查找。

  1. ./a.sh

只要保证这个脚本具有执行权限即可

  1. /usr/local/a.sh

只要保证这个脚本具有执行权限即可

  1. bash a.sh

直接可以执行,甚至这个脚本文件中的第一行都可以不引入/bin/bash,它是将hello.sh作为参数传给bash命令来执行的。

  1. bash -x /path/to/aa.sh

bash的单步执行

  1. bash -n /path/to/aa.sh

bash语法检查

变量

变量不需要声明,初始化不需要指定类型

变量命名

1、只能使用数字,字母和下划线,且不能以数字开头

2、变量名区分大小写

3、建议命令要通俗易懂

注意:变量赋值是通过等号(=)进行赋值,在变量、等号和值之间不能出现空格。

显示变量值使用echo命令(类似于java中的system.out) ,加上$变量名,也可以使用${变量名}

例如:

  1. echo $JAVA_HOME
  2. echo ${JAVA_HOME}

变量的申明和使用:

变量分类:

Shell变量有这几类:本地变量、环境变量、局部变量、位置变量、特殊变量。

本地变量:

  1. 只对当前shell进程有效的,对当前进程的子进程和其它shell进程无效。
  2. 定义:VAR_NAME=VALUE
  3. 变量引用:${VAR_NAME} 或者 $VAR_NAME
  4. 取消变量:unset VAR_NAME
  5. 相当于java中的私有变量(private),只能当前类使用,子类和其他类都无法使用。

比如在一个bash命令窗口下再使用bash,则变成了子进程,本地变量不会被这个子进程所访问。

环境变量:

自定义的环境变量对当前shell进程及其子shell进程有效,对其它的shell进程无效

定义:export VAR_NAME=VALUE

对所有shell进程都有效需要配置到配置文件中

  1. vi /etc/profile
  2. source /etc/profile

相当于java中的protected修饰符,对当前类,子孙类,以及同一个包下面可以共用。

和windows中的环境变量比较类似

自定义的环境变量:

局部变量:

  1. 在函数中调用,函数执行结束,变量就会消失
  2. 对shell脚本中某代码片段有效
  3. 定义:local VAR_NAME=VALUE
  4. 相当于java代码中某一个方法中定义的局部变量,只对这个方法有效。

位置变量:

比如脚本中的参数:

$0:脚本自身

$1:脚本的第一个参数

$2:脚本的第二个参数

相当于java中main函数中的args参数,可以获取外部参数。

编写脚本:

执行示例:

特殊变量:

$?:接收上一条命令的返回状态码

返回状态码在0-255之间

$#:参数个数

$*:或者$@:所有的参数

$$:获取当前shell的进程号(PID)(可以实现脚本自杀)(或者使用exit命令直接退出也可以使用exit [num])

引号

Shell编程中有三类引号:单引号、双引号、反引号。

''单引号不解析变量

  1. echo '$name'

""双引号会解析变量

  1. echo "$name"

``反引号是执行并引用一个命令的执行结果,类似于$(...)

  1. echo `$name`

示例:

循环

for循环

通过使用一个变量去遍历给定列表中的每个元素,在每次变量赋值时执行一次循环体,直至赋值完成所有元素退出循环


格式1

  1. for ((i=0;i<10;i++))
  2. do
  3. ...
  4. Done

格式2

  1. for i in 0 1 2 3 4 5 6 7 8 9
  2. do
  3. ...
  4. Done

格式3

  1. for i in {0..9}
  2. do
  3. ...
  4. done

注意:for i in {0..9} 等于for i in {0..9..1} , 第三个参数为跨步。

例如:

{0..9..2} 表示 0,2,4,6,8

while循环

适用于循环次数未知,或不便用for直接生成较大的列表时

 格式:

  1. while 测试条件
  2. do
  3. 循环体
  4. done

如果测试条件为“真”,则进入循环,测试条件为假,则退出循环。

打印结果为0~9.

循环控制

循环控制命令——break

break命令是在处理过程中跳出循环的一种简单方法,可以使用break命令退出任何类型的循环,包括while循环和for循环

循环控制命令——continue

continue命令是一种提前停止循环内命令,而不完全终止循环的方法,这就需要在循环内设置shell不执行命令的条件

条件

bash条件测试

格式:

  1. test EXPR
  2. [ EXPR ]:注意中括号和表达式之间的空格

     整型测试:

-gt:大于:

-lt:小于

-ge:大于等于

-le:小于等于

-eq:等于

-ne:不等于

例如[ $num1 -gt $num2 ]或者test $num1 -gt $num2

字符串测试:

=:等于,例如判断变量是否为空 [ "$str" =  "" ] 或者[ -z $str ]

!=:不等于

示例:

打印结果:

判断

if判断:

单分支

  1. if 测试条件;then
  2. 选择分支
  3. fi

双分支

  1. if 测试条件
  2. then
  3. 选择分支1
  4. else
  5. 选择分支2
  6. fi

多分支

  1. if 条件1; then
  2. 分支1
  3. elif 条件2; then
  4. 分支2
  5. elif 条件3; then
  6. 分支3
  7. ...
  8. else
  9. 分支n
  10. i

双分支示例:

Case判断

有多个测试条件时,case语句会使得语法结构更清晰

格式:

  1. case 变量引用 in
  2. PATTERN1)
  3. 分支1
  4. ;;
  5. PATTERN2)
  6. 分支2
  7. ;;
  8. ...
  9. *)
  10. 分支n
  11. ;;
  12. esac

PATTERN :类同于文件名通配机制,但支持使用|表示或者

a|b:a或者b

*:匹配任意长度的任意字符

?:匹配任意单个字符

[a-z]:指定范围内的任意单个字符

示例:

算术运算

  1. let varName=算术表达式
  2. varName=$[算术表达式]
  3. varName=$((算术表达式))
  4. varName=`expr $num1 + $num2`

使用这种格式要注意两个数字和+号中间要有空格。

示例:

逻辑运算符

if [ 条件A && 条件B ] 在shell中怎么写?

if [ 条件A && 条件B ];then    是不对的

解决方法:

(1)需要用到shell中的逻辑操作符

-a 与
-o 或
! 非

如if [ 条件A -a 条件B ]

(2)if [ 条件A ] && [条件B ]

(3)if((A&&B))

(4)if [[ A&&B ]]

自定义函数

格式:

  1. function 函数名(){
  2. ...
  3. }
  1. 引用自定义函数文件时,使用source  func.sh
  2. 有利于代码的重用性
  3. 函数传递参数(可以使用类似于Java中的args,args[1]代表Shell中的$1)
  4. 函数的返回值,只能是数字

函数的调用:

read

read命令接收标准输入(键盘)的输入,或者其他文件描述符的输入。得到输入后,read命令将数据放入一个标准变量中。

格式

  1. read VAR_NAME

read如果后面不指定变量,那么read命令会将接收到的数据放置在环境变量REPLY中

  1. #表示输入时的提示字符串:
  2. read -p "Enter your name:" VAR_NAME

  1. # -t表示输入等待的时间
  2. read -t 5 -p "enter your name:" VAR_NAME
  1. # -s 表示安全输入,键入密码时不会显示
  2. read  -s  -p "Enter your password: " pass

declare

用来限定变量的属性

-r 只读

-i 整数:某些算术计算允许在被声明为整数的变量中完成,而不需要特别使用expr或let来完成。

-a 数组

示例:

只读

整数

数组

字符串操作

获取长度:

  1. ${#VAR_NAME}

字符串截取

  1. ${variable:offset:length}或者${variable:offset}

取尾部的指定个数的字符

  1. ${variable: -length}:注意冒号后面有空格

大小写转换

小-->大:

  1. ${variable^^}

大-->小:

  1. ${variable,,}

示例:

数组

定义:declare -a:表示定义普通数组

特点

  1. 支持稀疏格式
  2. 仅支持一维数组

数组赋值方式

  1. 一次对一个元素赋值a[0]=$RANDOM
  2. 一次对多个元素赋值a=(a b c d)

按索引进行赋值a=([0]=a [3]=b [1]=c)

使用read命令read -a ARRAY_NAME查看元素

  1. ${ARRAY[index]}:查看数组指定角标的元素
  2. ${ARRAY}:查看数组的第一个元素
  3. ${ARRAY[*]}或者${ARRAY[@]}:查看数组的所有元素

获取数组的长度

  1. ${#ARRAY[*]}
  2. ${#ARRAY[@]}

获取数组内元素的长度

  1. ${#ARRAY[0]}

注意:${#ARRAY[0]}表示获取数组中的第一个元素的长度,等于${#ARRAY}

从数组中获取某一片段之内的元素(操作类似于字符串操作)

格式:

  1. ${ARRAY[@]:offset:length}
  1. offset:偏移的元素个数
  2. length:取出的元素的个数
  3. ${ARRAY[@]:offset:length}:取出偏移量后的指定个数的元素
  4. ${ARRAY[@]:offset}:取出数组中偏移量后的所有元素

数组删除元素:

  1. unset ARRAY[index]

示例:

其他命令

date

显示当前时间

  1. 格式化输出 +%Y-%m-%d
  2. 格式%s表示自1970-01-01 00:00:00以来的秒数
  3. 指定时间输出  --date='2009-01-01 11:11:11'
  4. 指定时间输出  --date='3 days ago' (3天之前,3天之后可以用-3)

示例:

后台运行脚本

在脚本后面加一个&

  1. test.sh &

这样的话虽然可以在后台运行,但是当用户注销(logout)或者网络断开时,终端会收到Linux HUP信号(hangup)信号从而关闭其所有子进程

nohup命令

不挂断的运行命令,忽略所有挂断(hangup)信号

  1. nohup test.sh &
  1. nohup会忽略进程的hangup挂断信号,所以关闭当前会话窗口不会停止这个进程的执行。
  2. nohup会在当前执行的目录生成一个nohup.out日志文件

标准输入、输出、错误、重定向

标准输入、输出、错误可以使用文件描述符0、1、2引用

使用重定向可以把信息重定向到其他位置

  1. ls >file 或者 ls 1>file(ls >>file)
  2. lk 2>file(lk是一个错误命令)
  3. ls >file 2>&1
  4. ls > /dev/null(把输出信息重定向到无底洞)

例子:

  1. command  >/dev/null 2>&1

Crontab定时器

  1. linux下的定时任务
  2. 编辑使用crontab  -e
  3. 一共6列,分别是:分 时 日 月 周 命令

示例:(表示每隔分钟执行一次 bash /opt/date.sh命令)

查看使用crontab -l

删除任务crontab -r

查看crontab执行日志

  1. tail -f /var/log/cron

必须打开rsyslog服务cron文件中才会有执行日志(service rsyslog status)

  1. tail -f /var/spool/mail/root(查看crontab最近的执行情况)

查看cron服务状态

  1. service crond status

启动cron服务

  1. service crond start

小结及示例:

基本格式 :

*  *  *  *  *  command

分 时 日 月 周 命令

第1列表示分钟1~59 每分钟用*或者 */1表示

第2列表示小时1~23(0表示0点)

第3列表示日期1~31

第4列表示月份1~12

第5列标识号星期0~6(0表示星期天)

第6列要运行的命令

crontab文件的一些例子:

30 21 * * * /usr/local/etc/rc.d/lighttpd restart

上面的例子表示每晚的21:30重启apache。

45 4 1,10,22 * * /usr/local/etc/rc.d/lighttpd restart

上面的例子表示每月1、10、22日的4 : 45重启apache。

10 1 * * 6,0 /usr/local/etc/rc.d/lighttpd restart

上面的例子表示每周六、周日的1 : 10重启apache。

0,30 18-23 * * * /usr/local/etc/rc.d/lighttpd restart

上面的例子表示在每天18 : 00至23 : 00之间每隔30分钟重启apache。

0 23 * * 6 /usr/local/etc/rc.d/lighttpd restart

上面的例子表示每星期六的11 : 00 pm重启apache。

* */1 * * * /usr/local/etc/rc.d/lighttpd restart

每一小时重启apache

* 23-7/1 * * * /usr/local/etc/rc.d/lighttpd restart

晚上11点到早上7点之间,每隔一小时重启apache

0 11 4 * mon-wed /usr/local/etc/rc.d/lighttpd restart

每月的4号与每周一到周三的11点重启apache

0 4 1 jan * /usr/local/etc/rc.d/lighttpd restart

一月一号的4点重启apache

ps和jps

  1. ps:用来显示进程的相关信息
  2. ps显示当前shell启动的所有进程
  3. ps -e显示系统中所有进程
  4. ps -ef|grep java
  5. jps:类似linux的ps命令,不同的是ps是用来显示所有进程,而jps只显示java进程,准确的说是显示当前用户已启动的部分java进程信息,信息包括进程号和简短的进程command。

问题:某个java进程已经启动,用jps却显示不了该进程进程号,使用ps -ef|grep java却可以看到?

java程序启动后,默认(请注意是默认)会在/tmp/hsperfdata_userName目录下以该进程的id为文件名新建文件,并在该文件中存储jvm运行的相关信息,其中的userName为当前的用户名,/tmp/hsperfdata_userName目录会存放该用户所有已经启动的java进程信息。而jps、jconsole、jvisualvm等工具的数据来源就是这个文件(/tmp/hsperfdata_userName/pid)。所以当该文件不存在或是无法读取时就会出现jps无法查看该进程号。

原因:1,磁盘读写、目录权限问题。2,临时文件丢失,被删除或是定期清理。3,java进程信息文件存储地址被设置,不在/tmp目录下

登录Shell和交互shell

交互式的:顾名思义,这种shell中的命令时由用户从键盘交互式地输入的,运行的结果也能够输出到终端显示给用户看。

非交互式的:这种shell可能由某些自动化过程启动,不能直接从请求用户的输入,也不能直接输出结果给终端用户看。输出最好写到文件。比如使用Shell脚本。

登录式:意思是这种是在某用户由/bin/login登陆进系统后启动的shell,跟这个用户绑定。这个shell是用户登陆后启动的第一个进程。login进程在启动shell时传递第0个参数指明shell的名字,该参数第一个字符为"-",指明这是一个login
shell。比如对bash而言,启动参数为"-bash"。

非登录式:不需login而由某些程序启动的shell。传递给shell的参数,是没有'-'前缀的。还以Bash为例,当以非login方式启动时,它会调用~/.bashrc,随后~/.bashrc中调用/etc/bashrc,最后/etc/bashrc调用所有/etc/profile.d目录下的脚本。

一旦打开一个交互式login shell,或者以--login选项登录的非交互式shell,都会首先加载并执行/etc/profile中的命令,然后再依次加载~/.bash_profile, ~/.bash_login, 和~/.profile中的命令。

当bash以login
shell启动时,它会执行/etc/profile中的命令,然后/etc/profile调用/etc/profile.d目录下的所有脚本;然后执行~/.bash_profile,~/.bash_profile调用~/.bashrc,最后~/.bashrc又调用/etc/bashrc。要识别一个shell是否为login
shell,只需在该shell下执行echo $0。

注意: /etc/profile中的设置只对Login Shell生效,而crontab运行脚本的shell环境是non-login的,不会加载/etc/profile的设置。

Shell应用示例

根据时间创建文件夹

需求:创建10个目录,目录名称以当天时间开头,后面拼上目录编码

例如:1970-01-01_1

编写脚本monitor.sh

持续观察服务器每天的运行状态,需要结合shell脚本程序和计划任务,定期跟踪记录不同时段服务器的cpu负载,内存,交换空间,磁盘使用量等信息

  1. #!/bin/bash
  2. #this is the second script!
  3. day_time=`date+"%F %R"`
  4. cpu_test=`uptime`
  5. mem_test=`free -m | grep "mem" | awk '{print $2}'`
  6. swap_test=`free -m | grep "mem" | awk '{print $4}'`
  7. disk_test=`df -hT`
  8. user_test=`last -n 10`
  9. echo "now is $day_time"
  10. echo "%cpu is $cpu_test"
  11. echo "Numbet of Mem size(MB) is $mem_test"
  12. echo "Number of swap size(MB) is $swap_test"
  13. echo "the disk shiyong qingkuang is $disk_test"
  14. echo "the users login qingkuang is $user_test"

设置cron任务

  1. */15 * * * * bash /monitor.sh
  2. 55 23 * * * tar cxf /var/log/runrec /var/log/running.today && --remove-files

备注:设置cron计划任务是为了时时执行此脚本,来监控系统状态,并记入日志,便于对系统更好的管理!

shell 脚本学习的更多相关文章

  1. 笔记——shell脚本学习指南

    <shell脚本学习指南>机械工业出版 ISBN 987-7-111-25504-8 第2章 2.4 初级陷阱 1.当今的系统,对#!这一行的长度限制从63到1024个字符都有,尽量不要超 ...

  2. Shell 脚本学习资料搜集

    Shell文档 ChinaUnix上大神“網中人”总结的Shell十三问,强烈推荐,这本书讲得比较精炼,而且都是一些Shell学习中容易把握不住的一些细节难点.每一问都写得非常精彩.ChinaUnix ...

  3. 学习笔记之Shell脚本学习指南 & sed与awk & 正则表达式

    正则表达式_百度百科 http://baike.baidu.com/link?url=ybgDrN2WQQKN64_gu-diCqdeDqL8LQ-jiQ-ftzzPaNUa9CmgBRDNnyx50 ...

  4. 转 shell脚本学习指南

    shell脚本学习指南 以下八点不敢说就能成为你shell脚本学习指南de全部,至少可以让你编写出可靠的shell脚本. 1. 指定bashshell 脚本的第一行,#!之后应该是什么?如果拿这个问题 ...

  5. Shell脚本学习 - 运算符

    继续shell脚本学习.上一篇是基本数据类型和语法的总结,这一篇是运算相关的操作. 运算符 bash不支持简单的数学计算,需要依赖其他命令实现. expr可以代为实现. # 表达式一般这么写 ` + ...

  6. shell脚本学习总结02--数组

    bash同时支持普通数组个关联数组,普通数组只能使用整数作为数组的索引,关联数组可以使用字符串作为数组的索引. 数组的定义方法: 在单行中使用一列值定义一个数组 [root@new ~]# array ...

  7. Shell脚本学习指南笔记

    Shell脚本学习指南 作者:Danbo 2015-8-3 脚本编程语言与编译型语言的差异 许多中型.大型的程序都是用编译型语言写的,例如:C.C+.Java等.这类程序只要从源代码(Source C ...

  8. shell脚本学习之6小时搞定(1)

    shell脚本学习之6小时搞定(1) 简介 Shell是一种脚本语言,那么,就必须有解释器来执行这些脚本. Unix/Linux上常见的Shell脚本解释器有bash.sh.csh.ksh等,习惯上把 ...

  9. shell脚本学习心得

    近来主要捣鼓ubuntu,大多数项目中都用到了sh脚本作为启动脚本等,以前只是大概明白如何使用,今天需要自己修改并运行脚本就碰到了很多问题,所以决定静下心来学习一下shell脚本,学习了几个小时,现将 ...

  10. shell脚本学习(一)

    Shell脚本最常用于系统管理工作,或者用于结合现有的程序以完成小型.特定的工作. Shell的特点有: 1. 简单性 2. 可移植性 3. 开发容易 [什么是shell] 简单点理解,就是系统跟计算 ...

随机推荐

  1. day 06 列表去重, 数据类型的补充,编码,深浅copy

    因为重要,所以放前面 列表去重 l1 = [1, 2, 3, 4, 5] l2 = [3, 4, 5, 6, 7] set = list(set(l1 + l2)) # set自动去重,然后变成lis ...

  2. GridView(网格视图)+MotionEvent(触控事件)实现可以拖动排序的网格图

    1.一触碰,就回调onInterceptTouchEvent(MotionEvent ev)事件,每次的触碰都只回调一次,接着只要还没有放手,就一直回调onTouchEvent(MotionEvent ...

  3. 关于EOF:

    请先看下面一段程序: #include"stdio.h" int main() { ],t; int i,j; while(scanf("%s",a)!=EOF ...

  4. vue2.0细节剖析

    1.样式切换 单个切换样式 /*html部分*/ <div class="bg" v-bind:class="{active:isActive}"> ...

  5. Sublime Text3 常用快捷键必看

    Sublime Text3 常用快捷键必看  https://blog.csdn.net/md1688/article/details/53043525

  6. PAT 1036 跟奥巴马一起编程(15)(代码)

    1036 跟奥巴马一起编程(15)(15 分) 美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统.2014年底,为庆祝"计算机科学教育周& ...

  7. 洛谷3119 [USACO15JAN]草鉴定Grass Cownoisseur

    原题链接 显然一个强连通分量里所有草场都可以走到,所以先用\(tarjan\)找强连通并缩点. 对于缩点后的\(DAG\),先复制一张新图出来,然后对于原图中的每条边的终点向新图中该边对应的那条边的起 ...

  8. 原生和web交互jsbridge交互总结

    技术点:jsbridge. 一: 参数及其意义(代码意义结合支付项目) 二:主动请求原生参数与方法(sendapi) 参数1 判断接口类型 参数2 传递给原生的数据 参数3 回调函数,response ...

  9. vs2015 npm list 更新问题

    在更新npm list时候,经常会非常的慢,今天试了一个诡异的方法,就是在文件夹下面直接把所有缓存全部删除,全部重新下,结果感觉反而速度快很多. 原来的更新包80M竟然1个小时没有下载完. C:\Us ...

  10. Netty 源码(二)NioEventLoop 之 Channel 注册

    Netty 源码(二)NioEventLoop 之 Channel 注册 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一 ...