这是我在自学Linux系统的过程中写下的笔记(学习平台:实验楼Linux基础教程),因为记录得比较松散、不成体系,所以主要供文档读者在有所遗忘或疑问时便于查询。要想系统完整地学习Linux系统,阅读大部头的教材更为合适,比如Richard Stevens的《Unix环境高级编程》。
创始人:雷纳斯托瓦茨,因为Unix系统过于昂贵,遂开发出模仿Unix系统的Linux系统,供开源使用。
特点:兼具图形界面(GUI)既命令行(Shell,命令行解释器)的使用方式。主要使用命令行。这也是学习该课程的目的。
用途:搭建服务器、程序开发
Linux发行版:有Ubuntu,CentOS,Mint等。Linux作为操作系统内核,是Linux发行版的一部分。 一个典型的Linux发行版包括:Linux内核,一些GNU程序库和工具,命令行shell,图形界面的X Window系统和相应的桌面环境,如KDE或GNOME,并包含数千种从办公套件,编译器,文本编辑器到科学工具的应用软件。
Shell,即命令解析器,相对于kernel(操作系统内核)而言,隐藏了操作系统的底层技术细节。主要功能是为使用者提供使用界面,类似于DOS下的command和cmd.exe。UNIX/Linux 操作系统下的 Shell 既是用户交互的界面,也是控制系统的脚本语言。Ubuntu终端默认使用bash。
sudo:即Switch User Do,一般用户只要使用该linux指令,即可在无需输入超级用户密码的情形下取得超级用户的权限,并执行特定的命令,如更新、安装程序等。
命令 | 说明 |
---|---|
touch file |
创建一个名为file的文件 |
cd /etc/ |
进入一个目录 |
pwd |
查看当前所在目录 |
ls |
列出当前文件夹下的所有文件 |
/? |
通配符,前者匹配字符串,后者匹配字符 |
↑ |
浏览命令的历史记录 |
man |
查询使用手册 |
|
查询某个命令的参数使用方法 |
命令 | 说明 |
---|---|
tab |
自动补全 |
Ctrl+c |
强行终止当前程序 |
Ctrl+d |
终止键盘输入或退出终端 |
Ctrl+s |
暂停当前程序 |
Ctrl+z |
当前程序放到后台运行 |
Ctrl+a/Ctrl+e |
光标移动至当前行首/行末 |
Ctrl+k |
删除光标后当前行的所有内容 |
Shift+↑/↓ |
终端显示向上or向下滚动 |
dev/tty
控制台的当前控制终端的别名
命令 | 说明 |
---|---|
who am I/who mom likes/ -m |
打印当前终端用户的用户名及伪终端信息 |
whoami |
当前登录用户的用户名(登录的用户不一定是打开终端的用户) |
-a (who命令的flag) |
打印能打印的一切关于用户的信息 |
-d |
打印死掉的进程 |
-q |
打印当前用户数及用户名 |
root 权限,系统权限的一种,与 SYSTEM 权限可以理解成一个概念,但高于 Administrator 权限,root 是 Linux 和 UNIX 系统中的超级管理员用户帐户,该帐户拥有整个系统至高无上的权力,所有对象他都可以操作,所以很多黑客在入侵系统的时候,都要把权限提升到 root 权限
一般我们登录系统时都是以普通账户的身份登录的,要创建用户需要 root 权限,这里就要用到 sudo 这个命令了。不过使用这个命令有两个大前提,一是你要知道当前登录用户的密码,二是当前用户必须在 sudo 用户组。
Linux系统下输入密码是不会显示的。
命令 | 说明 |
---|---|
su |
可以切换到用户 user,执行时需要输入目标用户的密码。 |
sudo |
可以以特权级别运行 cmd 命令,需要当前用户属于 sudo 组,且需要输入当前用户的密码。 |
su -l |
也是切换用户,但是同时用户的环境变量和工作目录也会跟着改变成目标用户所对应的。常用这个命令切换用户。 |
exit |
退出当前用户 |
在 Linux 里面每个用户都有一个归属(用户组),用户组简单地理解就是一组用户的集合,它们共享一些资源和权限,同时拥有私有资源。
命令 | 说明 |
---|---|
groups |
查看某用户所在的用户组 |
/etc/group |
记录了所有的用户组 |
| grep -E str |
管道命令,加在命令行后,过滤掉不含字符串str的文件 |
sudo usermod -G sudo |
添加用户user至sudo用户组中 |
命令 | 说明 |
---|---|
sudo adduser |
添加用户 |
sudo deluser |
移除用户并将其工作目录一并删除 |
sudo groupdel |
删除群组,必须在删除完该群组的所有用户后才能执行该操作 |
命令 | 说明 |
---|---|
ls |
列出并显示当前目录下的文件,其后可跟不同的参数 |
-l |
长格式列出文件,长格式从左至右依次为,“文件类型和权限”、“链接数”、“所有者”、“所属用户组”、“文件大小”、“创建时间”、“文件名” |
-a |
显示包括隐藏文件(以.开头)在内的所有文件 |
-t |
以时间先后顺序列出文件 |
d |
目录也会像普通文件一样被列出 |
-asSh |
以简单的方式呈现文件,其中s为显示文件大小,S为按文件名排序 |
Linux系统中的每个文件和目录都有访问许可权限,用于确定谁可以通过何种方式对文件和目录进行访问和操作。当我们在使用Linux系统时,常常会遇到无法用编辑器打开文件,或者是无法修改文件的问题,这时候常常是文件在权限上对当前用户设下了限制。如何去适当修改文件的权限以便使用,是一个相当重要的技能。
文件或目录的访问权限分为只读,只写和可执行三种,在Linux系统上由十个字符表示,如果我们键入ls -l
命令,每一个文件的权限都以10个小写字母的形式展现在左方,如下图的最左边一列。
10个字母的第一个字母表示该文件是否为一个目录,如果是,则用d
表示,如果不是,则用-
表示。
接下来9个字母从左至右分为三组,每3个字母为一组,第一、二、三组,分别代表着:当前用户、当前用户所属的用户组成员、除这两者外的其他用户。
对于同一组内的三个字母,第一个字母是r
(read)或-
, 前者表示可读,后者表示不可读。第二个字母是w
(write)或-
, 前者表示可写,后者表示不可写。第三个字母是x
(execute)或-
, 前者表示可执行,后者表示不可执行。
既然文件的权限可以被表示为一个9位的字符串,每一位都只能在两种字符间选择,很自然地,我们可以将这一个9位的字符串改写成一个9位的二进制数。进一步地,既然这9位是每三个一组分成一组,我们干脆将组内的三位并在一起,形成一个0~7之间的数字,那么9位二进制数也就被改写成一个3位的八进制数。在Linux中,我们也可以直接用这个八进制数来表示文件的权限。
举个简单的例子,如果一个文件的权限是r--rw-rwx
, 前三个字符r--
,写成二进制便是100
,也就是八进制中的4
;
中间的三个字符rw-
,在二进制中是110
,也就是八进制中的6
;最后的三个字符rwx
,二进制表示为111
,也就是八进制中的7
。那么这个文件的权限用八进制三位数表示就是467
。
(图源:https://blog.csdn.net/BjarneCpp/article/details/79912495)
假设要将文件的所有者改变为具有sudo权限的user,需要依次进行如下步骤:
sudo chown
要想修改文件权限,首先要将用户切换为文件的所有者,或者将文件的所有者修改为自己(如果自己是sudo用户),修改文件所有者的方法如上小节所示。
当确定文件的所有者是自己以后,就可以执行chmod
命令来修改文件的权限。
chmod
有两种常见的用法,第一种方法是通过加号或减号来增减文件对不同使用者的读写执行权限:
chmod u+r <file>
chmod g+w <file>
chmod o+x <file>
chmod u-r <file>
chmod g-w <file>
chmod o-x <file>
在这里,字母u
,g
,o
,分别表示“当前用户”(user)、“当前用户所属的用户组成员”(group)、“除这两者外的其他用户”(others)。这三个字母也分别对应着九位字母的前三位、中间三位、和最后三位。
除此之外,还可以用字母a
表示“所有用户的权限”(all)
+
和-
的意义比较好理解,就是对前一个字母表示的用户群体,增加或剥夺后一个字母表示的权限。
另一种方法是将chmod
的第一个参数设置为一个八进制的三位数,具体赋值方法如上一节所述。
chmod 777 <file> #所有用户都可以用任意方式使用文件
chmod 600 <file> #只有文件的拥有者才可以对文件进行读写,不能执行
Linux目录与windows目录是不同的,windows主要以盘符和分区实现文件管理,目录的层级次于盘符;Linux系统虽然从本质上,目录和系统内核仍然挂载在磁盘上,但从逻辑上来说linux系统的磁盘是挂在目录上的,目录不仅可以使用本地磁盘的文件,也可以使用网络上的文件系统。
Linux目录结构采用FHS标准(英文:Filesystem Hierarchy Standard 中文:文件系统层次结构标准):FHS 定义了系统中每个区域的用途、所需要的最小构成的文件和目录同时还给出了例外处理与矛盾处理。
FHS 定义了两层规范,第一层是, / 下面的各个目录应该要放什么文件数据,例如 /etc 应该放置设置文件,/bin 与 /sbin 则应该放置可执行文件等等;第二层则是针对 /usr 及 /var 这两个目录的子目录来定义。例如 /var/log 放置系统日志文件,/usr/share 放置共享数据等等。
FHS系统由世界各地的开发者总结而成,保持不断的更新。
路径的概念:通过路径可以对目录进行定位,在操作某一个目录之前,必须对先对目录进行定位。
命令 | 说明 |
---|---|
cd |
切换目录,在其后加上目标目录的绝对路径,即可移动至目标目录 |
. |
当前目录 |
.. |
上一级目录 |
- |
上一次所在目录 |
~ |
当前用户home目录 |
pwd |
显示当前目录的绝对路径 |
绝对路径:
关于绝对路径,简单地说就是以根/
目录为起点的完整路径,以你所要到的目录为终点,表现形式如: /usr/local/bin
,表示根目录下的 usr 目录中的 local 目录中的 bin 目录。
相对路径:
相对路径,也就是相对于你当前的目录的路径,相对路径是以当前目录 . 为起点,以你所要到的目录为终点,表现形式如: usr/local/bin
。如果是当前目录的上一级目录,则需要使用 ..
,比如你当前目录为 /home/shiyanlou
目录下,根目录就应该表示为 ../../
,home目录可以表示为../
提示:在进行目录切换的过程中请多使用 Tab 键自动补全,可避免输入错误,连续按两次 Tab 可以显示全部候选结果。
命令 | 说明 |
---|---|
touch |
在当前目录下新建空白文件file |
mkdir |
在当前目录下新建空白目录dir |
mkdir -p father/son/grandson |
在当前目录下创建多级目录 |
-tips: 如果当前目录已经存在文件file,则在新建名为file的目录时会报错;如果当前目录已经存在文件夹dir,在新建名为dir的文件时会改变文件夹的时间戳,并不会新建一个重名的文件
cp
将文件file复制至路径path所对应的目标目录中cp
,会报错,复制目录时不能直接使用指令cp
,而是要加上参数-r
(表示对目录的递归复制),在指令cp -r
下,才能把目录dir复制至路径path对应的目录中。命令 | 说明 |
---|---|
rm |
删除文件file,若file为只读文件时无法进行删除操作 |
rm -f |
删除文件file,可以无视file的只读属性 |
rm -r |
删除非只读目录dir,同复制操作要加上-r参数 |
rm -rf |
强制删除目录dir,可以无视只读属性 |
mv
将文件file移动(剪切)至path对应的目录中mv oldname newname
将名为oldname的文件重命名为newnamesudo apt-get install rename
命令自行安装。命令 | 说明 |
---|---|
cat |
正序显示文件内容 |
tac |
倒序显示文件内容 |
cat -n |
显示文件内容并加上行号 |
head 及tail |
这两个命令只查看文件的头几行(默认为 10 行,不足 10 行则显示全部)和尾几行。 |
head/tail -n x |
对head命令或tail命令加参数,查看文件的头(尾)x行 |
file |
查看文件file的文件类型 |
more |
使用more打开file文件,打开后默认只显示一屏内容,终端底部显示当前阅读的进度。可以使用 Enter 键向下滚动一行,使用 Space 键向下滚动一屏,按下 h 显示帮助,q 退出。 |
编辑文件一般会使用vim,emacs等命令行编辑器,在shell中输入vimtutor
,即可浏览Linux内置的vim教程。
如果读者对vim有兴趣,可以移步到我写的vim笔记,其中简要地罗列了一些vim的基本用法:Vim基础操作:从入门到入土
在使用Linux的过程中,我们通常会涉及到三种变量,分别是:
Shell本身的内置变量,不会在退出shell后消失。
用户创建的shell自定义变量,只在shell自身的进程中有效
从自定义变量导出的环境变量,可以作用于shell自身及其子进程
要想打印这三种不同的变量,我们有对应的三个命令:
命令 | 说明 |
---|---|
set |
显示当前shell中包括这三类变量的所有变量 |
env |
显示与当前用户相关的环境变量,可以让命令在指定环境中运行 |
export |
显示从shell中导出的环境变量,可以用于将自定义变量导出为环境变量 |
以下详细讲解后两种变量。
用户可以在Shell中创建、使用自定义变量。Shell自定义变量和程序语言中的变量一样,有着不同的类型,可以被赋值、参与运算,也有着自己的作用域。
创建变量,使用declare命令:
declare tmp
对变量赋值,使用=
,注意在赋值语句中不能出现空格。许多人出于一种良好的代码习惯,会加上空格,但在Linux中,空格的前后会被解释为命令行的不同参数,引起歧义。
# correct
tmp="str"
# wrong
tmp = "str"
读取变量的值,使用echo
命令以及$
符号,其中$
符号表示引用一个变量的值,如果缺失,将会返回变量的名字而非变量的值;而 echo 则是linux的内置命令,用于输出参数字符串,同样不能漏掉,否则变量名会被解读为内置的命令,容易导致command not found的报错。
# correct
> echo $tmp
str
# wrong case 1
> echo tmp
tmp
# wrong case 2
> tmp
tmp: command not found
删除变量:使用unset命令
unset tmp
变量名的命名也是存在限制的:只能是英文字母、数字、下划线的组合,且不能以数字开头,这和程序语言的要求是类似的。
我们也可以对变量进行修改,有相当多的方法,具体如下表:
变量设置方式 | 说明 |
---|---|
${变量名#匹配字串} | 从头向后开始匹配,删除符合匹配字串的最短数据 |
${变量名##匹配字串} | 从头向后开始匹配,删除符合匹配字串的最长数据 |
${变量名%匹配字串} | 从尾向前开始匹配,删除符合匹配字串的最短数据 |
${变量名%%匹配字串} | 从尾向前开始匹配,删除符合匹配字串的最长数据 |
${变量名/旧的字串/新的字串} | 将符合旧字串的第一个字串替换为新的字串 |
${变量名//旧的字串/新的字串} | 将符合旧字串的全部字串替换为新的字串 |
匹配子串既可以是字符串也可以是通配符。
与以上讨论的Shell自定义变量的作用域是较为狭窄的,仅在当前Shell的进程中有效。
而环境变量则不同,它的作用域被拓宽到Shell自身进程及其子进程。
在所有的 UNIX 和类 UNIX 系统中,每个进程都有其各自的环境变量设置,且默认情况下,当一个进程被创建时,除了创建过程中明确指定的话,它将继承其父进程的绝大部分环境设置。Shell 程序也作为一个进程运行在操作系统之上,而我们在 Shell 中运行的大部分命令都将以 Shell 的子进程的方式运行。
进程是计算机科学中的重要概念,更多细节可以参考经典教材CSAPP的讲解。
将一个自定义变量导出为环境变量,只需要使用export
命令:
export TMP
这样变量TMP就能在shell的子进程中被访问和使用。
但是在退出shell后,所有创建的自定义变量和环境变量将会失效,并不会被保存。要想永久保存这些变量,需要修改系统配置文件/etc/bashrc
,以及/etc/profile
,它们分别存放shell变量和环境变量。
注意:为了与普通变量区分,通常我们习惯将环境变量名设为大写。
shell中输入的命令,通过环境变量PATH
来进行搜索,也就是说,所有内置的命令的本体是一个个可执行程序,而这些程序被放在PATH中的路径下。
我们查看环境变量的内容
echo $PATH
可以看到类似于以下格式的输出:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
:/usr/local/games
其中不同的路径用冒号隔开,系统在寻找内置命令对应的可执行程序时,会依次搜索这些用冒号隔开的路径,如果存在同名程序于不同的路径中,则优先执行先找到的路径。
用户可以自行添加自定义的搜索路径到PATH变量,因为路径由冒号隔开,我们在添加路径时应该这样写:
# 依然要注意不能在等号两边空格,且这里一定要添加绝对路径
PATH=$PATH:/usr/bin
但由于PATH是一个环境变量,如果我们重新启动shell,就会失去我们添加的自定义路径,如何解决这一问题?我们选择每次启动 Shell 时自动执行上面添加自定义路径到 PATH 的命令。
在每个用户的 home目录中有一个 Shell 每次启动时会默认执行一个配置脚本,以初始化环境,包括添加一些用户自定义环境变量等等。如果使用的 Shell 是 zsh,它的配置文件是 .zshrc,相应的如果使用的 Shell 是 Bash,则配置文件为 .bashrc。它们在 etc 下还都有一个或多个全局的配置文件,不过我们一般只修改用户目录下的配置文件。
我们可以简单地使用下面命令直接添加内容到 .zshrc或,bashrc 中:
# .zshrc/.bashrc
# 注意:">>"表示以追加到结尾(append)的方法添加到文件中,不能写成表示直接覆盖的">"
echo "PATH=$PATH:/home/shiyanlou/mybin" >> .zshrc
这样,就可以解决自动添加自定义路径的问题。
在修改配置脚本文件.xxxrc后,如果不退出shell,并且想要让新的配置文件立刻生效,则需要使用source
命令,source
命令有别名.
:
# source的参数不需要是完整的路径,可以是配置文件的名字
source .zshrc
# .的参数必须是完整的绝对路径或相对路径
. /etc/.zshrc # 绝对路径
. ./.zshrc # 相对路径
常用的用于查找文件的命令有:whereis
/locate
/which
/find
四个命令。
whereis是一个简单实用的命令,语法简单,速度快,只需要直接执行
whereis who
whereis find
whereis filename
即可得到一系列路径作为返回,不同路径用空格隔开
who: /usr/bin/who /usr/share/man/man1/who.who1.gz
whereis命令在运行的过程中,并没有搜索磁盘,而是直接查询系统时刻维护的一个数据库,所以速度相当的快。
whereis命令有其局限性,它只能搜索二进制文件(-b),man 帮助文件(-m)和源代码文件(-s),所以搜索的内容有限制。
locate命令具有运行速度快,获得的结果完全的特点。虽然locate底层的实现机制类似于whereis,也是查询数据库(/var/lib/mlocate/mlocate.db)而非查询磁盘,但locate命令所查询的数据库更新的频率为每日一次,不像whereis是实时更新的。系统会使用定时任务每天自动执行 updatedb 命令来更新数据库。所以有时候你刚添加的文件,它可能会找不到,需要手动执行一次 updatedb 命令(在我们的环境中必须先执行一次该命令)。
locate命令也不是内置的命令,在部分环境中需要手动安装,然后执行更新。
sudo apt-get update
sudo apt-get install locate
sudo updatedb
locate命令可以用于查找指定目录下的不同文件类型
# 查找/etc下所有以sh开头的文件
locate /etc/sh
# 使用通配符 查找/usr/share下所有.jpg文件
locate /usr/share/*.jpg
如果想只统计数目可以加上 -c 参数,-i 参数可以忽略大小写进行查找,whereis 的 -b、-m、-s 同样可以使用。
which是shell内建的一个命令,具有体量小的特点。我们通常使用 which 来确定是否安装了某个指定的程序,因为它只从 PATH 环境变量指定的路径中去搜索命令,并且返回第一个搜索到的结果。用法同whereis命令
which man
which ping
find是这几个命令中功能最强大的命令,它不但可以通过文件类型、文件名进行查找而且可以根据文件的属性(如文件的时间戳,文件的权限等)进行搜索。find的功能强大,用法繁多,这里只介绍常用用法。
路径是find的第一个参数, 基本命令格式为 find [path][option] [action] ,例如:
sudo find /etc/ -name interfaces
表示去 /etc/ 目录下面 ,搜索名字叫做 interfaces 的文件或者目录。
在使用find时并不必须加sudo,但如果当前用户非root,且搜索的目录比较吃权限,则最好加上sudo。
find有一些与时间戳相关的命令参数:
参数 | 说明 |
---|---|
-atime | 最后访问时间 |
-ctime | 最后修改文件内容的时间 |
-mtime | 最后修改文件属性的时间 |
下面以 -mtime 参数举例:
写成命令行:
# 列出home目录中,24小时内有改动的文件
find ~ -mtime 0
# 列出home目录中比/etc目录新的文件
find ~ -newer /etc
在Linux中使用zip命令,可以将文件或文件夹压缩为一个zip文件。
假设我们想要压缩的文件(夹)的路径为target_path,执行命令:
zip -r -q -o target_name.zip target_path
就可以压缩目标的文件(夹),并在当前的位置生成一个名为target_name.zip的压缩文件。
-r
在被压缩的对象为文件夹时需要被加上,表示recursive,即递归的含义,用法类似rm命令的-r。
-q
参数表示安静模式,即在压缩的过程中不向屏幕输出信息。
-o
参数后紧跟着的是打包文件的文件名target_name。
我们还可以设置压缩级别,范围为从1到9:
zip -r -9 -q -o target_name_9.zip target_path -x ~/*.zip
zip -r -1 -q -o target_name_1.zip target_path -x ~/*.zip
压缩级别为1时,压缩最快,但压缩后文件的体积更大;压缩级别为9时,压缩最慢,但压缩后文件的体积是最小的。
在这里我们还设置了参数-x
,这个参数的作用是之前被打包的压缩文件再次被打包。-x
后面跟随的路径必须为绝对路径,否则不起作用。
除此之外,比较重要的参数还有-e
,-l
,其中-e
参数可以对zip文件进行加密,而-l
的作用是将文件的换行符,改为回车+换行,这样得到的压缩包才能在Windows系统上被成功地解开。
与zip命令对应,unzip命令的作用自然是解压缩。
最简洁的用法,直接将压缩文件在当前目录下解压:
unzip filename.zip
用-q
开启安静模式,解压到指定目录directory,如果该目录不存在则会自动创建:
unzip -q filename.zip -d directory
使用参数-l
,可以在不解压的状况下查看压缩包的内容:
unzip -l filename.zip
为了避免跨操作系统可能出现的编码问题(比如Linux上汉字编码为UTF-8, Windows上汉字编码为GBK),在解压时我们还可以使用-O
参数,人为指定解压后的字符编码:
# 解压为GBK汉字编码
unzip -O GBK 中文压缩文件.zip
tar工具的功能强大,既可以打包单独的文件和文件夹,也可以将多个文件一起打包。tar工具的使用也相当容易,无论是压缩或者解压缩,使用的都是同一个命令。
创建一个tar包:
tar -P -cf target_name.tar source_path
这里target_name是目标打包文件的文件名,source_path是被打包文件(夹)的路径。-P
保留绝对路径符,
-c
表示创建一个 tar 包文件,-f
用于指定创建的文件名,注意文件名必须紧跟在 -f
参数之后。还可以加上 -v
参数以可视的的方式输出打包的文件。
解包一个文件(-x
参数)到指定路径的已存在目录(-C
参数):
mkdir tardir
tar -xf filename.tar -C tardir
只查看不解包使用t
参数:
tar -tf filename.tar
在需要创建不同格式的压缩文件时,只需要多加一个参数,例如加上-z
参数就能压缩和解压缩gzip文件:
tar -czf filename.tar.gz source_path
tar -xzf filename.tar.gz
其他支持的压缩格式,还有-J
对应的*.tar.xz
,以及-j
对应的*.tar.bz2
等,具体见man手册。
物理主机上的 /dev/vda1 是对应着主机硬盘的分区,后面的数字表示分区号,数字前面的字母 a 表示第几块硬盘(也可能是可移动磁盘),如果主机上有多块硬盘则可能还会出现 /dev/sdb,/dev/sdc。这些磁盘设备都会在 /dev 目录下以文件的形式存在。
1K-块的表示以磁盘块大小的方式显示磁盘总容量,之后的已用和可用都以磁盘块大小的方式显示容量。
为了让磁盘容量以一种更易懂、更直观的方式展示,可以加上-h
参数:
df -h
du的主要作用是查看文件或目录的容量
# 默认以块的大小展示
du
# 加上-h可以将容量的单位改成字节,更加易读
du -h
# 利用-d指定查看目录的深度
# 只查看一级目录
du -h -d 0 ~
# 查看到二级目录
du -h -d 1 ~
# -a: 查看目录中所有文件的大小
du -a
# -s: 仅显示总计的容量,不显示目录名
du -s
在Linux中有着一切皆文件的思想,这使得一些硬件的设备驱动(如 /dev/zero 和 /dev/random),可以像文件一样进行输入和输出的操作。dd命令就可以用在备份硬件的引导扇区、获取一定数量的随机数据或者空数据等任务中。
dd语句的使用方法和一般的Linux命令不同,dd语句设置参数时使用=
进行连接,而不是更标准的-选项 值
或-选项=值
。例如使用dd命令输出文本到文件:
dd if=/dev/stdin of=test bs=10 count=1
上述命令从标准输入设备读入用户输入(缺省值,所以可省略,然后输出到 test 文件。bs
(block size)用于指定块的大小(缺省单位为 Byte,也可为其指定如 K,M,G 等单位),count
用于指定块数量。在上面一行代码中,文件的大小被限制在10B,因此从stdin输入字符的时候,至多可以输入10个字符,多余的字符会被截断并保留在标准输入(如图所示,输入的inputing a word只在文件中被保留了前10个字符)。
dd 在拷贝的同时还可以实现数据转换,比如将输出的英文字符转换为大写再写入文件:
dd if=/dev/stdin of=test bs=10 count=1 conv=ucase
其他类似的功能可以在man中查询,比如将conv设置为ASCII,可以实现EBCDIC转ASCII的功能。
当然,dd指令还可以用于创造一个虚拟磁盘(virtual image):
dd if=/dev/zero of=virtual.img bs=1M count=256
du -h virtual.img
在命令行中输入sudo mkfs
并按下Tab
,可以看到一系列以mkfs为前缀的命令。每一种命令都对应着一种文件系统的格式
接下来将虚拟磁盘镜像格式化为ext4文件系统:
sudo mkfs.ext4 virtual.img
可见在格式化的过程中,系统调用了mke2fs指令
如果想知道 Linux 支持哪些文件系统,可以输入 ls -l /lib/modules/$(uname -r)/kernel/fs
查看
用户在 Linux/UNIX 的机器上打开一个文件以前,包含该文件的文件系统必须先进行挂载的动作,此时用户要对该文件系统执行 mount 的指令以进行挂载。该指令通常是使用在 USB 或其他可移除存储设备上,而根目录则需要始终保持挂载的状态。 Linux/UNIX 文件系统可以对应一个文件而不一定要是硬件设备,所以可以挂载一个包含文件系统的文件到目录树。
Linux/UNIX 命令行的 mount 指令是告诉操作系统,对应的文件系统已经准备好,可以使用了,而该文件系统会对应到一个特定的点(称为挂载点)。挂载好的文件、目录、设备以及特殊文件即可提供给用户使用。
可以使用不加参数的mount命令来查看主机已经挂载的文件系统:
sudo mount
输出的结果中每一行表示一个设备或虚拟设备,每一行最前面是设备名,on 后面是挂载点,type 后面表示文件系统类型,再后面是挂载选项(比如可以在挂载时设定以只读方式挂载等等)
如果要挂载磁盘的目录树,需要执行如下格式的命令:
mount [options] [source] [directory]
例如挂载virtual.img到/mnt目录,执行代码:
# -o:操作选项,这里设置为回环设备
# -t:文件系统的类型
# 也可以省略挂载类型,很多时候 mount 会自动识别
mount -o loop -t ext4 virtual.img /mnt
# -r / --ro:以只读方式挂载
# -w / --rw:可读可写的方式挂载, 默认的设置
mount -o loop --ro virtual.img /mnt
# 或者 mount -o loop,ro virtual.img /mnt
卸载已挂载磁盘,则使用unmount命令:
# 参数设置为挂载点路径
sudo unmount /mnt
fdisk不加参数时可以查看硬盘分区表信息, 输出结果中开头显示了主机上磁盘的一些信息,包括容量扇区数,扇区大小,I/O 大小等信息。
sudo fdisk -l
如果设置fdisk的参数为虚拟设备,则可以进入磁盘分区模式:
sudo fdisk virtual.img
执行命令以后会进入一个GUI,根据GUI的指示一步步做完以后,就可以为虚拟设备做磁盘分区。
我们知道shell是运行在linux操作系统上的一个程序,而内建命令是作为shell程序的一部分而存在的命令。内建命令的程序包含在shell源码里,在系统加载时会被直接加载至内存并直接被shell识别,解析时也不需要新建子进程,因此运行速度相比于外部命令要更快。典型的内建命令有:history
,cd
,exit
等。
外部命令则是独立于shell的程序,功能更强,占用空间相对较大,因此不会被加载至内存。只有在系统需要执行外部命令的时候才会将其加载至内存并执行。外部命令通常放在/bin
,/usr/bin
,/sbin
等路径里,默认路径可以通过环境变量$PATH进行管理。典型的外部命令有:ls
,vi
等
使用type
命令可以用于区分命令是内建的或是外部的,一共可以得到三种结果:
# 第一种结果,内建命令
type cd
cd is a shell bulletin
# 第二种结果,外部命令,显示程序所在路径
type vim
vim is /usr/bin/vim
# 第三种结果得到一个alias,说明该命令是一个命令的别名:
type ls
ls is an alias for ls --color=tty
help命令时bash内置的一个命令,只能用于查看内建命令的用法:
help cd
而对于外部命令,我们是不能使用help命令的。但我们知道外部命令一般会设置–help参数,这时候只需要加上这个参数就行了。
ls --help
相比于help命令,man命令是一个更为好用、也是更为强大的命令,它不需要区分内外部命令,只需要直接将命令的名字作为参数:
man ls
运行的结果是一个页面,里面列举了命令的详细用法,可以当作参数词典来使用。打开手册之后我们可以通过 pgup 与 pgdn 或者上下键来上下翻看,可以按 q 退出当前页面
man命令十分易用,但如果我们需要快速了解一个内建命令,那么help相比于man更合适,因为help的结果比较简短,man对方法的详细描述则显得比较冗长。
info是GNU的超文本帮助系统,,是来自自由软件基金会的 GNU 项目,相比于man命令,能够更完整地显示出 GNU相关的信息。info是外部命令,需要手动安装:
sudo apt-get update
sudo apt-get install info
info ls
管道是一种通信机制,通常用于进程间的通信,它表现出来的形式就是将前面每一个进程的输出直接作为下一个进程的输入,命令行中的每个命令都是一个进程,所以可以通过管道将一个命令的输出作为下一个命令的输入来使用。
管道分为匿名管道和具名管道。我们在使用一些过滤程序时经常会用到的就是匿名管道,在命令行中由 | 分隔符表示。具名管道简单的说就是有名字的管道,通常只会在源程序中用到具名管道。
例如: 我们要查看/etc
下的文件
ls -al /etc
但是输出的内容很长,直接在bash上查看比较得麻烦,我们就可以将它们通过管道连接到另一个命令less
,来使用带滚动条的窗口查看:
ls -al /etc | less
使用cut命令,可以从一个文本文件的每一行中取出想要的部分。
# 打印 /etc/passwd 文件中以 : 为分隔符的第 1 个字段和第 6 个字段分别表示用户名和其家目录
cut /etc/passwd -d ':' -f 1,6
# 打印每一行的某些字符
# 前五个(包含第五个)
cut /etc/passwd -c -5
# 前五个之后的(包含第五个)
cut /etc/passwd -c 5-
# 第五个
cut /etc/passwd -c 5
# 2 到 5 之间的(包含第五个)
cut /etc/passwd -c 2-5
grep命令是一个强大且易用的命令,主要用于在文本以及输入中查找匹配字符串,结合正则表达式可以实现很强大的功能。
grep命令的一般形式为:
grep [命令选项]... 用于匹配的表达式 [文件]...
例如我们要搜索~目录下所有的python代码文件,可以输入命令:
# `-r` 表示递归搜索子目录中的文件,`-n` 表示打印匹配项行号
grep -rn ".*.py$" ~
grep常常与管道结合起来使用:
# 查看环境变量中以 "yanlou" 结尾的字符串
export | grep ".*yanlou$"
wc命令是一个简单小巧的计数工具,用于统计并输出一个文件中行、单词和字节的数目。
# 行数
wc -l /etc/passwd
# 单词数
wc -w /etc/passwd
# 字节数
wc -c /etc/passwd
# 字符数
wc -m /etc/passwd
# 最长行字节数
wc -L /etc/passwd
对于西文字符来说,一个字符就是一个字节,但对于中文字符一个汉字是大于 2 个字节的,具体数目是由字符编码决定的。
结合管道:
# 统计/etc下目录的个数
ls -dl /etc/*/ | wc -l
sort命令可以将输入按照一定顺序排序再输出
# 默认:将每一行按字典序排序
cat /etc/passwd | sort
# 反向字典排序
cat /etc/passwd | sort -r
# 按每一行的某一字段排序,参数-t后指定分隔符,每一行就被分隔符分为数个字段
# -k参数表示我们需要根据第几个字段来排序
cat /etc/passwd | sort -t ':' -k 3
# 当我们对数字排序时,可能不想按照字典序排序,而是想按照数字大小排序,这时候可以加参数-n
cat /etc/passwd | sort -t ':' -k 3 -n
uniq命令可以过滤或者输出重复的行。
举一个例子:我们可以使用history
来查看最近在命令行上执行过的命令(实际为读取 ${SHELL}_history 文件,如我们环境中的 .zsh_history 文件):
history
但我们可能并不在意这些命令的具体参数,而是只在意有哪些命令,这时候就可以结合管道与uniq命令来操作:
history | cut -c 8- | cut -d ' ' -f 1 | sort | uniq
# 或者
history | cut -c 8- | cut -d ' ' -f 1 | sort -u
输出结果:
在这里我们在uniq前经过了sort命令,这是因为uniq只会对相邻的行去重,经过sort后就可以将重复的行排列在相邻的位置上。
输出重复过的行并统计次数:
history | cut -c 8- | cut -d ' ' -f 1 | sort | uniq -dc
tr命令可以用于删除或转换一段文本中的某些文字,基本用法如下:
tr [option]...SET1 [SET2]
操作举例:
# 常用的选项有:
# `-d`:删除和 set1 匹配的字符,不是全词匹配也不是按字符顺序匹配
# `-s`:去除 set1 指定的在输入文本中连续并重复的字符
# 删除 "hello world" 中所有的'o','l','h'
$ echo 'hello world' | tr -d 'olh'
# 将"hello" 中的ll,去重为一个l
$ echo 'hello' | tr -s 'l'
# 将输入文本,全部转换为大写或小写输出
$ echo 'input some text here' | tr '[:lower:]' '[:upper:]'
# 上面的'[:lower:]' '[:upper:]'你也可以简单的写作'[a-z]' '[A-Z]',当然反过来将大写变小写也是可以的
col 命令用于将Tab换成对等数量的空格键,或反转这个操作。
# 常用的选项有:
# `-h`: 将空格转换为tab(默认选项)
# `-x`: 将tab转换为空格
# 给cat加选项-A, 可以查看 /etc/protocols中的不可见字符,可 ^I表示的就是转义成可见字符的Tab
cat -A /etc/protocols
# 使用 col -x 将 /etc/protocols 中的 Tab 转换为空格,然后再使用 cat 查看,你发现 ^I 不见了
cat /etc/protocols | col -x | cat -A
paste命令用于简单地合并文件,不同文件以tab隔开,主要用法为:
paste [option] file...
常用选项 | 说明 |
---|---|
-d | 指定分隔符,默认为tab |
-s | 不合并到一行,每个文件为一行 |
echo hello > file1
echo world > file2
echo PKUEECS > file3
# 输出 hello:world:PKUEECS
paste -d ':' file1 file2 file3
# 输出:
# hello
# world
# PKUEECS
paste -s file1 file2 file3
相比于paste命令,join命令在合并文件时可以将两个文件中相同的行合并在一起,主要用法为:
join [option]... file1 file2
常用选项 | 说明 |
---|---|
-t | 指定分隔符,默认为空格 |
-i | 忽略大小写的差异 |
-1 | 指明第一个文件要用哪个字段来对比,默认对比第一个字段 |
-2 | 指明第二个文件要用哪个字段来对比,默认对比第一个字段 |
# 简单合并两个字段
echo '1 hello' > file1
echo '1 world' > file2
join file1 file2 # 输出结果: '1 hello world'
# 将 /etc/passwd 与 /etc/shadow 两个文件合并,指定以':'作为分隔符
sudo join -t':' /etc/passwd /etc/shadow
# 将 /etc/passwd 与 /etc/group 两个文件合并,指定以':'作为分隔符,分别比对第4和第3个字段
sudo join -t':' -1 4 /etc/passwd -2 3 /etc/group
Linux的使用核心之一是其“万物皆文件”的思想,也正因此,Linux的数据流定向相当的灵活,例如标准输入、标准输出、以及任何设备,都可以当做重定向的源文件、目标文件而存在(/dev/stdin, /dev/stdout)。
最简单的重定向方式,是使用<
/>
/<<
/>>
运算符, 例如语句A > B
,表示A的输出被重定向,作为B的输入,与语句A >> B
/B < A
是等效的,即数据从运算符大屁股一侧的文件,流向运算符尖端指向的文件。
在Linux上执行一系列有序的命令的时候,有时排在前面的命令成功执行了,排在后面的命令才能被执行,又或者是后面的命令依赖于前面命令执行的结果。如果前面的命令执行过程中出了问题,就会连锁反应式地影响到后面命令的执行,有时候凭直观还无法判断命令执行地是否正确。于是我们需要掌握,如何在命令行中写出简单的if/else结构,来有选择性地执行命令。
来举个例子:
which info && info ls
如果电脑上没有安装info
,这一句命令会输出:info not found
;而如果电脑上已经安装好了info
,则命令与后半句info ls
是等效的。
如何来解释这一结果?在Linux中,&&
符号有着这样的作用:对于表达式 a && b
,如果a的值为1, 则只执行a,不再继续执行b ;而如果a的值为0, 则会接着执行b。
对于which cmd
,如果cmd是一个未被安装的命令,则which表达式返回1(也可以用环境变量$?
来获取表达式的结果),否则which表达式返回0。这时再回到上面的命令,当info未被安装时,which info
的值为1,则只执行&&
前面的命令;当info已经被安装时,which info
的值为0,会接着执行&&
后面的命令,也就是info ls
。这样一来,用一个小小的&&
符号和一行代码,我们实现了选择性执行命令的效果。
类似于C语言的逻辑与&&和逻辑或||, Linux中也有||
运算符。||
运算符与&&
的效果则相反,对于表达式 a || b
,如果a的值为0, 则只执行a,不再继续执行b ;而如果a的值为1, 则会接着执行b。值得一提的是,Linux中||
的优先级是小于&&
的,这和C语言类似。
还可以结合 && 和 || 来实现一些操作,比如:
which info>/dev/null && echo "exist" || echo "not exist"
(持续更新中)