什么是Shell
解释Shell脚本名词之前,我们先来说下什么是Shell?
Shell是一个命令解释器,它在操作系统的最底层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕返回给用户。这种对话方式可以是交互的方式(从键盘输入命令,可以立即得到shell的回应),或非交互(脚本)的方式。
命令行解释器:使用户得以与内核进行沟通的“翻译官”
下面图中黄色部分就是命令解释器shell。
提示:Shell在英文中的意思是贝壳,从上图我们可以看出,命令解释器shell就像一个贝壳一样包住了系统核心。
解释:
用户下达指令,shell把用户的指令翻译成`系统内核`能识别的二进制数,然后传达给系统内核,系统内核会把指令进行查看分析,然后下达“进程调度”和“资源分配”指令给CPU,让CPU去执行,然后由计算机硬件来显示结果。
什么是shell脚本
Linux Shell脚本 :实现某种功能的,有执行权限的文本文件
内核的作用:资源分配和进程调度
命令或语句不在命令行执行,而是通过一个程序文件执行时,该程序就被称为shell脚本或Shell程序,shell程序很类似DOS系统下的批处理程序(*.bat)。用户可以在shell脚本中敲入一系列的命令或命令语句。这些命令、变量和流程控制语句等有机的结合起来就形成了一个功能强大的shell脚本。
Windows下利用批处理程序bat开发的备份网站及数据库的脚本
@echo off set date=%date:~0,4%-%date:~5,2%-%date:~8,2% mysqldump -uroot -poldboy -A -B > D:ak"%date%".sql rar.exe a -k -r -s -m1 D:ak"%date%".sql.rar D:ak"%date%".sql del D:ak*.sql rar.exe a -k -r -s -m1 D:ak"%date%"htdocs.rar D:workPHPnowhtdocs
范例1:清除.var/log下messages日志文件的简单命令脚本
#把所有命令放在一个文件里堆积起来就形成了脚本,下面就是一个最简单的命令堆积形成的shell脚本。
#必须使用root身份来运行这个脚本
#清除日志脚本,版本1
cd /var/log cat /dev/null > messages echo "Logs cleaned up." #提示:/var/log/messages是系统的日志文件
看完这个脚本有什么想法?
1)不是root执行不了 2)没有流程控制,就是没有逻辑性
范例2:包含命令、变量和流程控制语句的清除/var/log下messages日志文件的shell脚本
#!/bin/bash #清除日志脚本,版本2 LOG_DIR=/var/log ROOT_UID=0 #$UID=0的时候,用户才具有root用户的权限 #要使用root用户来运行. if [ "$UID" -ne "$ROOT_UID" ] then echo "Must be root to run this scripts." exit 1 fi cd $LOG_DIR || { echo"Cannot change to necessary directory." >&2 exit 1 } cat /dev/null > messages && echo "Logs cleaned up." exit 0 #退出之前返回0表示成功,返回1表示失败
扩展:
清除日志的三种方法:
[root@Web ~]# echo >test.log [root@Web ~]# >test.log [root@Web ~]# cat /dev/null >test.log
应用场景:保留文件,清空内容
脚本在执行时会启动一个子shell进程;
1.命令行中启动的脚本会继承当前shell环境变量;
2.系统自动执行的脚本(非命令行启动)就需要自我定义需要各环境变量;
编写脚本
1.指定脚本的执行环境 #!/bin/bash
2.注释信息
3.脚本的功能体
进程和shell的关系
1.在每个进程看来,当前主机上只存在内核和当前进程,不知道别的进程存在
2.进程是程序的副本,进程是程序执行实例
3.进程可同名,进程号不同就是不同的进程
4.进程是程序的副本,进程是程序执行示例
5.多个用户同时登录shell,每个Shell都会有所不同,彼此之间各不相干
6.允许同一个用户登录多次,每个Shell都会有所不同,彼此之间各不相干
7.同一个用户在不同地方登录的Shell都会不同,都对应不同的Shell进程
8.bash自身是一个外部程序,但启动以后会带有内部命令
9.父Shell的设定对于子Shell是无效的,所之亦然。
10.shell之间可以交互打开
shell脚本与perl,php,python语言的差别?
Shell的优势在于处理操作系统底层的业务(大量的命令为它做支撑,例如,grep,sed,awk)。
一键安装,报警脚本,常规的业务应用,shell开发更简单快速。
php,python优势在于开发运维工具,web界面的管理工具
Shell脚本很擅长处理纯文本类型的数据,而linux中几乎所有的配置文件、日志文件(如nfs,rsync,httpd,nginx,lvs等)都是纯文本类型的文本。因此,如果学好Shell脚本语言,就可以利用它Linux系统中发挥巨大的作用。
Linux系统支持的Shell
类型 说明 bsh:Bourne Shell 70年代中期诞生于贝尔实验室,脚本编程功能较强 csh 80年代早期诞生于加利福尼亚大学,使用C语言风格,命令行交互方便 ksh:Korn Shell 兼容Bsh的语法和C语法的交互特性 bash:Bourne-Again Shell bsh的升级版,吸取了ksh的一些特点,是大多数Linux的默认Shell程序 zsh 兼备各种Shell程序的优点,交互式操作效率更高
Shell脚本语言是弱类型语言,较为通用的shell有标准的Bourne shell (sh)和 C shell (csh)。其中 Bourne shell (sh)。其中 Bourne shell (sh)已经被bash shell取代。
当前的Linux主机支持哪些Shell?
#cat /etc/shells /bin/sh //多数Unix默认的Shell /bin/bash //多数Linux默认使用的Shell /sbin/nologin //非登录Shell /bin/tcsh /bin/csh /bin/csh 在RHEL系统中,实际上sh是bash的符号链接
CentOS Linux系统默认的shell是():
[root@Web ~]# echo $SHELL /bin/bash [root@Web ~]# grep root /etc/passwd [root@Web ~]# grep root /etc/passwd root:x:0:0:root:/root:/bin/bash
Bash的基本特性
Tab键补全、快捷键
命令历史
命令别名
标准输入输出
重定向
管道操作
Tab键补全
命令补全 搜索PATH环境变量所指定的每个路径下以我们给出的字符串 开头的可执行文件,如果多于一个,两次tab,可以给出列表;否则将直接补全;
路径补全 搜索我们给出的起始路径下的每个文件名,并试图补全
快捷键
快捷键 注释 Ctrl+a 跳到命令行首 Ctrl+e 跳到命令行尾 Ctrl+u 删除光标至命令行首的内容 Ctrl+k 删除光标至命令行尾的内容 Ctrl+l 清屏
命令历史
默认记录1000条最近执行过的命令
存放位置:~/.bash_history
环境变量 注释
PATH 命令搜索路径
HISTSIZE 命令历史缓冲区大小
命令历史:history
参数选项 注释 -c 清空命令历史 -d OFFSET [n] 删除指定位置的命令 -w 保存命令历史至历史文件中
命令历史的使用技巧:
参数选项 注释 !n 执行命令历史中的第n条命令 !-n 执行命令历史中的倒数第n条命令; !! 执行上一条命令; !string 执行命令历史中最近一个以指定字符串开头的命令 !$ 引用前一个命令的最后一个参数 Esc, . Alt+.
命令别名
语法:
alias CMDALIAS='COMMAND [options] [arguments]'
有效范围
在shell中定义的别名仅在当前shell生命周期中有效
别名的有效范围 仅为当前shell进程
相关操作
相关操作 注释 aliase 列出已定义的命令别名 unaliase 别名 删除指定的命令别名 unaliase -a 清空所有别名 alias 别名='实际命令' 定义新的命令别名
如何让定义的别名命令生效?
vim ~/.bashrc 只对某个用户生效
vim /etc/bashrc 对所有用户生效
标准输入输出
1.标准输入:从该设备接收用户输入的数据
2.标准输出:从该设备向用户输出的数据
3.标准错误:通过该设备报告执行中的出错信息
类型 设备文件 文件描述号 默认设备 标准输入 /dev/stdin 0 键盘 标准输出 /dev/stdout 1 显示器 标准错误 /dev/stderr 2 显示器
重定向
重定向:改变标准输入/输出/错误输出的方向
set -C 禁止对已经存在文件使用覆盖重定向 强制覆盖输出,则使用 >| set +C 关闭上述功能
类型
类型 操作符 用途 重定向输入 < 将文本输入来源由键盘改为指定的文件 重定向输出 > 将命令行的正常执行输出保存到文件,而不是直接显示在屏幕上 >> 与>类似,但操作是追加而不是覆盖 重定向错误 2> 将命令行的执行错误信息保存到文件,而不是直接显示在屏幕上 2>> 与2>类似,但操作是追加而不是覆盖 混合重定向(重定向正确和错误输出信息) &> 相当于>和2>,覆盖到同一个文件 &>> 相当于>和2>>,追加到同一个文件
管道
管道:前一个命令的输出,作为后一个命令的输入
管道操作符”|“
cmd1 | cmd2 [...|cmdn] 将cmd1的输出结果,作为cmd2的输入
例如:
#pgrep -l "." | wc -l————统计运行的进程数 #ifconfig | grep "HWaddr"————查看本机的MAC地址信息
shell脚本的建立和执行
shell脚本(bash shell程序)通常是在编辑器(如vi/vim)中编写,由Unix/Linux命令、bash shell命令、程序结构控制语句和注释等内容组成。
脚本开头(第一行)
一个规范的shell脚本第一行会指出由哪个程序(解释器)来执行脚本中的内容,在linux bash编程中一般为:
#!/bin/bash
或
#!/bin/sh
“#!”又称为幻数,在执行bash脚本的时候,内核会根据它来确定该用哪个程序来解释脚本中的内容。这一行必须在脚本顶端的第一行,如果不是则为注释。
sh和bash的区别:
[root@Web ~]# ls -l /bin/sh lrwxrwxrwx. 1 root root 4 9月 25 2014 /bin/sh -> bash [root@Web ~]# ll /bin/sh lrwxrwxrwx. 1 root root 4 9月 25 2014 /bin/sh -> bash
提示:sh为bash的软链接,这里推荐用标准写法#!/bin/bash
下面是linux常用脚本语言开头的编码写法,不同语言脚本的开头一般都要加上如相应语言的开头标识内容
#!/bin/sh #!/bin/bash #!/usr/bin/awk #!/bin/sed #!/usr/bin/tcl #!/usr/bin/expect #!/usr/bin/perl #!/usr/bin/env python
Centos和RedHat Linux下默认的Shell均为bash。因此,在写shell脚本的时候,我们的脚本的开头也可以不加#!/bin/bash。但如果当前的shell非你默认的shell时,比如tcsh,那么就必须要写#!了。否则脚本文件就只能执行命令的集合,不能够使用shell内建的指令了,建议读者养成习惯,不管什么脚本最好都加上开头语言标识。
如果脚本的开头不指定解释器,那么,就要用对应的解释器来执行脚本。例如:
bash test.sh
python test.py
脚本注释
在shell脚本中,跟在(#)井号后面的内容表示注释,用来对脚本进行注释说明,注释部分不会被当作程序执行,仅仅是给人看的,系统解释器看不到的,注释可自成一行,也可以跟在脚本命令后面与命令在同一行。开发脚步时,如果没有注释,其他人就很难理解脚本究竟在做什么,时间长了自己也会忘记。因此,我们要尽量养成对所做的工作(脚本等)书写注释的习惯,不光是方便别人,也方便自己,否则,写完一个脚本后也许几天后就记不起脚本的用途了,再重新阅读也会浪费很多宝贵时间。对于团队的协作也不利。
Shell脚本的执行
当shell脚本以非交互式的方式运行时,它会先查找环境变量ENV,该变量指定了一个环境文件(通常是.bashrc),然后从该环境变量文件开始执行,当读取了ENV文件后,SHELL才开始执行shell脚本中的内容。
Shell脚本的执行通常可以采用一下三种方式:
bash script-name 或sh script-name(推荐使用) path/script-name或./script-name(当前路径下执行脚本) source script-name或. script-name #注意”.”点号
执行说明:
1.第一种方法是当脚本文件本身没有可执行权限(即文件x位为-号)时常使用的方法,这里推荐使用bash执行,或者文件开头没有指定解释器(推荐的方法)
2.第二种方法需要先将脚本文件的权限改成可执行文件(即文件加x位),具体方法:chmod u+x script-name或chmod 755 script-name。
然后通过脚本路径就可以直接执行脚本了。
在生产环境中,不少读者在写完shell脚本后,由于疏忽忘记给予该脚本文件执行权限,就直接应用了,结果导致脚本没有按自己的意愿手动或定时执行。
避免的方法是用第一种方法替代第二种。
3.第三种方法是使用source或者”.”点号读入或加载指定的shell脚本文件(如san.sh),然后依次执行指定shell脚本文件san.sh中的所有语句。这些语句将作为当前父shell脚本father.sh进程的一部分运行。因此,使用source或者”.”点号可以将san.sh自身脚本中的变量的值或函数等的返回值传递到当前的父shell脚本father.sh中使用。这是第三种方法和前两种的最大区别。也是值得读者注意的地方。
Source或者'.'点号命令的功能是在当前shell中执行source或者'.'点号加载并执行的相关脚本文件中的命令及语句,而不是产生一个子shell来执行命令文件中的命令
下面我们举例说明
[zgy@Web ~]$ cat >test.sh #编辑test.sh脚本文件 echo 'I am zgy' echo 'I am zgy'
输入"echo 'I am oldboy'"内容后按回车,然后在按ctrl+d组合键即可结束编辑。此操作作为特殊编辑方法,作为cat用法的扩展提及(在使用中去记忆是个好习惯)。
[zgy@Web ~]$ cat test.sh echo 'I am zgy' [zgy@Web ~]$ sh test.sh #使用第一种方式的sh命令执行test.sh脚本文件 I am zgy [zgy@Web ~]$ bash test.sh #使用第一种方式的bash命令执行test.sh脚本命令 I am zgy
我们使用第一种方法,发现均可以执行并得到预期的结果。
zgy@Web ~]$ ls -l test.sh -rw-rw-r--. 1 zgy zgy 22 4月 11 12:45 test.sh [zgy@Web ~]$ ./test.sh #使用第二种方式"./"在当前目录下执行test.sh脚本文件,可以发现,这个地方无法自动补全。 -bash: ./test.sh: 权限不够 #提示:此处没有可执行权限
但是可以用source或者"."点号执行。
[zgy@Web ~]$ . test.sh I am zgy [zgy@Web ~]$ source test.sh I am zgy
提示:"."点号和source命令的功能相同,都是读入脚本并可以执行脚本
给test.sh加执行权限
[zgy@Web ~]$ chmod u+x test.sh [zgy@Web ~]$ ./test.sh I am zgy
可以看到,给test.sh加完可执行权限就可以执行了。
现在测试第三种source或者"."点号的特殊的传递变量值到当前shell的例子
[zgy@Web ~]$ echo 'userdir=`pwd`'>testsource.sh #行的内容通常用echo很方便。 [zgy@Web ~]$ cat testsource.sh userdir=`pwd` [zgy@Web ~]$ sh testsource.sh [zgy@Web ~]$ echo $userdir
#此处为空,并没有出现当前路径输出,这是为什么呢?
根据上面例子,可以发现,通过sh或bash命令执行过的脚本,脚本结束后在当前shell窗口查看userdir变量的值,发现变量值是空的。现在以同样的步骤改用source执行,然后再看看userdir变量的值
[zgy@Web ~]$ source testsource.sh [zgy@Web ~]$ echo $userdir /home/zgy
再看看系统Nfs服务的脚本如何使用”.”号的。
# Source function library.
. /etc/rc.d/init.d/functions
提示:操作系统及服务自带的脚本是学习的标杆和参考(虽然有时感觉不是很规范)
结论:
通过source或”.”点号加载执行过的脚本,在脚本结束后脚本中的变量值(包括函数)在当前Shell中依然存在,而是sh和bash则不行。
因此,在做Shell脚本开发时,如果脚本中有需求引用其他脚本的内容或者配置文件时,最好用”.”点号或source在脚本开头加载该脚本或配置文件,
然后再下面的内容用可以调用source加载的脚本及文件中的变量及函数等内容
某互联网公司Linux运维职位实际面试笔试
1、已知如下命令返回结果,请问echo $user的返回的结果为() [zgy@Web ~]$ cat test.sh user=`whoami` [zgy@Web ~]$ sh test.sh [zgy@Web ~]$ echo $user 问:执行echo $user命令的结果是什么? 1)当前用户 2)zgy 3)空(无内容输出) #这个是正确答案
Shell脚本开发基本规范及习惯
1)开头指定脚本解释器
#!/bin/sh或#!/bin/bash
2)开头加版权版本等信息
#Date: 16:29 2015-4-11 #Author Created by zgy #Mail 1092327070@qq.com #Function: This scripts function is... #Version 1.1
3)脚本中不用中文注释
尽量用英文注释,防止本机或切换系统环境后中文乱码的困扰
4)脚本以.sh为扩展名
例:script-name.sh
5)代码书写优秀习惯
1.成对内容的一次写出来,防止遗漏。如:
[]、{}、''、``、""
2.[]中括号两段要有空格,书写时即可留出空格[ ],然后在退格书写内容。
3.流程控制语句一次书写成,再添加内容,如:
if语句格式一次完成:
if 条件内容 then 内容 fi
for语句格式一次完成:
for do 内容 done
提示:while和util,case等语句也是一样
6)通过缩进让代码易读
if 条件内容 then 内容 fi
提示:好的习惯可以避免很多不必要的麻烦,提升很多的工作效率。