在正式开始前,我们可以思考一个问题,学习一门技术难吗?不少人的答案可能是很难。

来看一个案例,也可以说是两个。

2017年苹果WWDC大会上,有两名开发者令世界瞩目。一个是已经82岁,来自日本的老太太,若宫正子;另一个是年仅10岁,来自澳大利亚的小朋友,Yuma。前者60岁时才接触电脑,81岁时用上Mac系统,然后花了半年时间开发出了自己的app;后者从6岁开始设计网站,随后四年为苹果商店贡献了五个app。有意思的是,两个人都有自己的编程课堂,老太太在自己家教老人电脑知识,小Yuma建了一个Youtube频道,叫 Anyone Can Code(人人皆可编程),主要教小孩编程。

若宫正子
Yuma

或许有人想说,他们不过是天赋好罢了。但我只想说,这种想法不过是不敢跳出舒适区,为自己的懒惰和无能找借口罢了。

如今,各种底层技术已经经过层层封装,可以说,只要你有想法,编程甚至能够像搭积木一样简单。

首先,你有什么需求,你的这个需求可以被什么技术解决,了解它的基本概念,然后把它变成生活的一部分,想方设法它。遇到问题了怎么办?把大问题拆分成小问题,找搜索引擎解答。

记住一点:你没必要学习某技术的所有知识,了解基本概念,基本原理,就可以用它了。

有人又会说了,我没啥需求怎么办?李敖曾说过:“作家不能等有了灵感才写作,一如妓女不能等有了性欲才接客。”

下面我们来学习shell脚本吧。主要面向linux初学者以及对linux感兴趣的人。请自行准备linux系统或Mac系统。

什么是Linux内核?

我们大多知道操作系统,但少有人知道内核,像CenOS,Redhat,Ubuntu等,这是操作系统,内核是Linux kernel,它是操作系统的心脏,是操作系统的大脑,去掉它,操作系统将不复存在,你的电脑也会成为破铜烂铁。

不用过于深究,你可以简单地将内核看做电脑的大管家,它的小弟们与它一起构成了操作系统,共同管理计算机资源。

什么是shell?

我们知道,计算机实际只懂0和1(二进制),我们与外国人沟通需要靠翻译,那么同计算机沟通当然也需要一个翻译,shell就是这个翻译。另外,你以为你是同计算机对话,其实不然,你实际上是同内核对话,你在电脑上看到的内存啊,磁盘啊,CPU等都是虚拟出来的,你的任何操作,最终都是由内核真正在计算机硬件上执行。

总结下shell定义:

  • 一个命令语言解释器,你执行的命令都由它翻译给内核,由内核转交给CPU执行
  • 是用户与内核之间的接口程序
  • 不管是图形界面(GUI)还是命令行界面(CLI),用户的一切输入都先由shell解释后再交给内核

什么是bash shell?

shell的版本有很多像什么sh、bash、csh、ksh等,其中bash是Linux世界使用最广泛的shell,也是很多Linux发行版的默认shell。

获取当前系统可用shell

