一、shell介绍1.1 shell起源1964年美国ATT公司的贝尔实验室、麻省理工学院及美国通用电气公司共同参与开始研发一套可以安装在大型主机上的多用户、多任务的操作系统该操作系统的名称为Multics。1970年丹尼斯•里奇和汤普逊启动了另外一个新的多用户、多任务的操作系统的项目他们把这个项目称之为UNICS。1973年使用C语言重写编写了Unix。通过这次编写使得Unix得以移植到其他的小型机上面。1979年第一个重要的标准UNIX Shell在Unix的第7版中推出并以作者史蒂夫•伯恩StephenBourne的名字命名叫做Bourne Shell简称为sh。20世纪70年代末C Shell作为2BSD UNIX的一部分发布简称csh。之后又出现了许多其他的Shell程序主要包括Tenex C Shelltcsh、Korn Shellksh以及GNU Bourne-Again shellbash。查看当前系统支持的shell[rootserver ~]# cat /etc/shells/bin/sh /bin/bash /usr/bin/sh /usr/bin/bash查看当前系统默认shell[rootserver ~]# echo $SHELL/bin/bash1.2 shell是什么1.2.1 shell是命令解释器shell壳能识别用户输入的各种命令并传递给操作系统。真正能够控制计算机硬件CPU、内存、显示器等的只有操作系统内核Kernel图形界面和命令行只是架设在用户和内核之间的一座桥梁由于安全、复杂、繁琐等原因用户不能直接接触内核也没有必要需要另外再开发一个程序让用户直接使用这个程序该程序的作用就是接收用户的操作点击图标、输入命令并进行简单的处理然后再传递给内核这样用户就能间接地使用操作系统内核。用户界面和命令行就是这个另外开发的程序就是这层“代理”。在Linux下这个命令行程序叫做 ShellShell 是一个应用程序它连接了用户和 Linux 内核让用户能够更加高效、安全、低成本地使用 Linux 内核这就是 Shell 的本质。Shell 本身并不是内核的一部分它只是站在内核的基础上编写的一个应用程序它和 QQ、迅雷、Firefox 等其它软件没有什么区别。然而 Shell 也有着它的特殊性就是开机立马启动并呈现在用户面前用户通过 Shell 来使用 Linux不启动 Shell 的话用户就没办法使用 Linux。1.2.2 shell是脚本语言如果有一系列经常需要使用的shell命令可以将其存储在一个文件里shell可以读取这个文件并顺序执行其中的命令我们把这样的文件就叫shell脚本。编程语言 C/C、java、Go语言等必须在程序运行之前将所有代码都翻译成二进制形式也就是生成可执行文件用户拿到的是最终生成的可执行文件在计算机中运行。而shell是一门解释性语言shell脚本不需要编译即可执行即直接运行源码。编译型语言的优点是执行速度快、对硬件要求低、保密性好适合开发操作系统、大型应用程序、数据库等。脚本语言的优点是使用灵活、部署容易、跨平台性好非常适合 Web 开发以及小工具的制作。shell脚本的优势在于处理操作系统底层的业务因为有大量的linux系统命令为它做支撑。2000多个命令都是shell脚本编程的有力支撑特别是grep、awk、sed等。1.3 脚本的书写规范与执行1.3.1脚本的书写规范脚本文件的名称建议脚本文件名以.sh结尾见名知意第一行声明#!/bin/bash注释其余以#开头的行表示注释尽量用英文注释防止本机或切换系统环境后中文乱码的困扰。单行注释可以放在代码行的尾部或代码行的上部。多行注释用于注解复杂的功能说明可以放在程序体中也可以放在代码块的开始部分。多行注释写法使用冒号“:”配合here document语法如下xxxx 可以是字符或数字单引号可以不加但以防出现莫名其妙的意外发生比如发生字符扩展、命令替换。:xxxx comment1 comment2 comment3 …… xxxx自动生成脚本开头的注释和说明通过给root账号配置vim的“个性化默认设置”使用root用户vim编辑创建.sh文件时会自动生成下面的关于脚本文件的注释说明。[rootserver ~]# vim /root/.vimrcautocmd BufNewFile *.py,*.cc,*.sh,*.javaexec:call SetTitle()func SetTitle()ifexpand(%:e)shcall setline(1,#!/bin/bash)call setline(2,#########################)call setline(3,#File name:.expand(%))call setline(4,#Version:v1.0)call setline(5,#Email:admintest.com)call setline(6,#Created time:.strftime(%F %T))call setline(7,#Description:)call setline(8,#########################)call setline(9,)endif endfunc脚本中的语句定义函数定义变量并赋值shell命令流程控制语句等脚本执行后打印内容到屏幕echo[rootserver ~]# echo hello worldhello world#-n不打印换行[rootserver ~]# echo -n hello worldhello world[rootserver ~]##-e启用转义字符格式echo -e \e[字体控制字体颜色或背景色 字符串内容 \e[0m#\e[表示控制开始\e[0m表示控制结束#字体控制选项1表示高亮4表示下划线5颜色闪烁#字颜色30-37 , 背景色40-47[rootserver ~]# echo -e \e[1;31m红色字\e[0m红色字[rootserver ~]# echo -e \e[4;45;37m 紫底白字\e[0m紫底白字printf#需要自己添加换行\n[rootserver ~]# printf hello worldhello world[rootserver ~]# printf hello world\nhello world#使用格式替代符打印[rootserver ~]# printf %d %s\n 1 abc1abc[rootserver ~]# printf %10s %-3d %-4.2f\n zhangsan 9 130.23 lisi 10 130.5zhangsan9130.23lisi10130.50# %s %d %f 都是格式替代符s 输出一个字符串d 整型输出f 输出实数以小数形式输出。#%10s 字符串占10个字符宽度- 表示左对齐没有则表示右对齐如果不足则自动以空格填充超过也会将内容全部显示出来。#%-3d 数字占3个宽度- 表示左对齐没有则表示右对齐# %-4.2f 格式化为小数其中 .2 指保留2位小数脚本中一般是一行一条命令而且是按照顺序执行每条命令。可以用以下符号在一行连接多条命令;、、||、|#先执行分号前的date命令再执行分号后面的命令[rootserver ~]# date ; ls -l /etc/passwd#先执行符号前面的命令如果前面命令执行成功则执行后面的命令如果前面命令执行失败、报错则不执行后面的命令[rootserver ~]# mkdir /mnt/iso mount /dev/sr0 /mnt/iso#先执行||符号前面的命令如果前面命令执行成功则不执行后面的命令如果前面命令执行失败、报错则执行后面的命令[rootserver ~]# mkdir tt || ls /#管道符|管道左边的命令的输出作为管道右边命令的输入#例如显示剩余内存大小[rootserver ~]# free -h | grep Mem | tr -s | cut -d -f4[rootserver ~]# free -h | awk /Mem/ {print $4} # 使用awk截取第4列[!Note]shell中没有强制要求缩进但是建议缩进可以提高阅读性程序更有层次感。1.3.2执行shell脚本输入脚本文件的路径绝对路径或者相对路径都可执行脚本文件需要x执行权限bash 脚本文件source 脚本文件. 脚本文件[!NOTE]前两种方法都是启动一个子shell在子shell中执行此脚本脚本中设置的变量在脚本执行完毕后不会保存。后两种方法是在当前shell进程中执行此脚本而不是重新启动一个shell 在子shell进程中执行此脚本并且脚本中设置的变量在脚本执行完毕后会保存下来。#使用绝对路径或者相对路径方式执行脚本需要x执行权限[rootserver ~]# mkdir /shell[rootserver ~]# vim /shell/test.sh#!/bin/bashechohello world[rootserver ~]# /shell/test.sh-bash: /shell/test.sh: 权限不够[rootserver ~]# cd /shell/[rootserver shell]# ./test.sh-bash: ./test.sh: 权限不够[rootserver shell]# bash /shell/test.shhello world[rootserver shell]# source /shell/test.shhello world[rootserver shell]# . /shell/test.shhello world[rootserver shell]# chmod ax /shell/test.sh[rootserver shell]# /shell/test.shhello world[rootserver shell]# ./test.shhello world#前两种方式会产生子shell脚本中的语句只在子shell中有效当前shell中无效[rootserver shell]# vim /shell/test1.sh#!/bin/bashcd/etc#执行脚本后当前shell的工作目录未发生任何变化因为只是在子shell中执行了语句不会影响当前shell[rootserver shell]# chmod ax /shell/test1.sh[rootserver shell]# /shell/test1.sh[rootserver shell]# bash /shell/test1.sh#source和.执行方式脚本中的语句会在当前shell中生效[rootserver shell]# source /shell/test1.sh#可以看到执行完脚本后工作目录发生了变化[rootserver etc]#[rootserver etc]# cd /shell/[rootserver shell]# . /shell/test1.sh[rootserver etc]#1.3.3脚本的退出状态码linux的命令执行后就会有一个退出状态码可使用echo $?查看命令的退出状态码退出状态码范围0-255通常情况下执行成功的命令返回0不成功的命令返回非0值脚本执行完毕后的退出状态码取决于脚本的最后一条命令也可以使用exit 0-255指定脚本的退出状态码可使用echo $?查看脚本的退出状态码函数也可以返回一个退出状态码使用return 0-255返回函数的状态码可使用echo $?查看函数的退出状态码。#命令执行后的状态码[rootserver ~]# echo hello worldhello world[rootserver ~]# echo $?0[rootserver ~]# ls /root/hahahals: 无法访问/root/hahaha:没有那个文件或目录[rootserver ~]# echo $?2#脚本执行后的状态码[rootserver ~]# vim /shell/test.sh#!/bin/bashechohello world[rootserver ~]# bash /shell/test.shhello world[rootserver ~]# echo $?0[rootserver ~]# vim /shell/test.sh#!/bin/bashechohello worldls/root/hahaha[rootserver ~]# bash /shell/test.shhello world ls: 无法访问/root/hahaha:没有那个文件或目录[rootserver ~]# echo $?2[rootserver ~]# vim /shell/test.sh#!/bin/bashechohello worldls/root/hahahaexit1[rootserver ~]# bash /shell/test.shhello world ls: 无法访问/root/hahaha:没有那个文件或目录[rootserver ~]# echo $?1[!note]常见状态码0----------------命令运行成功1----------------通知未知错误2----------------误用shell命令126--------------命令不可执行127--------------没有找到命令128--------------无效退出参数128x------------linux信号x的严重错误130--------------命令通过CtrlC终止255--------------退出状态码越界