Shell 概念
图形界面和命令行只是架设在用户和内核之间的一座桥梁。
由于安全、复杂、繁琐等原因,用户不能直接接触内核(也没有必要),需要另外再开发一个程序,让用户直接使用这个程序;该程序的作用就是接收用户的操作(点击图标、输入命令),并进行简单的处理,然后再传递给内核,这样用户就能间接地使用操作系统内核了。你看,在用户和内核之间增加一层“代理”,既能简化用户的操作,又能保障内核的安全,何乐不为呢?
用户界面和命令行就是这个另外开发的程序,就是这层“代理”。在Linux下,这个命令行程序叫做 Shell。
简单来说: Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。
Shell 是如何连接用户和内核的?
Shell 能够接收用户输入的命令,并对命令进行处理,处理完毕后再将结果反馈给用户,比如输出到显示器、写入到文件等,这就是大部分读者对 Shell 的认知。你看,我一直都在使用 Shell,哪有使用内核哦?我也没有看到 Shell 将我和内核连接起来呀?!
其实,Shell 程序本身的功能是很弱的,比如文件操作、输入输出、进程管理等都得依赖内核。我们运行一个命令,大部分情况下 Shell 都会去调用内核暴露出来的接口,这就是在使用内核,只是这个过程被 Shell 隐藏了起来,它自己在背后默默进行,我们看不到而已。
接口其实就是一个一个的函数,使用内核就是调用这些函数。这就是使用内核的全部内容了吗?嗯,是的!除了函数,你没有别的途径使用内核。
比如,我们都知道在 Shell 中输入cat log.txt
命令就可以查看 log.txt 文件中的内容,然而,log.txt 放在磁盘的哪个位置?分成了几个数据块?在哪里开始?在哪里终止?如何操作探头读取它?这些底层细节 Shell 统统不知道的,它只能去调用内核提供的 open() 和 read() 函数,告诉内核我要读取 log.txt 文件,请帮助我,然后内核就乖乖地按照 Shell 的吩咐去读取文件了,并将读取到的文件内容交给 Shell,最后再由 Shell 呈现给用户(其实呈现到显示器上还得依赖内核)。整个过程中 Shell 就是一个“中间商”,它在用户和内核之间“倒卖”数据,只是用户不知道罢了。
Shell 还能连接其它程序
在 Shell 中输入的命令,有一部分是 Shell 本身自带的,这叫做内置命令;有一部分是其它的应用程序(一个程序就是一个命令),这叫做外部命令。
Shell 本身支持的命令并不多,功能也有限,但是 Shell 可以调用其他的程序,每个程序就是一个命令,这使得 Shell 命令的数量可以无限扩展,其结果就是 Shell 的功能非常强大,完全能够胜任 Linux 的日常管理工作,如文本或字符串检索、文件的查找或创建、大规模软件的自动部署、更改系统设置、监控服务器性能、发送报警邮件、抓取网页内容、压缩文件等。
更加惊讶的是,Shell 还可以让多个外部程序发生连接,在它们之间很方便地传递数据,也就是把一个程序的输出结果传递给另一个程序作为输入。
大家所说的 Shell 强大,并不是 Shell 本身功能丰富,而是它擅长使用和组织其他的程序。Shell 就是一个领导者,这正是 Shell 的魅力所在。
可以将 Shell 在整个 Linux 系统中的地位描述成下图所示的样子。注意“用户”和“其它应用程序”是通过虚线连接的,因为用户启动 Linux 后直接面对的是 Shell,通过 Shell 才能运行其它的应用程序。
Shell 也支持编程
Shell 是一种脚本语言
任何代码最终都要被“翻译”成二进制的形式才能在计算机中执行。
有的编程语言,如 C/C++、Pascal、Go语言、汇编等,必须在程序运行之前将所有代码都翻译成二进制形式,也就是生成可执行文件,用户拿到的是最终生成的可执行文件,看不到源码。
这个过程叫做编译(Compile),这样的编程语言叫做编译型语言,完成编译过程的软件叫做编译器(Compiler)。
而有的编程语言,如 Shell、JavaScript、Python、PHP等,需要一边执行一边翻译,不会生成任何可执行文件,用户必须拿到源码才能运行程序。程序运行后会即时翻译,翻译完一部分执行一部分,不用等到所有代码都翻译完。
这个过程叫做解释,这样的编程语言叫做解释型语言或者脚本语言(Script),完成解释过程的软件叫做解释器。
编译型语言的优点是执行速度快、对硬件要求低、保密性好,适合开发操作系统、大型应用程序、数据库等。
脚本语言的优点是使用灵活、部署容易、跨平台性好,非常适合 Web 开发以及小工具的制作。
Shell 就是一种脚本语言,我们编写完源码后不用编译,直接运行源码即可。
shell使用
Shell 命令的基本格式如下:
command [选项] [参数]
短格式选项和长格式选项
Linux 的选项又分为短格式选项和长格式选项。
短格式选项是长格式选项的简写,用一个减号
-
和一个字母表示,例如ls -l
。长格式选项是完整的英文单词,用两个减号
--
和一个单词表示,例如ls --all
。一般情况下,短格式选项是长格式选项的缩写,也就是一个短格式选项会有对应的长格式选项。当然也有例外,比如 ls 命令的短格式选项
-l
就没有对应的长格式选项,所以具体的命令选项还需要通过帮助手册来查询。
使用参数
参数是命令的操作对象,一般情况下,文件、目录、用户和进程等都可以作为参数被命令操作。例如:
1 | [mozhiyan@localhost demo]$ ls -l main.c |
但是为什么一开始 ls 命令可以省略参数?那是因为有默认参数。命令一般都需要加入参数,用于指定命令操作的对象是谁。
Linux Shell命令提示符
不同的 Linux 发行版使用的提示符格式大同小异,例如在 CentOS 中,默认的提示符类似下面这样:
[mozhiyan@localhost ~]$
各个部分的含义如下:
[]
是提示符的分隔符号,没有特殊含义。mozhiyan
表示当前登录的用户,我现在使用的是 mozhiyan 用户登录。@
是分隔符号,没有特殊含义。localhost
表示当前系统的简写主机名(完整主机名是 localhost.localdomain)。~
代表用户当前所在的目录为主目录(home 目录)。如果用户当前位于主目录下的 bin 目录中,那么这里显示的就是bin
。$
是命令提示符。Linux 用这个符号标识登录的用户权限等级:如果是超级用户(root 用户),提示符就是#
;如果是普通用户,提示符就是$
。
第二层命令提示符
有些命令不能在一行内输入完成,需要换行,这个时候就会看到第二层命令提示符。第二层命令提示符默认为>
1 | [mozhiyan@localhost ~]$ echo "Shell教程" |
第一个 echo 命令在一行内输入完成,不会出现第二层提示符。第二个 echo 命令需要多行才能输入完成,提示符>
echo 命令用来输出一个字符串。字符串是一组由包围起来的字符序列,echo 将第一个作为字符串的开端,将第二个作为字符串的结尾。对于第二个 echo 命令,我们将字符串分成多行,echo 遇到第一个认为是不完整的字符串,所以会继续等待用户输入,直到遇见第二个。
简单编程
1 | 几乎所有编程语言的教程都是从使用著名的“Hello World”开始的,出于对这种传统的尊重(或者说落入俗套),我们的第一个 Shell 脚本也输出“Hello World”。 |
下面给出了一段稍微复杂的 Shell 脚本:
1 | !/bin/bash |
第 5 行中表示从终端读取用户输入的数据,并赋值给 PERSON 变量。read 命令用来从标准输入文件(Standard Input,stdin,一般就是指键盘)读取用户输入的数据。
第 6 行表示输出变量 PERSON 的内容。注意在变量名前边要加上$,否则变量名会作为字符串的一部分处理。
在新进程中运行 Shell 脚本
在新进程中运行 Shell 脚本有多种方法。
1) 将 Shell 脚本作为程序运行
Shell 脚本也是一种解释执行的程序,可以在终端直接调用(需要使用 chmod 命令给 Shell 脚本加上执行权限),如下所示:
1 | [mozhiyan@localhost ~]$ cd demo #切换到 test.sh 所在的目录 |
第 2 行中,chmod +x
表示给 test.sh 增加执行权限。
第 3 行中,./
表示当前目录,整条命令的意思是执行当前目录下的 test.sh 脚本。如果不写./
,Linux 会到系统路径(由 PATH 环境变量指定)下查找 test.sh,而系统路径下显然不存在这个脚本,所以会执行失败。
通过这种方式运行脚本,脚本文件第一行的#!/bin/bash
一定要写对,好让系统查找到正确的解释器。
在当前进程中运行 Shell 脚本
这里需要引入一个新的命令——source 命令。source 是 Shell 内置命令 的一种,它会读取脚本文件中的代码,并依次执行所有语句。你也可以理解为,source 命令会强制执行脚本文件中的全部命令,而忽略脚本文件的权限。
source 命令的用法为:
source filename
也可以简写为:
. filename
两种写法的效果相同。对于第二种写法,注意点号.