[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin

查看当前使用的shell

[root@localhost ~]# echo $SHELL
/bin/bash

什么是CLI和GUI?

  • CLI:Command Line Interface(命令行接口)
  • GUI:Graphical User Interface(图形用户接口)
  • 顾名思义,CLI就是命令行界面,GUI就是图形界面
  • 我们在电影看到的那些黑客的电脑界面,花花绿绿的,执行一条命令就会有一大堆输出的叫命令行界面,我们平时用的windows系统,用鼠标在上面点点点的,叫图形界面,Linux当然也有自己的图形界面
  • 我们同shell对话,就是通过这两种接口进行的

尝试执行几个基本命令

[root@localhost ~]# echo "hello,linux"
hello,linux
[root@localhost ~]# date
Sat Jan 12 23:48:13 CST 2019

什么是标准输入、标准输出和标准错误输出?

  • 或许你曾听说过:linux里面一切皆文件,所谓的标准输入、标准输出和标准错误输出其实都是文件
  • 标准输入,缺省是终端键盘,所谓缺省,即系统默认状态
  • 标准输出,缺省是终端屏幕,至于终端这个概念,你就简单理解为显示器加键盘吧
  • 标准错误输出,缺省是终端屏幕

我们执行一条shell命令,就会打开这三个文件,你可以理解为三条管道,标准输入与键盘相连,标准输出和标准错误输出都默认与屏幕相连。没有特别指明的情况下,我们通过键盘输入的内容通过“标准输入”这条管道“流入”至相应的命令,命令的执行结果又会通过“标准输出”或“标准错误输出”这条管道“流出”至屏幕,展现在我们面前。

比如输入cat test.txt,cat这个命令作用是从命令行给出的文本中读取数据并将数据送到标准输出。

如果test.txt中有内容,那么标准输入就不是键盘了,而是该文件,文件内容通过“标准输入”这个“管道”流向cat命令,cat命令读取其内容,然后将内容“流出”至标准输出,即屏幕上。

# 将"hello"添加到test.txt这个文件中,如果不存在就创建
[root@localhost ~]# echo "hello" > test.txt   
# 输出test.txt文件内容
[root@localhost ~]# cat test.txt
hello    #标准输出

假如test.txt不存在,那么就会报错,错误信息通过“标准错误输出”这条管道“流出”至屏幕上。

[root@localhost ~]# cat test.txt
cat: test.txt: No such file or directory    #标准错误输出

假如cat命令后面没有参数,那么其标准输入便是键盘了,终端就会等待我们输入,cat获取标准输入,通过“标准输出”这条管道输出到屏幕上。

[root@localhost ~]# cat
hello   # 标准输入
hello   # 标准输出

什么是重定向?

比如一条命令cat test.txt,它会把test.txt这个文件的内容输出到屏幕上,但我不想输出到屏幕上,我想保存到一个文件里,就可以用”标准输出重定向“符号>将其重定向到某个文件cat test.txt > temp.txt,这样屏幕上就没有输出,也就改变了标准输出的流出方向,故而称之为”重定向“。

  • >又称为”覆盖重定向“,就是说会覆盖输出文件原先的内容。
  • >>被称为”追加重定向“,也就是说不会覆盖输出文件的内容,而是把输出追加到原内容的后面
  • <被称为”标准输入重定向“,一般命令的标准输入都是键盘,用了<,其标准输入就变成其后的文件了。比如,cat本身就能够以文件作为其标准输入,但我们也可以利用<来将文件变成其标准输入:cat < test.txt
  • 前面又说了,所谓的标准输入标准输出以及标准错误输出其实都是文件,是文件就会有文件描述符,就像人的名字一样,不然内核哪知道哪个文件是哪个文件啊。
  • 标准输入的文件描述符是0标准输出1标准错误输出是2。所以,如果我们想把cat test.txt的标准输出重定向到stdout.txt,其标准错误输出重定向到stderr.txt,可以这样做:cat test.txt 1> stdout.txt 2> stderr.txt,01一般都可以省略。
  • 如何把标准错误输出标准输出重定向到同一个文件?还是上面的例子:cat test.txt > temp.txt 2>&1。还有一种方式是不区分标准错误输出标准输出,统统重定向到同一个文件:cat test.txt &> temp.txt

什么是管道?

前面我们了解到,命令的标准输出默认是屏幕,假如该命令的标准输出我们想再利用怎么办?那就需要利用管道了。强调一下,标准输出是指命令执行成功后的输出。

举个例子:

[root@localhost ~]# echo “zhangsan” | useradd
# 这条命令的意思是输出”zhangsan“,并利用useradd命令添加其为linux普通用户
# ”zhangsan“为管道前的标准输出,管道将其变成后面useradd命令的标准输入

注意事项:

  • 管道只能处理前一个命令的标准输出,标准错误输出无法处理
  • 管道后面的命令必须能够接受标准输入才行,像ls命令就不接受标准输入,所以它不能放在管道后面。ls命令的作用是列举目录下的内容,当然了,在linux世界怎么能接受不能呢,利用xargs就可以把管道前的标准输出当作ls命令的标准输入了
[root@localhost ~]# echo "/etc"| xargs ls
# 没有xargs的话,这里的ls只能列举当前目录的内容,而非/etc这个目录

什么是shell脚本?

前面说过,shell是一个命令语言解释器。另外,shell本身也是一个程序,有自己的命令,像什么ls、cd、pwd等都是其内置命令,还有一些其他命令,都是别人为linux添加的基本命令。我们知道,编程时通过各种库调用来实现功能,而shell没有库,他通过调用各种命令来实现相应功能。

我们通过输入各种命令与shell交互,但有时我们不想手动输入各种命令,我们想把命令存在一个文件中,然后让shell执行该文件中的命令,于是就有了shell脚本。

所谓的shell脚本其实就是一个包含一系列shell命令的文本,目的是减少重复工作,实现自动化。

shell脚本基本组件

shell脚本是由各种shell命令组成,但绝对不是简单的命令堆积,它像其他编程语言一样,有自己的基本组件。

  • shell命令
    • shell内部命令,如ls、cd、pwd等
  • 其他命令
    • who、du、free等各种丰富的功能命令
  • 数据结构
    • 变量、数组、字典
  • 函数
    • 将一系列命令组合成一个函数,减少重复工作
  • 控制流
    • if、case等分支语句
    • for、while等循环语句

如何快速编写一个脚本?

脚本组成

一个完整的脚本有三部分组成:脚本声明、注释以及可执行语句。

  • 脚本声明
    • 前面说过,CPU只认识二进制,也就是说只能执行二进制程序文件
    • 我们的脚本是文本文件,需要一个解释者解释下,也就是shell,shell本身是二进制程序
    • CPU运行shell程序,shell解释脚本的每一行内容,然后找到对应的二进制程序,由CPU执行
    • 所以,首先我们得告诉CPU,该脚本用哪个解释器来解释
    • 如何声明,在脚本第一行顶格写:#!/bin/bash
    • #!被称为shebang符号,用来声明所使用的解释器,一旦执行某个脚本,CPU看到第一行,就会去执行该解释器,再由解释器去找对应的命令程序
    • /bin/bash指明bash二进制程序的位置
  • 注释
    • 简单的脚本还好,一旦复杂起来,可读性就大大降低,所以注释相当重要
    • 解释器会忽略注释
    • shell脚本中的单行注释符号是#
    • 至于多行注释,方法有很多,一般用不到的,想用的话,自己在网上搜搜
  • 可执行语句
    • linux命令以及相应的控制流与数据结构
    • 相关语法可以在网上找
    • 需要条件判断了,就搜shell if语法shell case语法
    • 需要重复执行某命令,就搜shell for循环shell while循环
    • 某个需求不知道用什么命令,就搜需求关键字好了
    • 某个命令不知道怎么用,直接在命令行输入man 命令或者直接百度

一个批量ping ip脚本

说了这么多,让我们开始写一个脚本吧,先说一个命令,检测网络通不通,需要用到ping命令,我们想批量检测某一个网段的主机是否网络通畅,那就需要用到for循环。

#linux中默认编辑器通常是vi或vim,该文本编辑器分为三种模式
#默认是普通模式(无法输入内容,但可以通过相应命令操作文本),普通模式下按i表示进入插入模式(可以插入内容了)
#按:表示进入命令行模式(批量操作文本),在插入模式或命令行模式下按ESC进入普通模式。输入ZZ或:wq退出vim编辑器
[root@localhost ~]# vim ping.sh
 #默认进入普通模式,先按i开始编写脚本,下面是脚本完整内容
 
 #!/bin/bash
 NET=192.168.1    #声明一个变量并赋值
 for i in {1..254}    #后面的{1..254}表示循环范围从1-254,如果你就是想测试下,这里就改成{1..10}吧
 do
 IP=$NET.$i    #字符拼接并赋值给IP这个变量
 ping -c 3 $IP &> /dev/null    
 #-c指明发包次数,&> /dev/null表示无论正确输出还是错误输出统统丢弃
 #$?表示上条命令的执行结果,0表示成功,非0表示失败
 #-eq代表”是否等于“,[]是条件测试语法,测试条件左右必须各留至少一个空格
 if [ $? -eq 0 ];then    
     echo "$IP is up" 
 else 
     echo "$IP is down"
 fi
 done
 # 编写完毕,按ECS进入普通模式,然后输入:wq退出vim编辑器

脚本执行

执行脚本主要有三种方法:

  • 直接运行脚本文件
    • 这种方法需要脚本有可执行权限:chmod u+x ping.sh
    • 然后输入脚本的绝对路径或相对路径来执行文件
    • 比如相对路径,你得先进入脚本所在的目录,然后执行:./ping.sh
  • 利用解释器执行
    • bash ping.sh
    • sh ping.sh
  • 利用source或.
    • source ping.sh
    • . ping.sh

第一种方法,如果你没有shebang语句(即第一行声明解释器),可能会报错;第二种方法,你可以不声明解释器类型,因为你是直接用解释器来执行的,也可以不赋予执行权限;第三种方法,你是在当前shell环境来执行的,前两种实际上都是在子shell中执行,执行完毕才退回到当前shell。

基本上用哪种方法都行,看你心情。

# 假如你想把执行结果保存到一个文件,也就会利用输出重定向
# 但同时你又想输出其结果到屏幕上,那就会用到tee命令了
[root@localhost ~]# bash ping.sh | tee out.txt
192.168.1.1 is up
192.168.1.2 is down
...

#现在你想知道有多少个ip是通的,这就用到涉及文本过滤命令grep以及统计命令wc
[root@localhost ~]# grep "up" out.txt | wc -l
   3

最后补充一点,同window不同,linux世界,文件后缀没有任何意义,加后缀名只是方便人类自己查看。

学习一门新技术,最痛苦的过程只是前两小时,熬过了这两小时,后面就会越走越顺。