> For the complete documentation index, see [llms.txt](https://3ric.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://3ric.gitbook.io/blog/linux-command-line-and-shell-scripting-bible.md).

# Linux Command Line and Shell Scripting Bible

## 初识 Lniux Shell

### Linux 的组成

#### Linux 的内核

Linux 内核主要负责：系统内存管理、软件程序管理、硬件设备管理、文件系统管理。

内核内存管理不仅管理物理内存，还要管理虚拟内存。

内核控制着 Linux 系统如何管理运行在系统上的所有进程，内核创建了第一个进程 init 来启动系统上所有其他进程。

内核管理硬件设备。任何需要与 Linux 系统进行通讯的设备都需要在 Linux 内核代码中加入其驱动程序代码。

驱动代码可以编译进内核，也可以将驱动代码模块插入内核而无需重新编译。Linux 下一切皆文件，硬件设备也被当作特殊的文件，设备文件分为每次处理一个字符的字符型设备文件，每次处理大块数据的块设备文件，以及数据包接发的网络设备文件。

内核采用虚拟文件系统作为和每个文件系统交互的接口。

#### GUN 工具

GUN 工具是 GNU 组织开发的一套完整的 Unix 工具。

其核心 GNU 工具包括处理文件、操作文本、管理进程的工具。

Shell 是一种特殊的交互式工具，Shell 的核心是命令行提示符，他允许你输入文本命令，解释命令并在内核中执行。

#### 图形化桌面程序

X windows 系统是图形显示的核心部分，他是与显卡和显示器打交道的底层程序，目前最流行的软件包是 X.org 。

KDE 是 K Desktop Environment 桌面环境，GNOME 是 GNU Network Object Model Environement 。

### 基本的 bash shell 命令

#### 帮助指令

Man

> `man man`
>
> **man** is the system's manual pager. Each page argument given to **man** is normally the name of a program, utility or function. The manual page associated with each of these arguments is then found and displayed. A section, if provided, will direct **man** to look only in that section of the manual. The default action is to search in all of the available sections following a pre-defined order (see **DEFAULTS**), and to show only the first page found, even if page exists in several sections.

Info

> `info info`
>
> The "Info" program described here is a stand-alone program, part of the Texinfo distribution, which is used to view Info files on a text terminal. "Info files" are typically the result of processing Texinfo files with the program 'makeinfo' (also in the Texinfo distribution).

#### 文件系统

常见的 Linux 目录结构。

* **/bin**：bin是Binary的缩写, 这个目录存放着最经常使用的命令。
* **/boot：**&#x8FD9;里存放的是启动Linux时使用的一些核心文件，包括一些连接文件以及镜像文件。
* **/dev ：**&#x64;ev是Device(设备)的缩写, 该目录下存放的是Linux的外部设备，在Linux中访问设备的方式和访问文件的方式是相同的。
* *\*/etc：*&#x8FD9;个目录用来存放所有的系统管理所需要的配置文件和子目录。
* **/home**：用户的主目录，在Linux中，每个用户都有一个自己的目录，一般该目录名是以用户的账号命名的。
* **/lib**：这个目录里存放着系统最基本的动态连接共享库，其作用类似于Windows里的DLL文件。几乎所有的应用程序都需要用到这些共享库。
* **/lost+found**：这个目录一般情况下是空的，当系统非法关机后，这里就存放了一些文件。
* **/media**：linux 系统会自动识别一些设备，例如U盘、光驱等等，当识别后，linux会把识别的设备挂载到这个目录下。
* **/mnt**：系统提供该目录是为了让用户临时挂载别的文件系统的，我们可以将光驱挂载在/mnt/上，然后进入该目录就可以查看光驱里的内容了。
* **/opt**：这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。
* **/proc**：这个目录是一个虚拟的目录，它是系统内存的映射，我们可以通过直接访问这个目录来获取系统信息。这个目录的内容不在硬盘上而是在内存里，我们也可以直接修改里面的某些文件，比如可以通过下面的命令来屏蔽主机的ping命令，使别人无法ping你的机器：

  ```
  echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
  ```
* **/root**：该目录为系统管理员，也称作超级权限者的用户主目录。
* **/sbin**：s就是Super User的意思，这里存放的是系统管理员使用的系统管理程序。
* **/srv**：该目录存放一些服务启动之后需要提取的数据。
* **/sys**：这是linux2.6内核的一个很大的变化。该目录下安装了2.6内核中新出现的一个文件系统 sysfs 。

  sysfs文件系统集成了下面3种文件系统的信息：针对进程信息的proc文件系统、针对设备的devfs文件系统以及针对伪终端的devpts文件系统。

  该文件系统是内核设备树的一个直观反映。

  当一个内核对象被创建的时候，对应的文件和目录也在内核对象子系统中被创建。
* **/tmp**：这个目录是用来存放一些临时文件的。
* **/usr**：这是一个非常重要的目录，用户的很多应用程序和文件都放在这个目录下，类似于windows下的program files目录。
* **/usr/bin：**&#x7CFB;统用户使用的应用程序。
* **/usr/sbin：**&#x8D85;级用户使用的比较高级的管理程序和系统守护程序。
* **/usr/src：**&#x5185;核源代码默认的放置目录。
* **/var**：这个目录中存放着在不断扩充着的东西，我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。
* **/run**：是一个临时文件系统，存储系统启动以来的信息。当系统重启时，这个目录下的文件应该被删掉或清除。如果你的系统上有 /var/run 目录，应该让它指向 run。

#### 浏览文件系统

ls - 列表命令

`ls -F`可以在目录后显示`/`，以区分文件和目录。

`ls -a`显示隐藏文件。

`ls -l`可以显示附加信息，效果等同于`ll`。使用 `ls -l *sh`可以进行正则匹配过滤要现实的内容。

tree - 树形目录 / **需要安装\***

`tree dir`展示该目录的树形目录结构。

cp - 复制命令

`cp -i source destination`使用 `-i`可以强制 shell 询问是否覆盖存在的文件。

`cp -r source/ destination`递归复制文件夹。

ln - 链接文件

`ln -s source destfilename`创建一个符号链接，符号连接可理解为 Windows 下的快捷方式，请勿为符号连接创建符号链接，复制后的符号链接还是一个符号链接。

`ln source destfilename`创建一个硬链接，硬链接理解为源文件的另一个指针，注意，此时复制硬链接其实得到的不是硬链接的副本，而是直接复制了源文件。

mv - 移动文件或重命名

`mv -i source dest`使用 `-i`可以强制 shell 询问是否覆盖存在的文件。

mkdir - 创建文件夹

`mkdir -p New_Dir`递归创建文件夹。

rm - 删除文件或文件夹

`rmdir Empty_Dir`只能删除空文件夹。

`rm -ri dir`递归删除文件夹，询问。

`rm -rf`危险的命令！

file - 查看文件信息

`file my_file`

cat - 显示文本文件

`cat -n file`每一行添加行号并显示。

`cat -b file`有文本的每一行添加行号并显示。

sort - 排序显示

`sort file`字典序排序

`sort -n file`把数字当作数值排序

`sort -M file`当作日期排序

`sort -nr`降序输出

more - 分页显示

`more file`

less -less is more - 高级分页显示

`less file`支持上下左右以及分页键。

tail/head - 显示文件尾部/头部

`tail -fn 10 log`显示 log 文件后 10 行，并且实时显示更新内容。

### 更多的的 bash shell 命令

ps - 显示进程

`ps -A`显示所有进程。

`ps -u root`显示指定用户进程。

`ps -ef`显示所有进程，以及详细信息。

`ps -xf`显示进程树。

top - 实时进程监测

使用 f 和 s 修改排序方式，使用 d 修改刷新间隔，使用 q 退出。

kill - 给进程发送信号

`kill -s KILL 7890`

mount - 挂载储存媒体

umount - 卸载储存媒体

df - 查看挂载磁盘使用情况

`df -h`用M和G作为单位

du - 显示特定目录下使用情况

`du -h`用M和G作为单位

`du -sh`显示标准单位的总计

grep - 搜索数据

`grep str file`在文件中搜索字符串

`grep -n str file`在文件中搜索字符串并显示所在行号

`grep -c str file`在文件中搜索字符串并统计个数

### 理解Shell

`/etc/passwd`设置默认的Shell类型

* 命令列表

`(pwd;(echo $BASH_SUBSHELL))` 使用小括号创建命令列表，命令列表就是使用括号包围起来的一组命令，它能够创建出子shell来执行这些命令。通过`echo $BASH_SUBSHELL`环境变量可以看出当前子shell层数。

* 后台命令列表

命令 `sleep 10` 会将会话暂停10秒钟，然后返回shell CLI提示符，使用 & 符号将该子 shell 置入后台，让出 CLI ，通过 `jobs -l` 获取更多后台任务信息。同样的，我们可以在命令列表尾部加入 `(sleep 2;echo $BASH_SUBSHELL;sleep 2)&` 实现命令列表的后台运行，常用的有后台进行文件压缩。

* 协程 - coproc

`coproc My_Job { sleep 10; }` 创建协程名字 My\_Job，执行命令 sleep 10。

只有在拥有多个协 程的时候才需要对协程进行命名，因为你得和它们进行通信。否则的话，让 coproc 命令 将其设置成默认的名字 COPROC 就行了。

`coproc ( sleep 10; sleep 2 )`嵌套子进程。

* 内建命令

外部命令，有时候也被称为文件系统命令，是存在于bash shell之外的程序。通过 `type -a command` 获得命令详细信息，当外部命令执行时，会创建出一个子进程。这种操作被称为衍生（forking）。当进程必须执行衍生操作时，它需要花费时间和精力来设置新子进程的环境。所以说，外部 命令多少还是有代价的。

history - 历史命令

命令历史记录被保存在隐藏文件.bash\_history中，它位于用户的主目录中。

bash命令的历史记录是先存放在内存中，当shell退出时才被写入到历史文件中。

可以在退出shell会话之前使用 `history -a` 强制将命令历史记录写入.bash\_history文件。

.bash\_history文件只有在打开首个终端会话时才会被读取。要想强制重新读 取.bash\_history文件，更新终端会话的历史记录，可以使用 `history -n` 命令。

* alias - 命令别名

`ls='ls --color=auto` 输入 `ls` 即等于 `ls --color=auto`

在定义好别名之后，你随时都可以在shell中使用它，就算在shell脚本中也没问题。要注意， 因为命令别名属于内部命令，一个别名仅在它所被定义的shell进程中才有效。

### 环境变量

环境变量储存在内存中，分为全局变量和环境变量，全局环境变量对于shell会话和所有生成的子shell都是可见的，而局部变量则只对创建它们的 shell可见。

`printenv`可以显示当前的环境变量。

`printenv HOME`等价于`env HOME`等价于`echo $HOME`显示全局变量HOME。

所有的**环境变量名**均使用**大写字母**，这是bash shell的标准惯例。 如果是你自己创建的**局部变量**或是shell脚本，请使用**小写字母**。变量名区分大小写。在涉及用户定义的局部变量 时坚持使用小写字母，这能够避免重新定义系统环境变量可能带来的灾难。

export - 导出环境变量

修改子shell中全局环境变量并不会影响到父shell中该变量的值。

子shell甚至无法使用export命令改变父shell中全局环境变量的值。

unset - 删除环境变量

PATH - 外部命令搜索目录

当你在shell命令行界面中输入一个外部命令时，shell必须搜索系统来找到对应 的程序。

在大多数发行版中，存储个人用户永久性bash shell变量的地方是$HOME/.bashrc文件。

数组变量

环境变量有一个很酷的特性就是，它们可作为数组使用。数组是能够存储多个值的变量。这 些值可以单独引用，也可以作为整个数组来引用。

`mytest=(one two three four five)`

`echo $mytest 1`

`echo ${mytest[2]} three`

`echo ${mytest[*]} one two three four five`

`unset mytest[2]`注意，此时的三号位并不会被后面的数据补齐，而是空。

### Linux 文件权限

useradd - 添加用户

默认情况下，useradd命令不会创建HOME目录，但是-m命令行选项会使其创建HOME目录，并将/etc/skel目录中的文件复制了过来。

userdel - 删除用户

默认情况下，userdel命令会只 删除/etc/passwd文件中的用户信息，而不会删除系统中属于该账户的任何文件。

如果加上-r参数，userdel会删除用户的HOME目录以及邮件目录。

usermod - 修改用户

除此之外，还有另外 一些可能派上用场的选项。 

`usermod -l`修改用户账户的登录名。 

`usermod -L`锁定账户，使用户无法登录。 

`usermod -p`修改账户的密码。 

`usermod -U`解除锁定，使用户能够登录。

usermod -L选项尤其实用。它可以将账户锁定，使用户无法登录，同时无需删除账户和用户的数据。 要让账户恢复正常，只要用-U选项就行了。

passwd chpasswd - 修改密码

如果只用passwd命令，它会改你自己的密码。系统上的任何用户都能改自己的密码，但只 有root用户才有权限改别人的密码。

-e选项能强制用户下次登录时修改密码。你可以先给用户设置一个简单的密码，之后再强制 在下次登录时改成他们能记住的更复杂的密码。

chpasswd命令能从 标准输入自动读取登录名和密码对（由冒号分割）列表，给密码加密，然后为用户账户设置。你 也可以用重定向命令来将含有userid:passwd对的文件重定向给该命令。

`chpasswd < users.txt`

文件权限符

这个字段的第一个字符代表了对象 的类型：-代表文件、d代表目录、l代表链接、c代表字符型设备、b代表块设备、n代表网络设备。

之后有3组三字符的编码。每一组定义了3种访问权限：r代表对象是可读的、w代表对象是可写的、x代表对象是可执行的。这三组分别是属主、属组、任意用户权限。

chmod - 改变文件或目录的安全设置

chown - 改变文件或目录的属主

chgrp - 改变文件或目录的属组

### 管理文件系统

文件系统日志方法

* 数据模式：索引节点和文件都会被写入日志；丢失数据风险低，但性能差
* 有序模式：在顺序模式中，只有元数据被记录到日志中，但在日志被标记为提交前，数据会被写入文件系统。在这种模式下，如果在增大文件时，数据还未写入就发生崩溃，那么在恢复时这个事务会被简单的撤销，文件保持原来的状态；在性能和安全性之间取得了良好的折中
* 回写模式：在回写模式中，只有元数据被记录到日志中，数据会被直接写入主文件系统。这种模式能提供较好的性能，不过有较大的风险。例如：在增大文件时，数据还未写入就发生崩溃，那么文件系统恢复后，文件后面就可能出现垃圾数据。

写时复制（copy-on-write，COW）

如果要修改数据，会使用克隆或可写快照。修改过的数据 并不会直接覆盖当前数据，而是被放入文件系统中的另一个位置上。即便是数据修改已经完成， 之前的旧数据也不会被重写。

### 构建基础脚本

在创建shell脚本文件时，必须在文件的第一行指定要使用的shell。其格式为：

`#!/bin/bash`

变量

`value1=10 value2=$value1`给value2赋value1的值。

命令替换

反引号字符（\`）

将命令输出赋值给变量 `testing='date'`。

命令替换是由子shell完成的，由该子shell所执行命令是无法使用脚本中所创建的变量的，如果你使用的是内建的shell命令，并不会涉及子shell。

重定向输入和输出

`>`覆盖输出到目标文件

`>>`追加数据

管道（|）

Linux系统实际上会同时运行管道两端的命令，在系统内部将它们连接起来。在第一个命令产生输出的同时，输出会被立即送给第二个命令。数据 传输不会用到任何中间文件或缓冲区。

数学运算（$\[ operation ]）

bash shell数学运算符只支持整数运算。要使用浮点运算就要使用 bc 即 bash shell 内建的计算器。

### 使用结构化命令

```
if command
then 
    commands
else
    commends
fi
```

bash shell的if语句会运行if后面的那个命令。如果该命令的退出状态码是0 （该命令成功运行），位于then部分的命令就会被执行，fi语句用来表示if-then 语句到此结束。

```
if command1
then
    command
elif command2
then
    command
elif command3 
then
    command
elif command4 
then
    command
fi
```

在 elif 语句中， 紧跟其后的 else 语句属于 elif 代码块。 它们并不属于之前的 if-then 代码块。

```
if test condition 
then 
    commands 
fi
```

如果不写test命令的condition部分，它会以非零的退出状态码退出，并执行else语句块。

```
if [ $value1 -eq $value2 ]
```

数值比较方法，注意 bash shell 无法处理浮点数。

```
if [ $str1 \> $str2 ]
```

字符串使用大小于号比较必须转义，否则会被识别为重定向语句。

在比较测试中，大写字母被认为是小于小写字母的。但sort命令恰好相反。当你将同样的 字符串放进文件中并用sort命令排序时，小写字母会先出现。这是由各个命令使用的排序技术 不同造成的。

```
if [ -d $directory ]
```

检测目录是否存在。

```
if [ -f $file_name ]
```

检查文件是否存在。

```
if [ -s $file_name ]
```

检查文件是否有数据。

```
if [ -w $file_name ]
```

检查文件是否可写。

```
if [ -x $file_name ]
```

检查文件是否可执行。

```
if (( expression ))
```

数学表达式支持。

```
if [[ expression ]]
```

正则表达式支持。

```
case variable in 
pattern1 | pattern2) commands1;; 
pattern3) commands2;; 
*) default commands;; 
esac
```

case 语句支持。

### 更多的结构化命令

```
for var in list 
do 
    commands
