关键词: Linux
Reference:TLCL (billie66.github.io)
图形用户界面(Graphical User Interface,GUI)让简单的任务更容易完成,而命令行界面(Command Line Interface,CLI)使完成复杂的任务成为可能。
《快乐的Linux命令行》一书面向现代 Linux 命令行用户,传授如何与命令行界面友好相处,如何工作?能做什么?使用命令行最好的方法是什么?
前提条件:
可使用 WSL,WSL安装记录
shell 是一个程序,能够接受命令并且传递给操作系统执行。Linux 发行版都提供一个名为 bash
的shell 程序。bash 全称为 Bourne Again Shell。
同时,终端(Terminal)也能访问 shell。启动终端时,提示如下:
1 | fingsinz@FingsinzStudio:~$ |
其解释如下:当前用户名@主机名:工作目录$
。
$
表示这个终端具有 root(超级管理员)权限;#
则表示当前权限是普通用户权限。可以尝试输入一些命令:
1 | fingsinz@FingsinzStudio:~$ abcd |
还有,可以通过 ↑
按键获取上次输入的命令,也可以通过 ←
或 →
按键移动光标从而编辑命令。
不要尝试在终端里使用 ctrl + c
和 ctrl + v
进行拷贝粘贴操作,这两个控制代码有着不同的含义。
现在尝试运行一些简单的命令:
date
,显示系统当前时间和日期。1 | fingsinz@FingsinzStudio:~$ date |
cal
,显示当前月份日历。1 | fingsinz@FingsinzStudio:~$ cal |
df
,查看磁盘剩余空间数量。1 | fingsinz@FingsinzStudio:~$ df |
free
,显示空闲内存的数量。1 | fingsinz@FingsinzStudio:~$ free |
exit
,关闭终端。在完全的 Linux 系统下,即使终端仿真器没有运行,在后台仍然有几个终端运行着。它们叫做虚拟终端或者是虚拟控制台。这些终端会话都可以通过按下 Ctrl + Alt + F1
到 Ctrl + Alt + F6
访问。当一个会话被访问的时候,它会显示登录提示框,需要输入用户名和密码。要从一个虚拟控制台转换到另一个,按下 Alt 和 F1-F6 (中的一个)。返回图形桌面,按下 Alt + F7
。
Linux 以分层目录结构来组织所有文件,将所有文件组成了一棵树型目录。文件系统中的第一级目录称为根目录(/)。根目录包含文件和子目录, 子目录包含更多的文件和子目录。
当首次登录系统(或者启动终端仿真器会话)后,当前工作目录是家目录(~)。每个用户都有他自己的家目录,当用户以普通用户的身份操控系统时,家目录是唯一允许用户写入文件的地方。
命令 ls
可以列出(一个或多个)目录(默认是工作目录)的内容:
1 | fingsinz@FingsinzStudio:~$ ls / |
命令 pwd
(print working directory)可以显示当前工作目录:
1 | fingsinz@FingsinzStudio:~$ pwd |
命令 cd
可以进入某个目录,把某个目录作为工作目录:
1 | fingsinz@FingsinzStudio:~$ cd / |
cd
的目录可用绝对路径或相对路径。
绝对路径是以根目录 /
开始。
相对路径中,.
表示当前工作目录,..
表示当前工作目录的上一级目录(父目录)。
cd
有一些快捷的输入:
cd
可以更改工作目录到家目录
cd -
可以更改工作目录到先前的工作目录。
cd ~user_name
可以更改工作目录到用户家目录。
关于文件名的重要规则
- 以 “.” 字符开头的文件名是隐藏文件。这仅表示,ls 命令不能列出它们,需要用 ls -a 命令。当创建帐号后,几个配置帐号的隐藏文件被放置在家目录下。另外,一些应用程序也会把它们的配置文件以隐藏文件的形式放在你的家目录下面。
- 文件名和命令名是大小写敏感的。文件名 “File1” 和 “file1” 是指两个不同的 文件名。
- Linux 没有“文件扩展名”的概念。可以用你喜欢的任何名字来给文件起名。文件内容或用途由其它方法来决定。虽然类 Unix 的操作系统,不用文件扩展名来决定文件的内容或用途,但是有些应用程序会。
- 虽然 Linux 支持长文件名,文件名可能包含空格,标点符号,但标点符号仅限“.”、“-”、下划线。最重要的是,不要在文件名中使用空格。如果你想表示词与词间的空格,用下划线字符来代替。
命令通常会有一些选项,选项通过一些参数设定,如:
1 | command -options arguments |
当不清楚命令的用法时,可以使用 man
进行查询,了解其功能以及参数,如:
1 | man ls |
对于 ls
比较常用的估计是 ls -l
:
1 | fingsinz@FingsinzStudio:/$ ls -l |
对于上面输出的第一行 drwxr-xr-x 2 root root 4096 Jul 9 12:04 bin
,含义依次如下:
drwxr-xr-x
表示文件访问权限。第一个字符表示文件类型,“-”表示普通文件,“d”表示目录;后三个字符是文件所有者的访问权限;再后三个字符是文件所属组中成员的访问权限,最后三个字符是其他所有人的访问权限。完整含义后面讨论。2
表示文件硬链接数目。root
表示文件所有者用户名。root
表示文件所属用户组名字。4096
表示文件字节数大小。Jul 9 12:06
表示上次修改文件的时间和日期。bin
表示文件名。命令 file
可以输出文件内容的简单描述。
1 | fingsinz@FingsinzStudio:~$ file man.png |
命令 less
可以查看文本文件。
1 | fingsinz@FingsinzStudio:~$ less hello.cpp |
less
运行起来后,如果文件内容多于一页,那么可以上下滚动文件。按下“q”键,退出 less
程序。下面还有一些常用的键盘命令:
命令 | 行为 |
---|---|
PageUp 或 b |
向上翻滚一页 |
PageDown 或 空格 |
向下翻滚一页 |
上箭头 | 向上翻滚一行 |
下箭头 | 向下翻滚一行 |
G |
移到最后一行 |
1G 或 g |
移动到开头1行 |
/字符 |
查找指定字符串 |
n |
向前查找下一个出现的字符串 |
h |
显示帮助 |
运行命令:
1 | fingsinz@FingsinzStudio:~$ ls -l /bin/ |
可以看到,ping4
和 ping6
都有 -> ping
,其含义是符号链接(也称为软链接或者 symlink )。在大多数“类Unix”系统中,有可能一个文件被多个文件名所指向。在这种链接下,当使用 ping4
时,实际上是使用 ping
。
还有一种链接类型,叫做硬链接。硬链接同样允许文件有多个名字,但是硬链接以不同的方法来创建多个文件名。更多见后面章节。
目录 | 含义 |
---|---|
/ |
根目录,一切起源 |
/bin |
包含系统启动和运行所必需的可执行二进制程序 |
/boot |
Linux 内核、初始 RAM 磁盘映像和启动加载程序 |
/dev |
设备结点目录,内核维护着所有设备的列表 |
/etc |
系统层面的配置文件 |
/home |
给每个用户在此分配家目录 |
/lib |
核心系统查询所使用的共享库文件,类似于 Windows 中的 DLL |
/lost+found |
当部分恢复一个损坏的文件系统时会用到 |
/media |
可移动介质挂载的地方 |
/mnt |
可移动介质(存储空间)挂载的地方 |
/opt |
用来安装可选的软件。这个主要用来存储可能安装在系统中的商业软件产品 |
/proc |
由内核维护的虚拟文件系统,它所包含的文件是内核的窥视孔 |
/root |
超级管理员的家目录 |
/sbin |
包含系统二进制文件,通常为超级用户保留。 |
/tmp |
存储由各种程序创建的临时文件的地方 |
/usr |
包含普通用户所需要的所有程序和文件 |
/usr/bin |
包含系统安装的可执行程序 |
/usr/lib |
包含由 /usr/bin 目录中的程序所用的共享库 |
/usr/local |
是非系统发行版自带程序的安装目录 |
/usr/sbin |
包含许多系统管理程序 |
/usr/share |
包含许多由 /usr/bin 目录中的程序使用的共享数据。 |
/usr/share/doc |
大多数安装在系统中的软件包会包含一些文档 |
/var |
存放动态文件。各种数据库,假脱机文件,用户邮件等等 |
/var/log |
包含日志文件、各种系统活动的记录 |
通配符,一些特殊字符帮助快速指定一组文件名。
通配符 | 意义 |
---|---|
* |
匹配任意多个字符(包括零个或一个) |
? |
匹配任意一个字符(不包括零个) |
[字符] |
匹配任意一个属于字符集中的字符 |
[!字符] |
匹配任意一个不是字符集中的字符 |
[[:class:]] |
匹配任意一个属于指定字符集类中的字符 |
字符类有:
字符类 | 意义 |
---|---|
[:alnum:] |
匹配任意一个字母或数字 |
[:alpha:] |
匹配任意一个字母 |
[:digit:] |
匹配任意一个数字 |
[:lower:] |
匹配任意一个小写字母 |
[:upper:] |
匹配任意一个大写字母 |
举一些例子:
*
:匹配所有文件。g*
:匹配文件名以“g”开头的文件。b*.txt
:匹配文件名以“b”开头,中间可以有零个或任意多个字符,且以“.txt”结尾的文件。Data???
:匹配以“Data”开头,其后接着3个字符的文件。[abc]*
:匹配文件名以“a”或“b”或“c”开头的文件。BACKUP.[0-9][0-9][0-9]
:匹配以“BACKUP.”开头,后接三个数字的文件。[[:upper:]]*
:匹配以大写字母开头的文件。*[[:lower:]123]
:匹配文件名以小写字母结尾,或以“1”、“2”、“3”结尾的文件。创建目录命令:
1 | mkdir directory... |
如:
1 | fingsinz@FingsinzStudio:~$ mkdir tests |
可以一次性创建多个目录。
复制文件或者目录命令:
1 | cp item1 item2 |
item1
到 文件或目录 item2
。1 | cp item... directory |
directory
中。cp
命令有一些常用选项:
选项 | 意义 |
---|---|
-a ,--archive |
复制文件和目录,以及它们的属性,包括所有权和权限。通常副本具有用户所操作文件的默认属性 |
-i ,--interactive |
重写已存在文件之前,提示用户确认。如果不指定该选项,则默认重写文件 |
-r ,--recursive |
递归复制目录以及目录中的内容 |
-u ,--update |
当把文件从一个目录复制到另一个目录时,仅复制目标目录中不存在的文件,或者是文件内容新于目标目录中已经存在 的文件 |
-v ,--verbose |
显示翔实的命令操作信息 |
举例:
cp file1 file2
:复制文件 file1
内容到文件 file2
。如果 file2
已经存在,file2
的内容会被 file1
的内容重写。如果 file2
不存在,则会创建 file2
。cp -i file1 file2
:这条命令和上面的命令一样,但是如果文件 file2
存在的话, 在文件 file2
被重写之前,会提示用户确认信息。cp file1 file2 dir1
:复制文件 file1
和文件 file2
到目录 dir1
。目录 dir1
必须存在。cp dir1/* dir2
:使用一个通配符,在目录 dir1
中的所有文件都被复制到目录 dir2
中。dir2
必须已经存在。cp -r dir1 dir2
:复制目录 dir1
中的内容到目录 dir2
。如果目录 dir2
不存在, 创建目录 dir2
,操作完成后,目录 dir2
中的内容和 dir1
中 的一样。如果目录 dir2
存在,则目录 dir1
(和目录中的内 容)将会被复制到 dir2
中。mv
命令可以执行文件的移动和文件的命名。使用与 cp
命令类似:
1 | mv item1 item2 |
item1
移动或重命名为 item2
。1 | mv item... directory |
directory
中。mv
命令有一些常用选项:
选项 | 意义 |
---|---|
-i ,--interactive |
重写已存在文件之前,提示用户确认。如果不指定该选项,则默认重写文件 |
-u ,--update |
当把文件从一个目录移动到另一个目录时,仅移动目标目录中不存在的文件,或者是文件内容新于目标目录中已经存在 的文件 |
-v ,--verbose |
显示翔实的命令操作信息 |
举例:
mv file1 file2
:移动文件 file1
到文件 file2
。如果 file2
已经存在,file2
的内容会被 file1
的内容重写。如果 file2
不存在,则会创建 file2
。之后 file1
不再存在。mv -i file1 file2
:这条命令和上面的命令一样,但是如果文件 file2
存在的话, 在文件 file2
被重写之前,会提示用户确认信息。mv file1 file2 dir1
:移动 file1
和 file2
到目录 dir1
。目录 dir1
必须存在。mv dir1 dir2
:如果 dir2
不存在则创建,并且移动目录 dir1
的内容到目录 dir2
中,同时删除目录 dir1
。如果 dir2
存在,则移动 dir1
以及它的内容到目录 dir2
。删除文件和目录:
1 | rm item.. |
rm
有一些常用选项:
选项 | 意义 |
---|---|
-i ,--interactive |
在删除已存在文件前,提示确认信息。 |
-r, –recursive` |
递归删除文件。当删除一整个目录时,需要该选项 |
-f ,--force |
忽视不存在的文件,强力删除 |
-v ,--verbose |
显示翔实的命令操作信息 |
举例:
rm file1
:删除文件 file1
。rm -i file1
:删除之前提示确定。rm -r file dir1
:递归删除文件 file1
、目录 dir1
以及里面的内容。rm -rf file1 dir1
:强制删除文件 file1
和 目录 dir1
以及里面的内容。注意:rm
命令一旦删除没有复原,你需要知道你删的是什么,特别是与通配符一起用的情况,很容易删完所有文件。
硬链接时最初 Unix 创建链接的方式,每个文件默认会有一个硬链接,这个硬链接给予文件名字。每创建一个硬链接,就为一个文件创建了一个额外的目录下。硬链接的局限性如下:
一个硬链接和文件本身没有什么区别。当列出一个包含硬链接的目录内容时,会看到没有特殊的链接指示说明。当一个硬链接被删除时,这个链接被删除,但是文件本身的内容仍然存在(这是说,它所占的磁盘空间不会被重新分配),直到所有关联这个文件的链接都删除掉。
符号链接克服了硬链接的局限性。符号链接生效是通过创建一个特殊类型的文件, 这个文件包含一个关联文件或目录的文本指针。在这一方面,和 Windows 的快捷方式差不多。
一个符号链接指向一个文件,而且这个符号链接本身与其它的符号链接几乎没有区别。例如,如果往一个符号链接里面写入东西,那么相关联的文件也被写入。然而,当你删除一个符号链接时,只有这个链接被删除,而不是文件自身。如果先于符号链接删除文件,这个链接仍然存在,但是不指向任何东西。在这种情况下,这个链接被称为坏链接。在许多实现中,ls
命令会以不同的颜色展示坏链接,比如说红色,来显示它们的存在。
ln
命令可以创建硬链接或者符号链接。
1 | ln file link |
1 | ln -s item link |
首先在家目录中创建一个 playground
目录:
1 | fingsinz@FingsinzStudio:~$ mkdir playground |
进入 playground
目录,同时创建两个子目录:
1 | fingsinz@FingsinzStudio:~/playground$ mkdir dir1 dir2 |
复制一些数据到 playground
中:
1 | fingsinz@FingsinzStudio:~/playground$ cp /etc/passwd . |
尝试一下 cp
的选项:
1 | fingsinz@FingsinzStudio:~/playground$ cp -iv /etc/passwd . |
感受一下 mv
文件移动,尝试理解下面的操作:
1 | fingsinz@FingsinzStudio:~/playground$ ls |
dir2
是一个文件,是 passwd 文件改名后,并不是一个目录。好的现在恢复原样:
1 | fingsinz@FingsinzStudio:~/playground$ ls |
再试试创建硬链接:
1 | fingsinz@FingsinzStudio:~/playground$ ln passwd test |
passwd
和 test
文件的硬链接数目都是 4。文件至少有一个硬链接,因为文件名就是由链接创建的。通过 ls -li
展示文件索引节点信息:
1 | fingsinz@FingsinzStudio:~/playground$ ls -li . dir1 dir2 |
test
和 passwd
的索引号都是一样的,这就证实这些文件是同一个文件。先把之前的硬链接简单删掉,然后创建符号链接玩玩:
1 | fingsinz@FingsinzStudio:~/playground$ rm test dir1/test dir2/test |
test
应该是红色名字的,这表示是一个坏链接,找不到文件 passwd
。删掉这个坏链接,然后进入到 dir1
目录重新建立符号链接:
1 | fingsinz@FingsinzStudio:~/playground/dir1$ ls |
ls
命令会提示链接的是什么。建立符号链接时应当注意目录,符号链接的目录是目标文件的相对目录。建立符号链接时,可以使用绝对路径名,这样万无一失。
最后,删掉 playground/passwd
,观察链接情况:
1 | fingsinz@FingsinzStudio:~/playground$ ls |
最后的最后,恢复现场,把 playground
一并删了,记得 -r
递归:
1 | fingsinz@FingsinzStudio:~/playground$ cd .. |
命令可以是:
cd
命令。type
命令是 shell 内部的命令,会显示命令的类型:
1 | type command |
一些使用例子:
1 | fingsinz@FingsinzStudio:~$ type ls |
shell builtin
:表示是 shell 自身的命令,内建于 shell 的命令。为了确定所给定的执行程序的准确位置,使用 which
命令:
1 | which exe |
由前面可以知道,/bin
目录下存放的是一些二进制文件,所以可以试试:
1 | fingsinz@FingsinzStudio:~$ which cp |
which
只能用于显示可执行程序的位置,上面操作中, cd
并不是一个可执行程序,故没有输出。有时候我们并不能完全记住每个命令的用法,这个时候需要查阅帮助文档。
help
命令可以获取 shell 内建命令的帮助文档。man
命令可以获取程序的帮助文档(手册)。1 | fingsinz@FingsinzStudio:~$ help cd |
在大多数Linux 系统中,man
使用 less 工具来显示参考手册。所显示的参考手册,被分成几个章节,它们不仅仅包括用户命令,也包括系统管理员 命令、程序接口、文件格式等等:
章节 | 内容 |
---|---|
1 | 用户命令 |
2 | 程序接口内核系统调用 |
3 | C库函数程序接口 |
4 | 特殊文件,比如设备结点和驱动程序 |
5 | 文件格式 |
6 | 游戏娱乐 |
7 | 其他方面 |
8 | 系统管理员命令 |
默认情况下,会自动匹配 man
的对象属于什么内容显示。但如果要查找一 种文件格式,而同时它也是一个命令名时,这种情况没有指定章节号,总是得到第一个匹配项。具体使用:
1 | man section xxx |
手册只是参考,阅读起来很困难。使用 whatis
程序可以显示匹配特定关键字的手册的名字和一行命令说明:
1 | fingsinz@FingsinzStudio:~$ whatis rm |
使用 alias
命令可以创建自己的命令:
1 | alias name='string' |
首先,多个命令可以通过 ;
连接在一行使用,比如:
1 | fingsinz@FingsinzStudio:~$ mkdir playground; cd playground; ls -liha; cd ..; rm playground -r; |
playground
文件夹,然后列出目录文件,最后退出目录并删除。在起别名之前最好用 type
命令看看是否已经被用了:
1 | fingsinz@FingsinzStudio:~$ type test |
然后再起别名:
1 | fingsinz@FingsinzStudio:~$ alias foo='mkdir playground; cd playground; ls -liha; cd ..; rm playground -r;' |
删除别名使用 unalias
命令:
1 | fingsinz@FingsinzStudio:~$ unalias foo |
在此章节介绍中,建立的别名会在 shell 会话结束时会消失。随后的章节里, 会了解怎样把自己的别名添加到文件中去,每次登录系统,这些文件会建立系统环境。
命令运行结果会输送到一个叫做标准输出的特殊文件(经常用stdout表示),而它们的状态信息则送到另 一个叫做标准错误的文件(stderr)。默认情况下,标准输出和标准错误都连接到屏幕,而不是保存到磁盘文件。除此之外,许多程序从一个叫做标准输入(stdin)的设备得到输入,默认情况下,标准输入连接到键盘。
I/O 重定向允许更改输出地点和输入来源。一般地,输出送到屏幕,输入来自键盘,但 是通过 I/O 重定向,可以做出改变。
重定向符 >
,比如:
1 | fingsinz@FingsinzStudio:~$ ls / > ls-output.txt |
再比如:
1 | fingsinz@FingsinzStudio:~$ ls /aaaaa > ls-output.txt |
ls
出来。ls
程序不把它的错误信息输送到标准输出,而是输送到标准错误。这有一个技巧,简单地使用重定向符,没有命令在它之前,这会清空一个已存在文件的内容或是创建一个新的空文件:
1 | fingsinz@FingsinzStudio:~$ > ls-output.txt |
如果想要追加输出,使用 >>
:
1 | fingsinz@FingsinzStudio:~$ ls -lh ls-output.txt |
文件流的标准输入、标准输出和标准错误分别对应 shell 内部的文件描述符0
、1
、2
。。shell 使用件描述符提供了一种表示法来重定向文件。标准错误和文件描述符 2
一样:
1 | fingsinz@FingsinzStudio:~$ ls /aaaa 2> ls-error.txt |
使用 &>
:
1 | fingsinz@FingsinzStudio:~$ ls -l /aaa &> ls-output.txt |
如果不需要命令的输出结果,可以重定向到 /dev/null
。
/dev/null
是系统的一个设备,叫做位存储桶,可以接受输入且不对输入做处理。1 | fingsinz@FingsinzStudio:~$ ls -l /aaa 2> /dev/null |
cat
命令可以读取一个或多个文件,然后复制到标准输出:
1 | cat [file] |
当 cat
的对象只有一个文件时,可以用来显示文件里面的内容:
1 | fingsinz@FingsinzStudio:~$ ls -l /aaa 2> ls-output.txt |
当 cat
的对象有多个文件时,可以用来拼接文件:
1 | fingsinz@FingsinzStudio:~$ cat ls-output.txt ls-output.txt |
当 cat
不带参数时,它会从标准输入读入数据并输出到标准输出:
1 | fingsinz@FingsinzStudio:~$ cat |
ctrl + d
告诉 cat
从标准输入的读取结束。使用符号<
:
1 | fingsinz@FingsinzStudio:~$ cat < ls-output.txt |
<
,单靠 cat
也可以完成,但是 <
就是一个重定向标准输入符号,后面会有更好的例子。使用管道操作符 |
(竖杠),可以让一个命令的标准输出通过管道送至另一个命令的标准输入:
1 | command1 | command2 |
如:
1 | fingsinz@FingsinzStudio:~$ ls / | less |
有可能会把几个命令放在一起组成一个管道线。 以这种方式使用的命令被称为过滤器。
比如:
1 | fingsinz@FingsinzStudio:~$ ls / | sort | less |
sort
具有排序的作用。uniq
从标准输入或单个文件名参数接受数据有序列表(详情查看uniq 手册页),默认情况下,从数据列表中删除任何重复行。
uniq
命令带上 -d
选项。比如:
1 | fingsinz@FingsinzStudio:~$ mkdir playground |
playground
里面创建文件 a
、b
和文件夹 test
,在文件夹 test
内创建文件 a
。uniq
连接并重定向到 cat
中,输出并没有出现重复的文件 a
。ls
两个目录时中间会空一行,经过 sort
后排序到第一位。wc
(字计数)命令是用来显示文件所包含的行数、字数和字节数:
1 | fingsinz@FingsinzStudio:~$ wc ls-output.txt |
-l
选项可以限制只输出行数。同样可以通过管道线进行统计数据:
1 | fingsinz@FingsinzStudio:~/playground$ ls ./ test/ | sort | uniq | wc -l |
grep
用来找到文件中的匹配文本:
1 | grep pattern [file...] |
比如现在要从系统安装的程序中找到包含 zip
的文件:
1 | fingsinz@FingsinzStudio:~$ ls /bin /usr/bin | uniq | grep zip |
grep
有一些方便的选项:-i
使得 grep
在执行搜索时忽略大小写(通常,搜索是大小写敏感的);-v
选项会告诉 grep
只打印不匹配的行。
head
命令可以输出文件的前几行,tail
命令可以输出文件的后几行。
-n
调整打印的行数。1 | fingsinz@FingsinzStudio:~$ ls /bin | head |
tail
命令的 -f
选项可以持续检测文件,当新内容添加后会立即出现,直至 ctrl + c
。
tee
命令制造了一个 “tee”安装到管道上。tee
程序从标准输入读入数据,并且同时复制数据到标准输出(允许数据继续随着管道线流动)。
当在某个中间处理阶段来捕捉一个管道线的内容时,这很有帮助,如:
1 | fingsinz@FingsinzStudio:~$ ls /bin /usr/bin | sort | tee bin.txt | grep zip |