一、shell 命令及其运行原理
Linux严格意义上来说是一个操作系统,我们称之为 “核心 (kernel)”,即Linux内核;但是由于我们一般人并不善于与 kernel 打交道,所以我们不直接使用 kernel,而是通过 kernel 的 “外壳” 程序,也就是所谓的 shell 来与 kernel 进行沟通;
从技术角度来说,shell 最简单的定义为 “命令行解释器 (command Interpreter)”,其功能主要包括两方面:
- 将使用者的命令翻译传递给 kernel 处理;
- 将 kernel 的处理结果翻译返回给使用者;
以我们熟悉的 Windows 来说,我们操作 Windows 并不是直接操作 Windows 内核,而是操作 Windows GUI (图形化界面) 提供的接口,比如我们双击运行一个 .exe 程序或者最大化/最小化一个窗口,Windows GUI 会将我们进行的这些操作翻译为 Windows 内核所能理解的指令,然后传递给 Windows 内核进行处理;当 Windows kernel 处理完毕后,GUI 又会将处理结果翻译为我们用户能够看懂的操作,这样我们与 Windows kernel 的交互成本就会被大大降低。
shell 对于 Linux 有同样的作用,主要是对我们的指令进行解析并传递给 Linux kernel,然后再将处理结果解析给用户;
同时,shell 还在变相的保护操作系统 – shell 会对用户传递的指令进行解析,如果它解析到非法指令,那么它就会直接抛出错误,并不会将错误指令传递给 kernel,这样使得 kernel 只用处理合法的指令,一定程度上保护了操作系统。
另外,为了防止用户的非法指令导致 shell 自身崩溃,shell 本身一般都不会去执行对应的指令,而是会派生子进程来执行用户得到指令,关于多进程的知识我们后面再仔细学习;
所以,shell 和 GUI 一样,本质上都是 kernel 外面的一层软件层 (外壳程序),用于连接用户与 kernel。
为了更好的理解 kernel 与 shell 的关系,下面我们举一个有趣的例子:
假设你是一个优秀的程序员,你家庭条件也很不错 – 你爸是村长,你家有两套房、一辆车,你自己的年薪也是30W+;你喜欢上了村里的另一个姑凉如花,但是呢你十分内向,不敢向如花表明你的心意;
但是你们村中有一个十分厉害的媒婆 – 王婆,王婆在你们村方圆十里都是最厉害的媒婆,只要它出马基本上就没有谈不成的;所以你找上了王婆,让她去向如花说媒,王婆也去了;
不幸的是,如花表示你是一个好人,但是你们不适合,并且如花已经和村里的另一个高富帅订婚了,再过一年就结婚;
但是你是一个十分专一的人,非如花不可,于是你还是三番五次的找王婆去给你再说一说;王婆试了几次之后发现这媒完全不可能,于是后面直接不鸟你了,也不再去找如花;
你看王婆直接不鸟你了,于是跟王婆说:“我爸是村长,王婆您再去给我说说吧。”王婆看这事不办不行,但是如果继续三番五次去说媒,自己的口碑也没了;
最后,王婆想了一个办法 – 王婆收了十几个徒弟,然后每次你找王婆,王婆都找一些冠冕堂皇的借口说自己没空,然后派徒弟去找如花,这样即使失败了也可以说是因为徒弟萌新太菜了;这样即不得罪村长,也保住了自己的口碑。
在上面的例子中,王婆就相当于 shell,用来传递你 (用户) 的信息 (指令) 给如花 (kernel),然后将如花的信息反馈给你;同时,王婆拒绝多次去说媒,相当于 shell 拦截了非法指令,变相的保护了 kernel;最后,为了保证自己的口碑,王婆安排自己的徒弟去说媒,相当于 shell 为了防止自身崩溃,所以自己不会直接去执行指令,而是派生子进程去执行。
shell 是 “外壳程序” 的统称,不同 Linux 版本对应的 shell 程序可能不同,其中 bash 是标准的 GNU shell,所以它成为了所有 Linux 发行版上的标准 shell。 现在大多数现代 Linux 发行版都默认提供 Bash shell 。 对应上面的例子来说,shell 是媒婆,而 bash 是王婆。
二、Linux 权限的概念
1、用户分类
Linux下有两种用户:超级用户 (root) 与 普通用户
- 超级用户:可以再linux系统下做任何事情,几乎不受权限的限制;
- 普通用户:在linux下做权限范围内的事情;
- 超级用户的命令提示符是“#”,普通用户的命令提示符是“$”;
2、切换用户
Linux 切换用户的指令是:su;
在 Linux 中,如果我们要从超级用户切换到普通用户,直接 “su username” 即可,没有任何限制:
但是如果我们要从普通用户切换到超级用户,就需要输入 root 密码;普通用户切换到 root 用户有两种方式:
- su:切换到 root 的当前目录;
- su -:直接切换到 root 的根目录;
注:Linux 出于安全考虑,从键盘输入的密码在终端上是不会回显的,也就是说我们输入密码时屏幕不会显示任何东西;
3、用户提权
在工作中我们使用Linux的时候可能会出现这样一种状况:当前有一条指令必须拥有超级用户的权限才能执行,但我们不想切换用户,想直接让普通用户以root用户的身份去执行该指令;
sudo 指令可以满足我们的需求:
我们执行 sudo 指令发现了两个奇怪的现象:
1、使用 sudo 指令让我们拥有root用户的权限,按理来说应该提示我们输入root密码,但事实是它让输入我们自己用户的密码;
2、我以 thj 的身份执行 sudo 指令,它提示 thj 用户不在 sudoers file 内,sudo 失败;
上面这些现象是由 sudo 的机制造成的:sudo 存在的目的是为了 给受信任的用户提供最少的执行障碍,受信任的用户是指被添加到 sudoers 文件中的用户;也就是说,当我们存在于 sudoers 文件中时,说明 root 用户充分信任我们,所以当我们使用 sudo 指令提升权限时只需要输入自己的密码即可;而如果我们不在 sudoers 文件中,那么自然也不能通过 sudo 指令提权;
注:sudo 的有限时间为15分钟,即当我们使用 sudo 提权成功后,后面15分钟内再次使用 sudo 指令不需要再次输入密码;
三、Linux 权限管理
1、文件访问者的分类
在 Linux 下,文件的访问者被分为三类:
- 所有者:文件和文件目录的所有者 – u (user);
- 所属组:文件和文件目录的所有者所在的组的用户 – g (group);
- 其他用户:除所有者和所属组用户之外的用户 – o (other);
关于文件的所有者和其他用户相信大家都很容易理解,但是为什么会有一个所属组呢?我们以一个例子说明:
假设一家刚起步的很穷的公司要开发一款产品,该公司的老板奉行竞争理念,所以他把手下的人分为了两个组,让他们来研发同一款产品,最后哪个组的产品更优哪个组就拿额外的奖金;
但是呢由于公司很穷,只买得起一台主机,这时候问题就出现了:两个组的成员公用一台主机,那么组内成员的代码如何进行共享呢?
如果我把文件设置为私有,这样虽然另外一组的人看不到我的代码,但同时我的队友也看不到;而如果我把文件设置为共有,那么又可能有代码泄露的风险;
为了解决这种情况,Linux 设计出了所属组的概念,我们可以把多个用户归为一个组,让组内的成员共享文件,组外的成员则受权限限制;
2、文件类型和访问权限
我们在 Linux 中查看一个文件/目录时,文件前面通常会出现很长一串字符:
他们对应的含义如下:
第一个字符代表文件类型,后面九个字符三三一组分别代表文件拥有者、所属组和其他用户的权限,其中文件的权限一共分为三类:
r – 读权限:Read 对文件而言,具有读取文件内容的权限;对目录来说,具有浏览该目录信息的权限;
w – 写权限:Write 对文件而言,具有修改文件内容的权限;对目录来说具有删除移动目录内文件的权限;
x – 可执行权限:execute 对文件而言,具有执行文件的权限;对目录来说,具有进入目录的权限;
其中对应位置上有代表权限的字符说明该用户拥有对应权限,如果显示 - 则代表该用户没有对应权限;
文件类型
Liunx 中一共有七种文件:
- d:目录;
- -:普通文件 (可执行程序也属于普通文件);
- l:软链接(类似Windows的快捷方式)
- b:块设备文件(例如硬盘、光驱等)
- p:管道文件
- c:字符设备文件(例如屏幕等串口设备)
- s:套接口文件
文件后缀与文件类型
在之前我们就提到,Linux 中不以文件的后缀名来区分文件类型,文件后缀属于文件名的一部分;但这并不不代表我们不可以使用文件后缀来区分文件类型 – 意思就是虽然在 Linux 眼中文件后缀没用,但是我们可以把文件后缀当作对用户的一个提示符号;
同时,Linux 中不区分文件后缀并不代表 Linux 下的各种工具,比如 gcc、g++ 不区分:
四、文件权限值的表示方法
1、字符表示法
2、八进制数值表示法
五、设置文件权限
1、chmod 指令
格式:chmod [参数] 权限 文件名;
功能:设置文件的访问权限;
常用选项
- -R 递归修改目录文件的权限;
注意:只有文件的拥有者和root才可以改变文件的权限;
chmod 指令权限值的格式
(1) 用户表示符 +/-/= 权限字符:
用户符号:
- u:拥有者;
- g:拥有者同组用户;
- o:其它用户;
- a:所有用户;
+/-/= 的意义: - +:向权限范围增加权限代号所表示的权限;
- -:向权限范围取消权限代号所表示的权限;
- =:向权限范围赋予权限代号所表示的权限;
指令演示
1、对单个用户 ± 权限:
2、对多个用户 ± 权限:
(2) 三位八进制数字
文件的权限一共三种 – 读写执行,所以用三个二进制位就可以表示全部情况,如果用八进制则只需要一位:
指令演示
2、chown 指令
格式:chown [参数] 用户名 文件名;
功能:修改文件的拥有者;
常用选项
- -R 处理指定目录以及其子目录下的所有文件;
注意:当我们使用 chown 指令将属于我们的文件赋给别人时,需要获得别人的同意,所以在一般情况下我们是不能改变文件的拥有者的;只有 root 不受权限的约束,可以直接将属于 A 的文件赋给 B,而不需要征得 A 和 B 的同意;
指令演示
3、chgrp 指令
格式:chgrp [参数] 用户组名 文件名;
功能:修改文件或目录的所属组;
常用选项
- -R 递归修改文件或目录的所属组;
注意:和修改文件的拥有者一样,我们也不能直接修改文件的所属组,需要征得别人的同意,而 root 不受权限约束;
指令演示
4、umask 指令
Linux下文件的默认权限如下 – 普通文件的默认权限是0666,目录文件的默认权限是0777,其中第一位数字代表这是三位八进制数,后面三位数字分别代表拥有者、所属组和其他人对文件的权限;但是我们发现,我们平时创建的文件的权限和上面的好像并不相同:
这是因为创建文件或目录的时候除了受默认权限的约束之外,还要受到 umask (文件掩码) 的影响;假设默认权限是mask,则实际创建的出来的文件权限是: mask & ~umask;
权限掩码的作用是将在其中出现的权限 在起始权限中全部去掉,因为一个权限在 umask 中为1,那么其按位取反后再与默认权限按位与得到的结果一定为0;如果 umask 中为0,则其按位取反后与默认权限按位与后不影响默认权限的值;
格式:umask 权限值;
功能:查看或修改文件掩码;
注意:将现有的存取权限减去权限掩码后,即可产生建立文件时预设权限;超级用户默认掩码值为0022,普通用户默认为0002;
指令演示
查看文件掩码:
修改文件掩码:可以看到,当我们把文件掩码设置为000后,此时创建出的文件的最终权限就等于文件的默认权限;
5、file 指令
格式:file [选项] 文件或目录;
功能:辨识文件类型;
常用选项
- c 详细显示指令执行过程,便于排错或分析程序执行的情形;
- -z 尝试去解读压缩文件的内容;
指令演示
六、目录的权限
目录权限所代表的含义如下:
- 可执行权限:能否进入目录 – 如果目录没有可执行权限, 则无法cd到目录中;
可读权限:能否查看目录中的文件内容 – 如果目录没有可读权限, 则无法用ls等命令查看目录中的文件内容;
可写权限:能否在目录中创建与删除文件 – 如果目录没有可写权限, 则无法在目录中创建文件, 也无法在目录中删除文件;
所以如果我们要进入一个目录,就必须有可执行权限;这也就是为什么普通文件的默认权限是666,而目录文件的默认权限是777的原因;
注意事项
目录的可执行权限是表示你可否在目录下执行命令;
如果目录没有可执行权限,则无法对目录执行任何命令,甚至无法 cd 进入目录, 即使目录仍然有读权限;(这个地方很容易犯错,认为有读权限就可以进入目录读取目录下的文件)
如果目录具有可执行权限,但没有读权限,则用户可以执行命令,可以 cd 进入目录,但由于没有目录的读权限,所以在目录下即使可以执行 ls 命令,但仍然没有权限读出目录下的文档;
七、粘滞位
在一个Linux系统中通常有很多用户,那么有时就会有这样一种需求 – 不同的用户需要在一个公共的目录下进行临时文件的增删查改,这个公共目录通常由 root 用户创建,然后将目录权限修改为777;
但是这里出现了一个问题:由于这个公共目录的权限是777,所以任何一个用户都可以删除此目录下的文件,无论该文件是否属于自己;如下
可以看到,对于 yzpq 来说,thj.txt 这个文件是不可读不可写且不可执行的,yzpq 也确实不能对它进行这些操作;但是 yzpq 却能够直接删除 thj.txt,因为 thj.txt 处于 tmp 这个目录之下,而这个目录对于 yzpq 是可读可写可执行的;
为了出来上面这种不科学的情况,Linux 引入了粘滞位的概念;
粘滞位是权限的一种特殊情况,它不影响不同用户在公共目录下的读写执行操作,但是它可以禁止不同用户之间互删文件;粘滞位设置的方式很简单,只需要在已有权限基础上加上 t 即可,并且添加时不用指定用户,Linux 会自动识别;
注:当一个目录被设置为 “粘滞位” 以后,该目录下的文件只能由文件的所有者或者 root 删除,其余用户不能删除。