done
```

按空格遍历字符串。

```
list="Alabama Alaska Arizona Arkansas Colorado" list=$list" Connecticut"
```

拼接字符串。

### 用户输入

bash shell会将一些称为位置参数（positional parameter）的特殊变量分配给输入到命令行中的 所有参数。这也包括shell所执行的脚本名称。位置参数变量是标准的数字：`$0`是程序名，`$1`是第一个参数，`$2`是第二个参数，依次类推，直到第九个参数`$9`。在第9个变量之后，你必须在变量数字周围加上花括号，比如`${10}`。

basename命令会返回不包含路径的脚本名。

特殊变量`$#`含有脚本运行时携带的命令行参数的个数。

通过使用for命令遍历这两个特殊变量，你能看到它们是如何不同地处理命令行参数的。`$*`变量会将所有参数当成单个参数，而`$@`变量会单独处理每个参数。这是遍历命令行参数的一个绝妙方法。

## 引用

* [Linux 系统目录结构](https://www.runoob.com/linux/linux-system-contents.html)

## [许可协议](/blog/license.md)

{% hint style="info" %}
本作品采用[知识共享署名 4.0 国际许可协议(CC BY-NC-SA 4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/)进行许可，转载时请注明原文链接，图片在使用时请保留全部内容，可适当缩放并在引用处附上图片所在的文章链接。
{% endhint %}
