以下内容参考于书籍《The Linux Command Line》,中文版本翻译项目:快乐的 Linux 命令行
终端
- 提示符:
$
表示普通用户,#
表示超级用户 - 鼠标与光标:使用光标选择了一些文本则对这些文本就行了复制,按下鼠标中键则进行粘贴
- 结束终端会话:
exit
文件系统
-
文件系统树:Linux 只有一个单一的文件系统树,不管有多少个磁盘或者存储设备连接到计算机上
-
工作目录:当我们首次登录系统(或者启动终端仿真器会话)后,当前工作目录是我们的家目录。每个用户都有他自己的家目录,当用户以普通用户的身份操控系统时,家目录是唯一允许用户对文件进行写入的地方
-
pwd
:当前工作目录 -
ls
:列出目录内容-a
--all 列出所有文件,甚至包括文件名以圆点开头的默认会被隐藏的隐藏文件-d
--directory 通常,如果指定了目录名, ls 命令会列出这个目录中的内容,而不是目录本身。把这个选项与 -l 选项结合使用,可以看到所指定目录的详细信息,而不是目录中的内容-F
--classify 这个选项会在每个所列出的名字后面加上一个指示符。例如,如果名字是目录名,则会加上一个’/’ 字符-h
--human-readable 当以长格式列出时,以人们可读的格式,而不是以字节数来显示文件的大小-l
以长格式显示结果-r
--reverse 以相反的顺序来显示结果。通常, ls 命令的输出结果按照字母升序排列-S
命令输出结果按照文件大小来排序-t
按照修改时间来排序
-
cd
:更改当前工作目录;直接cd
是切换到自己的家目录;cd -
更改工作目录到先前的工作目录;cd ~username
更改工作目录到用户家目录 -
路径:
- 绝对路径:开始于根目录,紧跟着目录树的一个个分支,一直到达所期望的目录或文件。举例:
cd /usr/bin
- 相对路径:开始于工作目录,符号 “.” 指的是工作目录,符号 ”…” 指的是工作目录的父目录;在几乎所有的情况下,可以省略 “./”,因为它是隐含地。举例:
cd ..
返回上一级目录
- 绝对路径:开始于根目录,紧跟着目录树的一个个分支,一直到达所期望的目录或文件。举例:
-
文件命名
- 以 “.” 字符开头的文件名是隐藏文件,可使用
ls -a
来显示 - 文件名和命令名是大小写敏感的
- Linux 没有“文件扩展名”的概念:不像其它一些系统,可以用你喜欢的任何名字来给文件起名,文件内容或用途由其它方法来决定(虽然类 Unix 的操作系统,不用文件扩展名来决定文件的内容或用途,但是有些应用程序会)
- Linux 支持长文件名,文件名可能包含空格(但最好不要用空格,使用下划线来替换),标点符号(但标点符号仅限使用“.”,“-”,下划线)
- 以 “.” 字符开头的文件名是隐藏文件,可使用
-
file
:确定文件类型file filename
:打印出文件内容的简单描述
操作系统
-
less
:属于 “页面调度器” 程序类,这些程序允许通过页方式,在一页中轻松地浏览长长的文本文件less filename
:查看文件内容。举例:less /etc/passwd
查看定义了系统中全部用户身份的文件- 键盘操作
- Page UP or b:向上翻滚一页
- Page Down or space:向下翻滚一页
- UP Arrow:向上翻滚一行
- Down Arrow:向下翻滚一行
- G:移动到最后一行
- 1G or g:移动到开头一行
- /charaters:向前查找指定的字符串
- n:向前查找下一个出现的字符串,这个字符串是之前所指定查找的
- h:显示帮助屏幕
- q:退出 less 程序
-
Linux 常用目录
- /:根目录,万物起源
- /bin:包含系统启动和运行所必须的二进制程序
- /boot:包含 Linux 内核,最初的 RMA 磁盘映像(系统启动时,由驱动程序所需),和启动加载程序。如:/boot/grub/grub.conf or menu.lst,被用来配置启动加载程序;/boot/vmlinuz,Linux 内核
- /dev:这是一个包含设备结点的特殊目录。“一切都是文件”,也使用于设备。在这个目录里,内核维护着它支持的设备
- /etc:这个目录包含所有系统层面的配置文件。它也包含一系列的 shell 脚本,在系统启动时,这些脚本会运行每个系统服务。这个目录中的任何文件应该是可读的文本文件。如:/etc/crontab,定义自动运行的任务;/etc/fstab,包含存储设备的列表,以及与他们相关的挂载点;/etc/passwd,包含用户帐号列表
- /home:在通常的配置环境下,系统会在/home 下,给每个用户分配一个目录。普通只能在他们自己的目录下创建文件。这个限制保护系统免受错误的用户活动破坏
- /lib:包含核心系统程序所需的库文件。这些文件与 Windows 中的动态链接库相似
- /lost+found:每个使用 Linux 文件系统的格式化分区或设备,如:ext3文件系统,都会有这个目录。当部分恢复一个损坏的文件系统时,会用到这个目录。除非文件系统真正的损坏了,那么这个目录会是个空目录
- /media:在现在的 Linux 系统中,/media 目录会包含可移除媒体设备的挂载点,例如 USB 驱动器, CD-ROMs 等等。这些设备连接到计算机之后,会自动地挂载到这个目录结点下
- /mnt:在早些的 Linux 系统中,/mnt 目录包含可移除设备的挂载点
- /opt:这个/opt 目录被用来安装“可选的”软件。这个主要用来存储可能安装在系统中的商业软件产品
- /proc:这个/proc 目录很特殊。从存储在硬盘上的文件的意义上说,它不是真正的文件系统。反而,它是一个由 Linux 内核维护的虚拟文件系统。它所包含的文件是内核的窥视孔。这些文件是可读的,它们会告诉你内核是怎样监管计算机的
- /root:root 帐户的家目录
- /sbin:这个目录包含“系统”二进制文件。它们是完成重大系统任务的程序,通常为超级用户保留
- /tmp:这个/tmp 目录,是用来存储由各种程序创建的临时文件的地方。一些配置,导致系统每次重新启动时,都会清空这个目录
- /usr:在 Linux 系统中,/usr 目录可能是最大的一个。它包含普通用户所需要的所有程序和文件
- /usr/bin:/usr/bin 目录包含系统安装的可执行程序。通常,这个目录会包含许多程序
- /usr/lib:包含由/usr/bin 目录中的程序所用的共享库
- /usr/local:这个/usr/local 目录,是非系统发行版自带,却打算让系统使用的程序的安装目录。通常,由源码编译的程序会安装在/usr/local/bin 目录下。新安装的 Linux 系统中,会存在这个目录,但却是空目录,直到系统管理员放些东西到它里面
- /usr/sbin:包含许多系统管理程序
- /usr/share /usr/share:目录包含许多由/usr/bin 目录中的程序使用的共享数据。其中包括像默认的配置文件,图标,桌面背景,音频文件等等
- /usr/share/doc:大多数安装在系统中的软件包会包含一些文档。在/usr/share/doc 目录下,我们可以找到按照软件包分类的文档
- /var:除了/tmp 和/home 目录之外,相对来说,目前我们看到的目录是静态的,这是说,它们的内容不会改变。/var 目录是可能需要改动的文件存储的地方。各种数据库,假脱机文件,用户邮件等等,都驻扎在这里
- /var/log:这个/var/log 目录包含日志文件,各种系统活动的记录。这些文件非常重要,并且应该时时监测它们。其中最重要的一个文件是/var/log/messages。注意,为了系统安全,在一些系统中,你必须是超级用户才能查看这些日志文件
-
硬链接
- 默认情况下,每个文件有一个硬链接,这个硬链接给文件起名字(一个硬链接和文件本身没有什么区别);当我们创建一个硬链接以后,就为文件创建了一个额外的目录条目
- 当一个硬链接被删除时,这个链接被删除,但是文件本身的内容仍然存在(这是说,它所占的磁盘空间不会被重新分配),直到所有关联这个文件的链接都删除掉
ln file link
:使用ln
命令创建硬链接。创建硬链接时,可以假设文件由两部分组成:文件内容的数据部分和文件名的名字部分;当创建文件硬链接的时候,实际上是为文件创建了额外的名字部分,并且这些名字都关系到相同的数据部分- 局限性
- 一个硬链接不能关联它所在文件系统之外的文件(也就是一个链接不能关联与链接本身不在同一个磁盘分区上的文件 or 硬链接不能跨越物理设备)
- 一个硬链接不能关联一个目录
-
符号链接(创建 Windows 的快捷方式与其类似,是为了克服硬链接的局限性,可解决版本问题等)
- 符号链接是一个特殊文件,有可能被多个文件名所指向,
- 符号链接生效,是通过创建一个特殊类型的文件,这个文件包含一个关联文件或目录的文本指针
- 往一个符号链接里面写入东西,那么相关联的文件也被写入;当删除一个符号链接时,只有这个链接被删除,而不是文件自身(如果先于符号链接删除文件,这个链接仍然存在,但是不指向任何东西,这种情况被称为坏链接;ls命令会以不同的颜色展示坏链接,比如红色)
ln -s item link
:使用ln
命令创建符号链接,“item” 可以是一个文件或是一个目录- 符号链接文件的长度是所指向文件的文件名长度
- 举例:
lrwxrwxrwx 1 root root 11 2007-08-11 07:34 libc.so.6 -> libc-2.6.so
表明一个叫做“libc.so.6”的符号链接(前面一个是符号链接),指向一个叫做“libc-2.6.so”的共享库文件,而 11 为“libc-2.6.so”的文件名长度
操作文件与目录
-
通配符
*
:匹配任意多个字符(包括零个或一个)?
:匹配任意一个字符(不包括零个)[characters]
:匹配任意一个属于字符集中的字符[!characters]
:匹配任意一个不是字符集中的字符[[: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.” 开头,并紧接着 3 个数字的文件[[:upper:]]*
:以大写字母开头的文件[![:digit:]]*
:不以数字开头的文件*[[:lower:]123]
:文件名以小写字母结尾,或以“1”,“2”,或“3”结尾的文件
-
mkdir
mkdir directory...
:创建目录。注意表示法: 在描述一个命令时,当有三个圆点跟在一个命令的参数后面,这意味着那个参数可以重复。举例:mkdir dir1 dir2 dir3
-
cp
- 复制文件和目录
cp item1 item2
:复制单个文件或目录 “item1” 到文件或目录”item2”cp item... directory
:复制多个项目(文件或目录)到一个目录下- 可选项
-a
, --archive 复制文件和目录,以及它们的属性,包括所有权和权限。通常,复本具有用户所操作文件的默认属性-i
, --interactive 在重写已存在文件之前,提示用户确认。如果这个选项不指定, cp 命令会默认重写文件(输入 “y”,文件就会被重写,其它的字符(例如, ”n”)会导致 cp 命令不理会文件)-r
, --recursive 递归地复制目录及目录中的内容。当复制目录时,需要这个选项(或者 -a 选项)-u
, --update 当把文件从一个目录复制到另一个目录时,仅复制目标目录中不存在的文件,或者是文件内容新于目标目录中已经存在的文件-v
, --verbose 显示翔实的命令操作信息
- 举例
cp file1 file2
复制文件 file1 内容到文件 file2。如果 file2 已经存在, file2的内容会被 file1 的内容重写。如果 file2 不存在,则会创建 file2cp -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
- 移动和重命名文件(完成操作之后,原来的文件名不再存在)
mv item1 item2
ormv item... directory
:使用方法与cp
相似- 可选项
-i
--interactive 在重写一个已经存在的文件之前,提示用户确认信息。 如果不指定这个选项, mv 命令会默认重写文件内容-u
--update 当把文件从一个目录移动另一个目录时,只是移动不存在的文件,或者文件内容新于目标目录相对应文件的内容-v
--verbose 当操作 mv 命令时,显示翔实的操作信息
-
rm
(小技巧:最好先使用ls
命令来确认待删除文件!)- 删除文件和目录
rm item...
:“item” 代表一个或多个文件或目录- 可选项
-i
, --interactive 在删除已存在的文件前,提示用户确认信息。 如果不指定这个选项, rm 会默默地删除文件-r
, --recursive 递归地删除文件,这意味着,如果要删除一个目录,而此目录又包含子目录,那么子目录也会被删除。要删除一个目录,必须指定这个选项-f
, --force 忽视不存在的文件,不显示提示信息。这选项颠覆了“–interactive”选项-v
, --verbose 在执行 rm 命令时,显示翔实的操作信息
- 举例:
rm file1
默默地删除文件rm -i file1
除了在删除文件之前,提示用户确认信息之外,和上面的命令作用一样rm -r file1 dir1
删除文件 file1, 目录 dir1,及 dir1 中的内容rm -rf file1 dir1
同上,除了如果文件 file1,或目录 dir1 不存在的话, rm 仍会继续执行
命令
-
什么是命令
- 是一个可执行程序,就像我们所看到的位于目录/usr/bin 中的文件一样。属于这一类的程序,可以编译成二进制文件,诸如用 C 和 C++ 语言写成的程序, 也可以是由脚本语言写成的程序,比如说 shell, perl, python, ruby,等等
- 是一个内建于 shell 自身的命令。 bash 支持若干命令,内部叫做 shell 内部命令(builtins)。例如, cd 命令,就是一个 shell 内部命令
- 是一个 shell 函数。这些是小规模的 shell 脚本,它们混合到环境变量中。在后续的章节里,我们将讨论配置环境变量以及书写 shell 函数。但是现在,仅仅意识到它们的存在就可以了
- 是一个命令别名。我们可以定义自己的命令,建立在其它命令之上
-
相关命令
- type:
type command
,显示命令的类型 - which:
which program
,显示可执行程序(不包括内部命令和命令别名)的位置 - help:
help command
,显示shell内部命令帮助文档。对于某些可执行程序,有program --help
来显示用法 - man:
man program
,是一个分页程序,用于显示手册文档;查找特定章节:man section search_term
;显示关键字匹配:man -k keyword
- apropos:
apropos keyword
,粗糙简单地显示手册种的关键字匹配,类似于man -k keyword
- whatis:
whatis ...
,显示匹配特定关键字的手册页的名字和一行命令说明 - info:
info ...
,info 程序读取 info 文件, info 文件是树型结构,分化为各个结点,每一个包含一个题目。info 文件包含超级链接,它可以让你从一个结点跳到另一个结点。一个超级链接可通过它开头的星号来辨别出来,把光标放在它上面并按下 enter 键,就可以激活它 - alias:
alias
显示所有已有别名的命令;alias name='string'
,创建别名命令;unalias ...
删除别名命令- 举例:创建别名命令:
alias foo='cd /usr; ls; cd -'
- alias创建的别名命令仅在当前会话中有效,退出当前会话并重新登录后则不再存在,是暂时性的
- 举例:创建别名命令:
- type:
重定向
-
标准输入(stdin,0)、标准输出(stdout,1)、标准错误(stderr,2)
- 默认情况下,标准输出和标准错误都连接到屏幕,而标准输入连接到键盘
-
I/O重定向
- I/O 重定向允许我们可以更改输出走向和输入来向
- 重定向标准输出
>
:... > output_file
;重定向符来重定向输出结果时,目标文件总是从开头被重写;> get_empty_file
是删除文件内容或新建一个文件的小技巧;>>
用于追加标准输出 - 重定向标准错误
2>
:标准输入,输出和错误, shell 内部参考它们为文件描述符 0, 1 和 2 - 重定向标准输出和错误到同一个文件:
- 方法一:重定向文件描述符 2(标准错误)到文件描述符 1(标准输出)使用表示法
2>&1
,标准错误的重定向必须总是出现在标准输出重定向之后。举例:ls -l /bin/usr > ls-output.txt 2>&1
- 方法二:表示法
&>
来重定向标准输出和错误到文件,当然也可追加文本内容&>>
。举例:ls -l /bin/usr &> ls-output.txt
- 方法一:重定向文件描述符 2(标准错误)到文件描述符 1(标准输出)使用表示法
- 处理不需要的输出:通过重定向输出结果到一个特殊的叫做 “/dev/null” 的文件。这个文件是系统设备,叫做位存储桶,它可以接受输入,并且对输入不做任何处理
- 举例:
ls -l /bin/usr 2> /dev/null
- 举例:
- 重定向标准输入
<
:cat
-
相关命令
- cat:
- 作用一:
cat [file]
,读取一个或多个文件的内容,显示文件而没有分页,常用于显示较短的文件内容或拼接多个文件的内容 - 作用二:仅输入
cat
,而不接任何参数表示接收来自键盘的输入,从键盘输入完之后按下Ctrl-d
表示已到达文件末尾(EOF);这种方法可用于记录简短的文本内容,例如:先输入cat > lazy_dog.txt
,再输入文本内容
- 作用一:
- uniq:从标准输入或单个文件名参数接受数据有序列表(即需先使用
sort
命令),从数据列表中删除任何重复行;* -d
返回重复的数据,* -u
返回仅出现一次的数据,* -c
用于显示该行重复的次数 - wc:wc 打印出来三个数字依次是行数,单词数,字节数;
wc -l
只显示行数,wc -w
只显示单词数,wc -c
只显示字节数;通常和管道结合使用- 举例:
ls /bin /usr/bin | sort | uniq | wc -l
- 举例:
- grep:
grep pattern [file...]
用于查找内容包含指定的范本样式的文件;* -a
表示不忽略二进制数据,* -c
计算符合样式的行数,* -e<范本样式>
指定正则表达式来查找,* -i
忽略大小写来匹配,* -l
输出包含匹配内容的文件名,* -L
输出不包含匹配内容的文件名,* -n
加上行标,* -h
在显示符合样式的那一行之前表示该行所属的文件名称 - head:查看文件的开头部分的内容,
* -q
隐藏文件名,* -v
显示文件名,* -c
显示字节数,* -n
显示行数 - tail:查看文件的结尾部分的内容,
* -q
隐藏文件名,* -v
显示文件名,* -c
显示字节数,* -n
显示行数,* -f
循环读取文件内容,使用Ctrl-c来结束;* --pid=PID
与-f合用,表示在进程ID,PID死掉之后结束 - tee:从 Stdin 读取数据,并同时输出到 Stdout 和文件(既想把输出内容保存到文件中,又想在屏幕上看到输出内容)
- 举例:
ls /usr/bin | tee ls.txt | grep zip
- 举例:
- cat:
-
管道
|
command1 | command2
:命令可以从标准输入读取数据,然后再把数据输送到标准输出,命令的这种能力被一个 shell 特性所利用,这个特性叫做管道线
Shell
-
相关命令
- echo:在标准输出中打印出它的文本参数,用于字符串输出
echo -n
:删除输出结果末尾的换行符
- echo:在标准输出中打印出它的文本参数,用于字符串输出
-
展开
- 字符展开:通过展开,所输入的字符,在 shell 对它起作用之前,会展开成为别的字符。举例:
echo *
- 路径名展开:
echo [[:upper:]]*
,echo /usr/*/share
;隐藏路径名展开:echo *
不能直接显示隐藏文件,使用echo .*
可以显示隐藏文件但也可能显示出错误结果(比如带..
的当前工作目录以及它的父目录),一种可行的模式ls -d .[!.]?*
或直接用ls -A
- 波浪线展开:
~
用在一个单词的开头时,它会展开成指定用户的家目录名,如果没有指定用户名,则是当前用户的家
目录。举例:echo ~
,echo ~foo
- 算术表达式展开:
$((expression))
,算术表达式只支持整数(全部是数字,不带小数点),支持加+、减-、乘*、整数除/、取余%、取幂**。举例:echo $(((5**2) * 3))
- 花括号展开
{}
:可从花括号的模式中批量创建多个文本字符串,花括号表达式本身可包含一个由逗号分开的字符串列表,或者一系列整数,或者单个的字符串;这种模式不能嵌入空白字符。举例:echo Front-{A,B,C}-Back
,echo {Z..A}
,echo a{A{1,2},B{3,4}}b
,mkdir {2007..2009}-0{1..9} {2007..2009}-{10..12}
- 参数展开:类似于变量名来获取数据与信息。举例:
echo $USER
,printenv | less
- 命令替换:命令替换允许把一个命令的输出作为一个展开模式来使用。举例:
ls -l $(which cp)
,file $(ls /usr/bin/* | grep zip)
,也可以使用倒引号``来替换美元符号和括号
- 字符展开:通过展开,所输入的字符,在 shell 对它起作用之前,会展开成为别的字符。举例:
-
引用机制:用来删除多余的空格:
echo this is a test
;用来将未定义的$1替换为空字符串:echo The total is $100.00
- 双引号:把文本放在双引号中, shell 使用的特殊字符,除了 $, \ (反斜杠),和 ·(倒引号)之外,则失去它们的特殊含义,被当作普通字符来看待。这意味着单词分割,路径名展开,波浪线展开,和花括号展开都被禁止,然而参数展开,算术展开,和命令替换仍然执行;举例:处理文件名带空格的文件
ls -l "two words.txt"
- 单引号:禁止所有展开!(比双引号更强的禁止程度)
- 双引号:把文本放在双引号中, shell 使用的特殊字符,除了 $, \ (反斜杠),和 ·(倒引号)之外,则失去它们的特殊含义,被当作普通字符来看待。这意味着单词分割,路径名展开,波浪线展开,和花括号展开都被禁止,然而参数展开,算术展开,和命令替换仍然执行;举例:处理文件名带空格的文件
-
转义字符
\
:- 经常在双引号中使用转义字符\,来有选择地阻止展开
- 但在单引号中,反斜杠失去它的特殊含义,它被看作普通字符!
键盘
- 光标移动
Ctrl-a
:移动光标到行首Ctrl-e
:移动光标到行尾Ctrl-f
:光标前移一个字符,和右箭头作用一样Ctrl-b
:光标后移一个字符,和左箭头作用一样Alt-f
:光标前移一个单词Alt-b
:光标后移一个单词Ctrl-l
:清空屏幕,类似于clear
命令
- 修改文本
Ctrl-d
:删除光标处的字符Ctrl-t
:交换光标位置的字符和光标前面(左边)的一个字符Alt-t
:交换光标位置的单词和光标前面(左边)的一个单词(整体交换)Alt-l
:把从光标位置到字尾的字符转换成小写字母Alt-u
:把从光标位置到字尾的字符转换成大写字母
- 剪切与粘贴(Readline 的文档使用术语 killing 和 yanking 来指我们平常所说的剪切和粘贴。剪切下来的本文被存储在一个叫做剪切环 (kill-ring) 的缓冲区中)
Ctrl-k
:剪切从光标位置(含)到行尾的文本Ctrl-u
:剪切从光标位置(不含)到行首的文本Alt-d
:剪切从光标位置到当前单词词尾的文本Alt-Backspace
:剪切从光标位置到词头的文本。如果光标在一个单词的开头,剪切前一个单词Ctrl-y
:把剪切环中的文本粘贴到光标位置(注意:是剪切环缓存的文本!)
- 自动补全(tab键)
- 一般地可补全:路径名、文件名、变量名(以$开头)、用户名(以~开头)、命令(一行的第一个单词)、主机名(@开头)
- 历史命令(默认存储最后500个命令,
history | less
)Ctrl-p
:移动到上一个历史条目,类似于上箭头按键Ctrl-n
:移动到下一个历史条目,类似于下箭头按键Alt-<
:移动到历史列表开头Alt->
:移动到历史列表结尾,即当前命令行Ctrl-r
:反向递增搜索。从当前命令行开始,向上递增搜索。找到某条历史命令后,可使用Ctrl-j
来继续编辑或运行当前命令Alt-p
:反向搜索,不是递增顺序。输入要查找的字符串,然后按下Enter,执行搜索Alt-n
:向前搜索,非递增顺序Ctrl-r
:执行历史列表中的当前项,并移到下一个。如果想要执行历史列表中一系列的命令,这很方便!!
:重复最后一次执行的命令(相当于按下上箭头按键和 Enter 键)!number
:重复历史列表中第 number 行的命令!string
:重复最近历史列表中,以这个字符串开头的命令!?string
:重复最近历史列表中,包含这个字符串的命令
权限
-
用户、组成员、其他人
id
:查看自己的身份信息- 用户帐户(用户(登录)名, uid, gid,帐号的真实姓名,家目录,和登录 shell)定义在/etc/passwd 文件里面;用户组定义在/etc/group 文件里面(除了普通用户,还有各种系统用户);当用户帐户和用户组创建以后,这些文件随着文件/etc/shadow 的变动而修改,文件/etc/shadow 包含了关于用户密码的信息
-
文件类型(文件属性列表的第一个字符)
-
:普通文件;d
:目录;l
:符号链接,剩余的文件属性总是”rwxrwxrwx”,而且都是虚拟值。真正的文件属性是指符号链接所指向的文件的属性;c
:一个字符设备文件,这种文件类型是指按照字节流来处理数据的设备,比如终端机、调制解调器;b
:一个块设备文件,这种文件类型是指按照数据块来处理数据的设备,例如一个硬盘、CD-ROM 盘
-
文件模式及权限修改
- 八进制,二进制,权限(读r、写w、执行x):(0,000,—),(1,001,–x),(2,010,-w-),(3,011,-wx),(4,100,r–),(5,101,r-x),(6,110,rw-),(7,111,rwx)。常见的755表示为-rwxr-xr-x
chmod
:更改文件或目录的模式(权限),支持八进制数字表示法或符号表示法(注意:只有文件的所有者或者超级用户才能更改文件或目录的模式);* -c
若该文件权限确实已经更改,才显示其更改动作;* -v
显示权限变更的详细资料;* -R
对目前目录下的所有文件与子目录进行相同的权限变更(即以递归的方式逐个变更)- chmod命令符号:
u
文件或目录的所有者;g
用户组;o
其他人;a
是”u”, ”g” 和“o”三者的联合 - chmod操作符号:
+
表示添加一种权限;-
表示去掉一种权限;=
表示指定某些权限,并去掉其他权限 - 举例:
u+x
为文件所有者添加可执行权限;+x
为文件所有者,用户组,和其他所有人添加可执行权限,等价于a+x
;ugo-rw
删除用户、组、其他人的读权限和写权限;go=rw
给群组的主人和任意文件拥有者的人读写权限,若群组的主人或全局之前已经有了执行的权限,他们将被移除;u+x,go=rw
给文件拥有者执行权限并给组和其他人读和执行的权限。多种设定可以用逗号分开
-
设置默认权限
umask
:掩码的二进制形式中,出现数字 1 的位置,相应地关掉一个文件模式属性(默认为0002或0022,若设置为0000则表示关闭所有掩码)- 举例:原始文件权限为-rw-rw-rw-,设置掩码为
umask 0022
(000 000 010 010),则掩码后文件的权限更改为-rw-r–r–
-
特殊权限
- setuid位(八进制4000)
s
:当应用到一个可执行文件时,它把有效用户 ID 从真正的用户(实际运行程序的用户)设置成程序所有者的 ID,这种操作通常会应用到一些由超级用户所拥有的程序;因为这会引起安全方面的问题,所有可以设置 setuid 位的程序个数,必须控制在绝对小的范围内。举例:-rwsr-xr-x
一个程序被设置为 setuid 属性 - setgid位(八进制2000)
g
:相似于 setuid 位,把有效用户组 ID 从真正的用户组 ID 更改为文件所有者的组 ID;如果设置了一个目录的 setgid 位,则目录中新创建的文件具有这个目录用户组的所有权,而不是文件创建者所属用户组的所有权。举例:drwxrwsr-x
具有 setgid 属性的目录 - sticky位(八进制1000)
t
:继承于 Unix,在 Unix 中,它可能把一个可执行文件标志为“不可交换的”。在 Linux 中,会忽略文件的 sticky 位,但是如果一个目录设置了 sticky 位,那么它能阻止用户删除或重命名文件,除非用户是这个目录的所有者,或者是文件所有者,或是超级用户。这个经常用来控制访问共享目录,比方说/tmp。举例:drwxrwxrwt
设置了 sticky 位的目录
- setuid位(八进制4000)
-
更改身份
su [-[l]] [user]
:以其他用户身份和组 ID 运行一个 shell- 若包含
-l
则需指定用户,会加载此用户的 shell 环境,并且工作目录会更改到这个用户的家目录 su -
表示使用超级用户,输入正确密码登录成功后,出现一个新的shell 提示符#
(而不是$
),当前工作目录更改为超级用户的家目录(通常是/root),且能执行超级用户所使用的命令;当工作完成后,输入 “exit”,则返回到原来的 shell。和仅使用su
的区别:su
只是切换了root身份,但Shell环境仍然是普通用户的Shell,pwd一下,工作目录仍然是普通用户的工作目录;su -
连用户和Shell环境一起切换成root身份,工作目录变成root的工作目录su -c 'command'
在不启动新shell的情况下,执行单条命令。举例:su -c 'ls -l /root/*'
- 若包含
sudo
:以另一个用户身份执行命令- sudo 命令在很多方面都相似于 su 命令,但是 sudo 还有一些非常重要的功能,管理员能够配置 sudo 命令,从而允许一个普通用户以不同的身份(通常是超级用户),通过一种非常可控的方式来执行命令。尤其是,只有一个用户可以执行一个或多个特殊命令时,(更体现了 sudo命令的方便性)。另一个重要差异是 sudo 命令不要求超级用户的密码,用户使用他/她自己的密码来认证
-
更改用户
chown [owner][:[group]] file...
:用来更改文件或目录(后面这个file参数)的所有者(owner)和用户组(group),使用这个命令需要超级用户权限- chown参数实例:
bob
:把文件所有者从当前属主更改为用户 bobbob:users
:把文件所有者改为用户 bob,文件用户组改为用户组 users:admins
:把文件用户组改为组 admins,文件所有者不变bob:
:文件所有者改为用户 bob,文件用户组改为,用户 bob 登录系统时,所属的用户组
useradd
groupadd
进程
- 查看进程
ps
- 仅用
ps
:默认显示当前控制台下属于当前用户的进程。TTY 是 “Teletype” 的简写,是指进程的控制终端,这里Unix 展示它的年龄,若为?则表示没有控制终端。 TIME 字段表示进程所消耗的 CPU 时间数量 ps -u username
查看指定用户信息ps -o pid,ppid,pgrp,session,tpgid,comm
输出指定字段ps ef
:查看系统上的运行的所有进程+命令行,System V风格;ps ef | grep ...
常用组合用法,查找特定进程ps aux
:能够显示属于每个用户的进程信息,BSD 风格;ps aux | grep ...
- USER:用户 ID. 进程的所有者
- %CPU:以百分比表示的 CPU 使用率
- %MEM:以百分比表示的内存使用率
- VSZ:虚拟内存大小
- RSS:进程占用的物理内存的大小,以千字节为单位
- START:进程运行的起始时间。若超过 24 小时,则用天表示
- 进程状态STAT:
- R:进程正在运行或准备运行;
- S:正在睡眠。进程没有运行,而是,正在等待一个事件,比如说,一个按键或者网络数据包;
- D:不可中断睡眠。进程正在等待 I/O,比方说,一个磁盘驱动器的 I/O;
- T:指示进程停止运行;
- Z:一个死进程或“僵尸”进程。这是一个已经终止的子进程,但是它的父进程还没有清空它。(父进程没有把子进程从进程表中删除);
- <:一个高优先级进程。这可能会授予一个进程更多重要的资源,给它更多的 CPU 时间。进程的这种属性叫做 niceness。具有高优先级的进程据说是不好的(less nice),因为它占用了比较多的 CPU 时间,这样就给其它进程留下很少时间;
- N:低优先级进程。一个低优先级进程(一个“好”进程)只有当其它高优先级进程执行之后,才会得到处理器时间
- 仅用
pstree
:以树结构来显示进程关系pstree -p
:显示PID;pstree PID
以树状图显示进程PID为的进程以及子孙进程;pstree -p PID
如果有-p参数则同时显示每个进程的PID
top
- 后台执行
&
- 启动一个程序,让它立即在后台运行,我们在程序命令之后,加上 “&” 字符
- 工作控制:命令后台执行后会返回一个工作序号jobspec和PID数字,可直接使用
ps
查看进程,或使用jobs
命令查看此任务 $!
:包含放到后台执行的最后一个任务的进程 ID 号
- 查看任务状态
jobs
- 前台执行
- 一个在后台运行的进程对一切来自键盘的输入都免疫,也不能用 Ctrl-c 来中断它。使用 fg命令,让一个进程返回前台执行
fg %工作序号
:fg命令之后,跟随着一个百分号和工作序号(使用jobs
查看)。如果我们只有一个后台任务,那么 jobspec 是可有可无的
Ctrl-c
:中断进程,发送一个叫做 TSTP(终端停止)的信号;Ctrl-z
:停止进程,发送一个叫做 TSTP(终端停止)的信号- 信号Signals(是操作系统与程序之间进行通信的一种方式)
1
,HUP:挂起。发送这个信号到终端机上的前台程序,程序会终止。许多守护进程也使用这个信号,来重新初始化。这意味着,当发送这个信号到一个守护进程后,这个进程会重新启动,并且重新读取它的配置文件2
,INT:中断。实现和 Ctrl-c 一样的功能,由终端发送。通常,它会终止一个程序3
,QUIT:退出9
,KILL: 杀死。Kill 信号从不发送到目标进程,而是内核立即终止这个进程。当一个进程以这种方式终止的时候,它没有机会去做些“清理”工作,或者是保存劳动成果。因为这个原因,把 KILL 信号看作杀手锏,当其它终止信号失败后,再使用它,它不能被忽略11
,SEGV:段错误。如果一个程序非法使用内存,就会发送这个信号。也就是说,程序试图写入内存,而这个内存空间是不允许此程序写入的15
,TERM:终止。这是 kill 命令发送的默认信号。如果程序仍然“活着”,可以接受信号,那么这个信号终止18
,CONT:继续。在停止一段时间后,进程恢复运行19
,STOP:停止。个信号导致进程停止运行,而没有终止。就像 KILL 信号那样,它不被发送到目标进程,因此它不能被忽略20
,TSTP:终端停止。当按下 Ctrl-z 组合键后,终端发送这个信号。不像 STOP 信号, TSTP 信号由目标进程接收,且可能被忽略28
,WINCH:改变窗口大小。当改变窗口大小时,系统会发送这个信号。一些程序,像 top 和 less 程序会响应这个信号,按照新窗口的尺寸,刷新显示的内容
kill [-signal] PID...
:用来给程序发送信号,默认情况下发送 TERM(终止)信号!需要拥有超级用户权限才能杀死别人的进程kill -l
查看所有信号kill -u username
杀死指定用户所有进程
killall [-u user] [-signal] name...
:给匹配特定程序或用户名的多个进程发送信号,与 kill 不同的是它会杀死指定名字的所有进程。需要拥有超级用户权限才能杀死别人的进程vmstat
:查看虚拟内存使用情况xload
:一个图形界面程序,可以画出系统负载的图形tload
:与 xload 程序相似,但是在终端中画出图形。使用 Ctrl-c,来终止输出
Shell环境
- 什么存储在环境变量中:环境变量,shell变量
- 查看变量
printenv | less
查看环境变量、printenv USER
查看用户set | less
查看环境变量echo $HOME
查看变量alias
查看别名:别名既不可用 set 命令也不可用 printenv 命令显示,使用 alias 命令查看
- 常见的环境变量(不同发行版本可能有所不同)
- DISPLAY 如果你正在运行图形界面环境,那么这个变量就是你显示器的名字。通常,它是”:0”,意思是由 X 产生的第一个显示器
- EDITOR 文本编辑器的名字
- SHELL shell 程序的名字
- HOME 用户家目录
- LANG 定义了字符集以及语言编码方式
- OLD_PWD 先前的工作目录
- PAGER 页输出程序的名字。这经常设置为/usr/bin/less
- PATH 由冒号分开的目录列表,当你输入可执行程序名后,会搜索这个目录列表
- PS1 Prompt String 1. 这个定义了你的 shell 提示符的内容。随后我们可以看到,这个变量内容可以全面地定制
- PWD 当前工作目录
- TERM 终端类型名。类 Unix 的系统支持许多终端协议;这个变量设置你的终端仿真器所用的协议
- TZ 指定你所在的时区。大多数类 Unix 的系统按照协调时间时(UTC) 来维护计算机内部的时钟,然后应用一个由这个变量指定的偏差来显示本地时间
- USER 你的用户名
- 系统建立shell环境
- 当我们登录系统后,启动 bash 程序,并且会读取一系列称为启动文件的配置脚本,这些文件定义了默认的可供所有用户共享的 shell 环境。然后是读取更多位于我们自己家目录中的启动文件,这些启动文件定义了用户个人的 shell 环境。精确的启动顺序依赖于要运行的 shell 会话类型。有两种 shell 会话类型:一个是登录 shell 会话,另一个是非登录 shell 会话。在普通用户看来,文件
~/.bashrc
可能是最重要的启动文件,因为它几乎总是被读取。非登录 shell 默认会读取它,并且大多数登录 shell 的启动文件会以能读取 ~/.bashrc 文件的方式来书写- shell会话的启动文件
- /etc/profile:应用于所有用户的全局配置脚本
- ~/.bash_profile:用户私人的启动文件。可以用来扩展或重写全局配置脚本中的设置
- ~/.bash_login:如果文件 ~/.bash profile 没有找到, bash 会尝试读取这个脚本
- ~/.profile:如果文件 ~/.bash profile 或文件 ~/.bash login 都没有找到,bash 会试图读取这个文件。这是基于 Debian 发行版的默认设置,比方说 Ubuntu
- 非shell会话的启动文件(除了读取以上启动文件之外,非登录 shell 会话也会继承它们父进程的环境设置,通常是一个登录 shell)
- /etc/bash.bashrc:应用于所有用户的全局配置文件
- ~/.bashrc:用户私有的启动文件。可以用来扩展或重写全局配置脚本中的设置
- shell会话的启动文件
export PATH
:让这个 shell 的子进程可以使用 PATH 变量的内容
- 当我们登录系统后,启动 bash 程序,并且会读取一系列称为启动文件的配置脚本,这些文件定义了默认的可供所有用户共享的 shell 环境。然后是读取更多位于我们自己家目录中的启动文件,这些启动文件定义了用户个人的 shell 环境。精确的启动顺序依赖于要运行的 shell 会话类型。有两种 shell 会话类型:一个是登录 shell 会话,另一个是非登录 shell 会话。在普通用户看来,文件
- 修改启动文件并强制刷新
- 文件备份:为保险起见,可先复制
.bashrc
文件为.bashrc.bak
,常见的备份文件后缀:.bak、.sav、.old、.orig - 修改文件:添加修改内容以及代码注释
- 激活修改:
source filename
或. filename
(source
的另一种形式为一个点.
),在当前bash环境下读取并执行filename中的命令。举例:source .bashrc
- 文件备份:为保险起见,可先复制
VI
- 光标移动按键
- l or 右箭头:向右移动一个字符,前可加number
- h or 左箭头:向左移动一个字符,前可加number
- j or 下箭头:向下移动一行,前可加number
- k or 上箭头:向上移动一行,前可加number
- 0 (零按键):移动到当前行的行首
- ^:移动到当前行的第一个非空字符
- $:移动到当前行的末尾
- w:移动到下一个单词或标点符号的开头
- W:移动到下一个单词的开头,忽略标点符号
- b:移动到上一个单词或标点符号的开头
- B:移动到上一个单词的开头,忽略标点符号
- Ctrl-f or Page Down:向下翻一页
- Ctrl-b or Page Up:向上翻一页
- numberG:移动到第 number 行。例如, 1G 移动到文件的第一行
- G:移动到文件末尾
- 文本编辑
- 撤销:命令模式下
u
- 恢复:命令模式下
Ctrl-r
- 追加文本:
a
在光标后一位开始添加,A
在当前行末尾开始添加 - 另起一行:
o
下面插入一行,O
上面插入一行
- 撤销:命令模式下
- 删除或剪切
- x:当前字符
- 3x:当前字符及其后的两个字符
- dd:当前行
- 5dd:当前行及随后的四行文本
- dW:从光标位置开始到下一个单词的开头
- d$:从光标位置开始到当前行的行尾
- d0:从光标位置开始到当前行的行首
- d^:从光标位置开始到文本行的第一个非空字符
- dG:从当前行到文件的末尾
- d20G:从当前行到文件的第 20 行
- 粘贴
- p:把剪切板中的文本粘贴到光标位置之后、粘贴某行内容到下一行
- P:粘贴在光标之前、粘贴某行内容到上一行
- 复制
- yy:当前行
- 5yy:当前行及随后的四行文本
- yW:从当前光标位置到下一个单词的开头
- y$:从当前光标位置到当前行的末尾
- y0:从当前光标位置到行首
- y^:从当前光标位置到文本行的第一个非空字符
- yG:从当前行到文件末尾
- y20G:从当前行到文件的第 20 行
- 连接行
- J:连接当前行和它下面的一行(额外增加一个空格来连接这两行)
- 查找
- 当前行查找字符:先按
f
,再输入待查找的字符;后续按分号可继续查找下一个此字符 - 文件内查找:先按
/
,再输入待查找的字符、单词、短语、正则表达式,再按下回车;通过按 n 命令来重复先前的查找
- 当前行查找字符:先按
- 替换
- 全局查找与替换:
:%s/before/after/g
表示全局替换,:%4,7s/before/after/g
表示对4-7行替换- : 冒号字符运行一个 ex 命令
- %:指定要操作的行数。 % 是一个快捷方式,表示从第一行到最后一行。另外,操作范围也可以用 1,5 来代替(因为我们的文件只有 5 行文本),或者用 1,$ 来代替,意思是“从第一行到文件的最后一行。”如果省略了文本行的范围,那么操作只对当前行生效
- s:指定操作。这里是替换(查找与替代)操作
- /before/after:/before/after
- g:这是“全局”的意思,意味着对文本行中所有匹配的字符串执行查找和替换操作。如果省略 g,则只替换每个文本行中第一个匹配的字符串
- 全局查找与替换,并确认每一处替换:
:%s/before/after/gc
,最后多加一个c;replace with Line (y/n/a/q/l/^E/^Y)?
这里用于确认每一处的操作- y:直接执行本处的替换操作
- n:跳过当前这个匹配的实例
- a:对这个及随后所有匹配的字符串执行替换操作
- q or esc:退出替换操作
- l:执行这次替换并退出。 l 是“last”的简写
- Ctrl-e, Ctrl-y:分别是向下滚动和向上滚动。用于查看建议替换的上下文
- 全局查找与替换:
- 多文件编辑
vi file1 file2 file3...
:同时打开多个文件- 切换文件:
:n
切换到下一个;:n!
不保存对当前文件的修改,强制切换文件:N
切换到上一个buffers
:查看VI中的所有编辑的文件列表buffer n
:切换到第n个文件;buffer! n
:不保存,强制切换
- 在当前行插入另一个文件:
:r another_file
- 很清楚
- 文件保存
- 文件保存并退出:
ZZ
、:wq
- 文件另存为(保存副本):
:w new_file
- 文件保存并退出:
软件包管理
-
查找软件包
- Debian:
apt-get update
,apt-cache search search_string
- Red Hat:
yum search search_string
- Debian:
-
安装软件包(package_name指软件包实际名称,package_file指包含在软件包中的文件名)
- 从资源库中安装
- Debian:
apt-get update; apt-get install package_name
(举例:apt-get update; apt-get install emacs
) - Red Hat:
yum install package_name
- Debian:
- 通过安装包安装(底层软件包安装)
- Debian:
dpkg --install package_file
- Red Hat:
rpm -i package_file
(使用底层的 rpm 程序来执行安装任务,若 rpm 程序发现缺少了一个依赖,则会报错并退出。举例:rpm -i emacs-22.1-7.fc7-i386.rpm
)
- Debian:
- 从资源库中安装
-
删除软件包
- Debian:
apt-get remove package_name
(举例:apt-get remove emacs
) - Red Hat:
yum erase package_name
- Debian:
-
更新软件包
- 从资源库中更新
- Debian:
apt-get update; apt-get upgrade
(举例:apt-get remove emacs
) - Red Hat:
yum erase package_name
- Debian:
- 通过安装包更新(底层软件包安装)
- Debian:
dpkg --install package_file
(与安装的命令相同) - Red Hat:
rpm -U package_file
- Debian:
- 从资源库中更新
-
列出安装包
- Debian:
dpkg --list
- Red Hat:
rpm -qa
- Debian:
-
确认软件包状态
- Debian:
dpkg --status package_name
- Red Hat:
rpm -q package_name
- Debian:
-
显示软件包信息
- Debian:
apt-cache show package_name
- Red Hat:
yum info package_name
- Debian:
-
查找安装了某个文件的软件包(包文件识别)
- Debian:
dpkg --search file_name
- Red Hat:
rpm -qf file_name
- Debian:
存储媒介
- 文件系统的挂载与卸载
mount
:用来挂载文件系统。执行这个不带参数的命令,将会显示一系列当前挂载的文件系统,其显示列表格式为:设备 on 挂载点 type 文件系统类型(可选的)。- 举例:/dev/sda2 on / type ext3 (rw),表示设备/dev/sda2 作为根文件系统(/)被挂载,文件系统类型是 ext3,并且可读可写(rw)
umount
:用于卸载文件系统
- 设备分区操作
fdisk
:可以在设备上编辑,删除,和创建分区
- 创建文件系统
mkfs
- 数据整体迁移
dd
:d if=input_file of=output_file [bs=block_size [count=blocks]]
把数据块从一个地方复制到另一个地方(注意:由于可能会错误输入 if 或 of 的规范,导致文件数据误删,因此按下回车前必须得再三确认!)- 举例:
dd if=/dev/sdb of=/dev/sdc
把第一个驱动器(或硬盘)中的所有数据复制到第二个驱动器中;dd if=/dev/sdb of=flash_drive.img
把一个驱动器的内容复制到一个普通文件中供以后恢复或复制数据
- 举例:
- 创建CD-ROM与写入CD-ROM
- 验证文件的完整性
md5sum
:产生一个独一无二的十六进制数字,比如:生成一个文件的 checksum 数字
网络系统
-
检查和检测网络
ping
:发送一个特殊的网络数据包,叫做 IMCP ECHO_REQUEST,到一台指定的主机,大多数接收这个包的网络设备将会回复它,来允许网络连接验证- 一个成功执行的“ping”命令会意味着网络的各个部件(网卡,电缆,路由,网关)都处于正常的工作状态
traceroute(某些系统叫做 tracepath)
:会显示从本地到指定主机要经过的所有“跳数”的网络流量列表;对于那些提供标识信息的路由器,我们能看到它们的主机名, IP 地址和性能数据,这些数据包括三次从本地到此路由器的往返时间样本。对于那些没有提供标识信息的路由器(由于路由器配置,网络拥塞,防火墙等方面的原因),则会看到几个星号 ***netstat
:用来检查各种各样的网络设置和统计数据netstat -ie
:查看系统中的网络接口netstat -r
:显示内核的网络路由表,展示了系统是如何配置网络之间发送数据包的
-
文件传输
ftp
:ftp fileserver
连接到远端ftp服务器,接着需要输入用户名(如:anonymous)以及密码(如:user@example.com)lcd Desktop
在ftp服务器中,对本地目录进行切换get file
:从ftp服务器中下载文件file到本地的当前目录中bye
、quit
、exit
:退出ftp服务器
lftp
wget
:许多选项允许 wget 递归地下载,在后台下载文件(你退出后仍在下载),能完成未下载全的文件(从网络和 FTP 网站两者上都能下载数据,不只能下载单个文件,多个文件,甚至整个网站都能下载)- 举例:
wget http://linuxcommand.org/index.php
下载一个网页
- 举例:
-
远程连接(安全通信)
ssh
:由两部分组成。 SSH 服务器运行在远端主机上运行,在端口号 22 上监听将要到来的连接,而 SSH 客户端用在本地系统中,用来和远端服务器通信。若不能成功连接,则需考虑原因(如:删除∼/.ssh/known hosts
文件中的废弃(或攻击性)密钥)ssh remote-sys
:连接远程服务器,需要输入密码ssh username@remote-sys
:以某个用户来连接远程服务器,需要输入密码ssh remote-sys command
:在远程服务器中执行某条命令,并将结果返回到本地系统shell会话中ssh remote-sys 'ls \*' > dirlist.txt
:在远端系统中执行 ls 命令,并把命令输出重定向到本地系统中的一个文件里面(命令中的单引号是不想让路径名展开操作在本地执行,而希望它在远端系统中被执行)ssh remote-sys 'ls * > dirlist.txt'
:在远端系统中执行 ls 命令,并重定向到远端服务器中(注意:单引号的范围)
scp
:安全复制文件,与熟悉的 cp 程序非常相似。最显著的区别就是源或者目标路径名要以远端主机的名字,后跟一个冒号字符开头scp remote-sys:document.txt .
:从远端系统 remote-sys 的家目录下复制文档 document.txt,到我们本地系统的当前工作目录下(.)scp username@remote-sys:document.txt .
:指定某个用户
sftp
: 与 ftp 程序很相似。但它不用明码形式来传递数据,而使用加密的 SSH 通道。 sftp 有一个重要特性强于传统的 ftp 命令,就是** sftp 不需要远端系统中运行 FTP 服务器,仅需要 SSH 服务器,这意味着任何一台能用 SSH 客户端连接的远端机器,可当作类似于 FTP 的服务器来使用**
查找文件
- 查找文件
-
locate
:快速搜索路径名数据库,并且输出每个与给定字符串相匹配的文件路径名- 举例:
locate bin/zip
:输出任一个包含字符串“bin/zip”的路径名
- 举例:
-
find
:locate 程序只能依据文件名来查找文件,而 find 程序能基于各种各样的属性,搜索一个给定目录(以及它的子目录),来查找文件find path
:在某path下查找文件- 添加测试条件(各种条件的添加顺序很重要,因为其与各种逻辑运算符顺序相结合,顺序不同则返回结果可能不同!!!)
find path -type [bcdfl]
:指定文件类型来查找文件。b:块设备文件;c:字符设备文件;d:目录;f:普通文件;l:符号链接-name
:文件名条件,一般加入双引号以防止shell进行路径名展开。比如:-name "\*.JPG"
-size
:文件大小条件,文件大小参数有:b:512 个字节块(如果没有指定单位,则这是默认值);c:字节;w:两个字节的字;k:千字节(1024个字节单位);M:兆字节;G:千兆字节。==出现数值参数时,均可使用 “+”(大于数值)、“-”(小于数值) 或 不指定(精确等于数值)来进行限制。==比如:-size +1M
指大于1M的文件- 常见测试条件如下表(可查看find命令手册):
- 使用操作符
( expression 1 ) -op ( expression 2 )
:op有:-and
(简写-a
);-or
(简写-o
);-not
(简写!
)- 举例:
find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
用于确定是否一个目录中的所有的文件和子目录拥有安全权限(注意:圆括号需要进行转义,以防止shell解释它们!)
- 举例:
- 基于
find
搜索结果执行 预定义操作(查看find手册)-print
:把匹配文件的全路径名输送到标准输出(如果没有指定其它操作,这是默认操作)-delete
:删除当前匹配的文件(注意:使用前再三确认,可先用-print来检查)ls
:对匹配的文件执行等同的 ls -dils 命令,并将结果发送到标准输出quit
:一旦找到一个匹配,则退出- 举例:
find ~ -type f -name '*.BAK' -delete
找出用户家目录(和它的子目录)下搜索每个以.BAK 结尾的文件名,并删除它们。等价于find ~ -type f -and -name '*.BAK' -and -print
- 举例:
- 基于
find
搜索结果执行 用户自定义操作-exec command {} ;
:基于find搜索结果执行其他命令。command 是指一个命令的名字, {} 是当前路径名的符号表示,分号是要求的界定符表明命令结束(执行多次命令)-exec command {} +
:通过把末尾的分号改为加号,则把搜索结果结合为一个参数列表,然后执行一次所期望的命令(合并搜索结果,且仅执行一次命令)-ok command {} ;
:使用 -ok 行为来代替 -exec,在执行每个指定的命令之前,会让用户确认是否执行
xargs
:将标准输入转换为一个命令的参数列表(被放置到命令行中参数个数是有限制的,–show–limits 选项的 xargs 命令,来查看命令行的最大值)- 举例:
find ~ -type f -name 'foo\*' -print | xargs ls -l
find 命令的输出被管道到 xargs 命令,反过来, xargs 会为 ls 命令构建参数列表,然后执行 ls 命令
- 举例:
- find 命令可选项
-depth
:指导 find 程序先处理目录中的文件,再处理目录自身。当指定 -delete 行为时,会自动应用这个选项-maxdepth levels
:当执行测试条件和行为的时候,设置 find 程序陷入目录树的最大级别数-mindepth levels
:在应用测试条件和行为之前,设置 find 程序陷入目录数的最小级别数-mount
:指导 find 程序不要搜索挂载到其它文件系统上的目录-noleaf
:指导 find 程序不要基于搜索类 Unix 的文件系统做出的假设,来优化它的搜索
-
touch file
:用来设置或更新文件的访问,更改,和修改时间;若文件名参数是一个不存在的文件,则会创建一个空文件 -
stat file
:是 ls 命令更丰富的版本,展示系统对某个文件及其属性所知道的所有信息
-
归档与备份
-
压缩文件(在 Linux 中 gzip 是主要的压缩程序,而 bzip2 则位居第二)
gzip
:用来压缩一个或多个文件,原始文件的压缩版会替代原始文件。gunzip
:用来把压缩文件复原为没有被压缩的版本- gzip的可选项
- 举例:先压缩
gzip foo.txt
在解压gunzip foo.txt.gz
;gzip -tv foo.txt.gz
测试压缩文件的完整性;gzip -d foo.txt.gz
解压文件,类似于gunzip
;gunzip -c foo.txt | less
仅浏览压缩文件内容
- gzip的可选项
zcat
:解压文件并输出到标准输出,相当于带有 -c 选项的 gunzip 命令zless
:查看zip压缩文件bzip2
:与 gzip 程序相似,但是使用了不同的压缩算法,舍弃了压缩速度,而实现了更高的压缩级别- 在大多数情况下,它的工作模式等同于 gzip,可以使用 gzip 的所有选项(除了 -r);压缩级别选项(-number)对于bzip2 程序来说,有少许不同的含义
- 由bzip2 压缩的文件,用扩展名.bz2 来表示
bunzip2
和bzcat
程序来解压缩文件;bzip2 文件也带有bzip2recover
程序,其会试图恢复受损的.bz2 文件
-
归档文件
-
tar
是 tape archive 的简称,揭示了它的根源,它是一款制作磁带备份的工具。而它仍然被用来完成传统任务,它也同样适用于其它的存储设备tar mode[options] pathname...
:注意:不需要短横线-,且必须首先指定操作模式mode,然后才是其它的选项option- tar 的操作模式mode
- tar 命令,gzip(z选项) 和 bzip2(j选项)压缩两者都直接支持
- 举例:
find playground -name 'file-A' | tar czf playground.tgz -T -
;find playground -name 'file-A' | tar cjf playground.tbz -T -
文件名“-”,则其被看作是标准输入或输出,正是所需(顺便说一下,使用“-”来表示标准输入/输出的惯例,也被大量的其它程序使用)
- 举例:
- 扩展名为.tar 或者.tgz 的文件,它们各自表示“普通”的 tar 包和被 gzip 程序压缩过的 tar 包(.tgz 扩展名是命名由 gzip 压缩的 tar 文件的常规扩展名,有时候也会使用.tar.gz 这个扩展名)。一个 tar 包可以由一组独立的文件,一个或者多个目录,或者两者混合体组成
- tar 处理归档文件路径名的方式,默认情况下的路径名是相对的,而不是绝对路径;当创建归档文件(c)的时候, tar 命令会简单地删除路径名开头的斜杠(这允许抽取文件到任意位置,在哪里抽取则再此处进行完全展开,而不是强制地把抽取的文件放置到原始目录下)
tar xf archive.tar pathname
:当抽取一个归档文件(archive.tar)时,仅抽取单个文件(pathname)或若干个需要的文件(使用通配符--wildcards
)- 举例:
tar xf ../playground2.tar --wildcards 'home/me/playground/dir-\*/file-A'
- 举例:
- tar 的实际应用
sudo tar cf /media/BigDisk/home.tar /home; cd /(到另一个系统的根目录下进行挂载); sudo tar xf /media/BigDisk/home.tar
借助U盘来复制家目录及其内容到另一个系统中find playground -name 'file-A' -exec tar rf playground.tar '{}' '+'
使用 find 命令来产生一个文件集合,然后这些文件被包含到归档文件中。这里是使用 find 命令来匹配 playground 目录中所有名为 file-A 的文件,然后使用 -exec 行为,来唤醒带有追加模式(r)的 tar 命令,把匹配的文件添加到归档文件 playground.tar 里面- 使用 tar 和 find 命令,来创建逐渐增加的目录树或者整个系统的备份,是个不错的方法。通过 find 命令匹配新于某个时间戳的文件,我们就能够创建一个归档文件,其只包含新于上一个 tar 包的文件,假定这个时间戳文件恰好在每个归档文件创建之后被更新了
find playground -name 'file-A' | tar cf - --files-from=- | gzip > playground.tgz
使用 find 程序产生了一个匹配文件列表,然后把它们管道到 tar 命令中。这个 --file-from 选项(也可以用 -T 来指定)导致 tar 命令从一个文件而不是命令行来读入它的路径名列表。最后,这个由 tar 命令产生的归档文件被管道到 gzip 命令中,然后创建了压缩归档文件 playground.tgz
-
zip
:既是压缩工具,也是一个打包工具,它读取和写入.zip 文件zip options zipfile file...
(会自动添加 .zip 扩展名,也可显示给zipfile添加后缀.zip)- 使用zip后会显示添加到文件包中每个文件的状态,zip会使用两种存储方式来添加文件到文件包中(存储方式之后显示的数值表明了压缩量):
store
表示没有压缩的文件;deflate
表示对文件执行压缩操作 - 举例:
zip -r playground.zip playground
添加 -r 将会包含文件目录及其内容
- 使用zip后会显示添加到文件包中每个文件的状态,zip会使用两种存储方式来添加文件到文件包中(存储方式之后显示的数值表明了压缩量):
- 对于 zip 命令(与 tar 命令相反)要注意一点:如果指定了一个已经存在的文件包,其将会被更新而不是被替代(这意味着会保留此文件包,但是会添加新文件,同时替换匹配的文件)
- zip 命令可以接受标准输入,可以被用来压缩其它程序的输出,zip 命令也支持把它的输出写入到标准输出,但是它的使用是有限的,因为很少的程序能利用输出。但 unzip 程序,不接受标准输入,这就阻止了 zip 和 unzip 一块使用
-@
选项:把一系列的文件名管道到 zip 命令- 举例:
find playground -name "file-A" | zip -@ file-A.zip
使用 find 命令产生一系列与“file-A”相匹配的文件列表,并且把此列表管道到 zip 命令,然后创建包含所选文件的文件包 file-A.zip
- 举例:
unzip
:抽取zip文件的内容。举例:unzip ../playground.zip
- 可有选择地从一个 zip 文件包中抽取文件,只要给 unzip 命令指定文件名
- 可列出文件(
-l
选项) - unzip 程序允许它的输出发送到标准输出(
-p
选项)举例:unzip -p ls-etc.zip | less
-
同步文件与目录
rsync
:同步本地与远端的目录,通过使用 rsync 远端更新协议,此协议允许 rsync 快速地检测两个目录的差异,执行最小量的复制来达到目录间的同步rsync options source destination
:将 source 的内容同步到 destination- source 和 destination 是下列选项之一:一个本地文件或目录;一个远端文件或目录,以 [user@]host:path 的形式存在;一个远端 rsync 服务器,由 rsync://[user@]host[:port]/path 指定
- source 和 destination 两者之一必须是本地文件, rsync 不支持远端到远端的复制
- 可选项:
-a
递归和保护文件属性;-v
冗余输出;--delete
删除可能在备份设备中已经存在但却不再存在于源设备中的文件(更新时,删除已有的旧文件) - 举例:
sudo rsync -av --delete /etc /home /usr/local /media/BigDisk/backup
- 通过网络来远程使用 rsync(两种方式)
- 方式一:要求另一个系统已经安装了 rsync 程序,还安装了远程 shell 程序,比如 ssh
- 举例:
sudo rsync -av --delete --rsh=ssh /etc /home /usr/local remote-sys:/backup
添加了 --rsh=ssh 选项,其指示 rsync 使用 ssh 程序作为它的远程 shell。以这种方式,我们就能够使用一个 ssh 加密通道,把数据安全地传送到远程主机中。其次,通过在目标路径名前加上远端主机的名字(在这种情况下,远端主机名为 remote-sys),来指定远端主机
- 举例:
- 方式二:通过使用 rsync 服务器。 rsync 可以被配置为一个守护进程,监听即将到来的同步请求。这样做经常是为了允许一个远程系统的镜像
- 举例:
rsync -av -delete rsync://rsync.gtlib.gatech.edu/fedora-linuxcore/development/i386/os fedora-devel
使用了远端 rsync 服务器的 URI,其由协议(rsync:// ,远端主机名(rsync.gtlib.gatech.edu),和软件仓库的路径名组成
- 举例:
- 方式一:要求另一个系统已经安装了 rsync 程序,还安装了远程 shell 程序,比如 ssh
-
正则表达式
-
grep
(global regular expression print)grep [options] regex [file...]
-i
忽略大小写(–ignore-case);-v
不匹配的项(–invert-match);-c
匹配的数量(–count);-l
打印包含匹配项的文件名,而不是文本行本身(–files-with-matches);-L
相似于 -l 选项,但是只是打印不包含匹配项的文件名(–files-without-match);-n
在每个匹配行之前打印出其位于文件中的相应行号(–line-number);-h
应用于多文件搜索,不输出文件名(–no-filename)
-
元字符
^ $ . [ ] { } - ? * + ( ) | \
- 除了上述元字符,其它所有字符都被认为是原义字符(在个别情况下,反斜杠()会被用来创建元序列);也允许元字符被转义为原义字符,而不是被解释为元字符
- 任意字符
.
:匹配任意一个字符。举例:grep -h '.zip' dirlist*.txt
- 锚点
^ $
:正则表达式只有在文本行的开头(^)或末尾(KaTeX parse error: Expected group after '^' at position 17: …被找到时,才算发生一次匹配。`^̲` 将匹配空行 - 中括号
[]
:从一个指定的字符集合中匹配一个单个的字符。举例:grep -h '[bg]zip'
匹配包含字符串“bzip”或者“gzip”的任意行 - 否定
^
:如果在**正则表示式中的第一个字符(在中括号表达式中,若^不在第一个位置则表示一个普通字符)**是一个插入字符,则剩余的字符被看作是不会在给定的字符位置出现的字符集合。举例:grep -h '[^bg]zip'
文件名都包含字符串“zip”,并且“zip”的前一个字符是除了“b”和“g”之外的任意字符 - 三字符区域
[.-.]
:任意字符的区域都能按照这种方式表达(如:grep -h '[A-Z]'
),也可包括多个区域(如:grep -h '[A-Za-z0-9]'
);若想在一个正则表达式中单纯地包含一个连字符(-),则将其作为第一个字符(如:grep -h '[-AZ]'
)
-
POSIX字符集
- POSIX 规范的字符集适用于正则表达式、shell路径名展开操作
- 常见的 POSIX 字符集
- [:alnum:] 字母数字字符。在 ASCII 中,等价于: [A-Za-z0-9]
- [:word:] 与 [:alnum:] 相同, 但增加了下划线字符
- [:alpha:] 字母字符。在 ASCII 中,等价于: [A-Za-z]
- [:blank:] 包含空格和 tab 字符
- [:cntrl:] ASCII 的控制码。包含了 0 到 31,和 127 的 ASCII 字符
- [:digit:] 数字 0 到 9
- [:xdigit:] 用来表示十六进制数字的字符。在 ASCII 中,等价于:[0-9A-Fa-f]
- [:graph:] 可视字符。在 ASCII 中,它包含 33 到 126 的字符
- [:lower:] 小写字母
- [:upper:] 大写字母
- [:punct:] 标点符号字符
- [:print:] 可打印的字符。在 [:graph:] 中的所有字符,再加上空格字符
- [:space:] 空白字符,包括空格, tab,回车,换行, vertical tab, 和 form feed. 在 ASCII 中,等价于:[ \t\r\n\v\f]
locale
:- locale命令
export LANG=POSIX
:把 LANG 变量设置为 POSIX,来更改 locale,使其使用传统的 Unix 行为;也可把这条语句添加到 .bashrc 文件中,使这个更改永久有效
- 基本的正则表达式(BRE)Vs 扩展的正则表达式(ERE)
- BRE的元字符:
^ $ . [ ] *
,而其它的所有字符被认为是文本字符。在 BRE 中,字符“(”,“)”,“{”,和“}”用反斜杠转义后,被看作是元字符- 逆参照:以
sed 's/regexp/replacement/' distros.txt
为例,若序列\n
( n 是指从 1 到 9 的数字,如\3
)出现在 replacement 中,则这个序列指的是在前面正则表达式 regexp 中相对应的子表达式(用圆括号括起来的),所以有sed 's/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/' distros.txt
的实例
- 逆参照:以
- ERE的元字符:
^ $ . [ ] *
+( ) { } ? + |
。在 ERE 中,在任意元字符之前加上反斜杠会导致其被看作是一个文本字符 egrep
相当于grep -E
(GUN 版本的 grep 程序也支持扩展的正则表达式,当使用了 -E 选项之后)
- BRE的元字符:
-
Alternation机制
- 从一系列字符串或者是其它的正则表达式中选择匹配项
- 竖杠线元字符
|
为标记,grep -E
相当于egrep
- 首先,把待匹配的正则表达式用单引号引起来,为的是阻止 shell 把竖杠线元字符解释为一个 pipe 操作符;其次,为了把 alternation 和其它正则表达式元素结合起来,可以使用圆括号 () 来分离 alternation
- Alternation举例:
echo "AAA" | grep -E 'AAA|BBB|CCC'
匹配字符串 AAA 或者是字符串 BBB 或者是字符串 CCCgrep -Eh '^(bz|gz|zip)' dirlist*.txt
将会在列表中匹配以“bz”,或“gz”,或“zip”开头的文件名,而rep -Eh '^bz|gz|zip' dirlist*.txt
则会匹配任意以“bz”开头,或包含“gz”,或包含“zip”的文件名
-
限定符
?
:匹配零个或一个元素- 举例:
^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$
在圆括号之后加上一个问号,来表示它们将被匹配零次或一次,又因为通常圆括号都是元字符(在 ERE 中),所以我们在圆括号之前加上了反斜杠,使它们成为文本字符。此正则表达式可用来匹配这两种电话号码:(nnn) nnn-nnnn 或 nnn nnn-nnnn,n 代表数字
- 举例:
*
:匹配任意多个元素(包括零个)- 举例:
[[:upper:]][[:upper:][:lower:] ]*.
这个表达式由三个元素组成:一个包含 [:upper:] 字符集的中括号表达式,一个包含[:upper:] 和 [:lower:] 两个字符集以及一个空格的中括号表达式,和一个被反斜杠字符转义过的圆点。第二个元素末尾带有一个 * 元字符,所以在开头的大写字母之后,可能会跟随着任意数目的大写和小写字母和空格。将匹配 “This works.” 与 “This Works.” ,而不匹配 “this does not”(因为不是大写字母开头)
- 举例:
+
:匹配一个或多个元素{ }
:匹配特定个数的元素n
:匹配前面的元素,如果它确切地出现了 n 次n,m
:匹配前面的元素,如果它至少出现了 n 次,但是不多于 m 次n,
:匹配前面的元素,如果它出现了 n 次或多于 n 次,m
:匹配前面的元素,如果它出现的次数不多于 m 次- 举例:
^\(?[0-9]{3}\)? [0-9]{3}-[0-9]{4}$
是^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$
的简化版本
-
正则表达式的一些应用
find . -regex '.*[^-\_./0-9a-zA-Z].*'
使用 find 扫描出包含空格和其它潜在不规范字符的路径名(因为find需要精确的路径名)locate --regex 'bin/(bz|gz|zip)'
在 locate 中使用正则表达式less
中应用正则表达式:/^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$
vim
中应用基本的正则表达式:/([0-9]\{3\}) [0-9]\{3\}-[0-9]\{4\}
;:hlsearch
可打开 vim 的高亮模式
-
zgrep
:允许 grep 来读取压缩文件
文本处理
-
cat
:用于连接文件并打印到标准输出设备上-n 或 --number
:由 1 开始对所有输出的行数编号-b 或 --number-nonblank
:和 -n 相似,只不过对于空白行不编号-v 或 --show-nonprinting
:显示不可打印字符,使用 ^ 和 M- 符号,除了 LFD 和 TAB 之外-s 或 --squeeze-blank
:将有连续两行以上的空白行,就代换为一行的空白行-E 或 --show-ends
:在每行结束处显示 $-T 或 --show-tabs
:将 TAB 字符显示为 ^I-A, --show-all
:等价于 -vET-e
:等价于"-vE"选项-t
:等价于"-vT"选项- 应用:
cat > xxx_file
直接从命令行中输入一些内容到xxx_file文件,使用Ctrl-d
完成输入cat -Ans xxx_file
显示文件中的各种非打印字符、超过连续两行的空白行被换成一个空白行、显示行标号等
-
sort
:对标准输入的内容,或命令行中指定的一个或多个文件进行排序,然后把排序结果发送到标准输出- -b 忽略每行前面开始出的空格字符
- -c 检查文件是否已经按照顺序排序
- -f 排序时,忽略大小写
- -m 将几个排序好的文件进行合并
- -n 依照数值的大小排序,适用于数字在每行开头
- -u 返回结果是去重后的
- -o<输出文件> 将排序后的结果存入指定的文件
- -r 以相反的顺序来排序
- -k 指定字段来排序(参考数据库的表格数据,可多键值排序,可指定键值偏移量)
| sort -nr -k 5 |
使用第5个字段来排序-k 1,1 -k 2n
是使用了两个键来排序,第一个键开始并结束于第一个字段,第二个键使用第二个字段且按照数值来排序(一些选项字母添加在键值说明符的末尾,用来指定排序的种类,如:b(忽略开头的空格), n(数值排序), r(逆向排序)等)sort -k 3.7nbr -k 3.1nbr -k 3.4nbr
指定键值字段中的偏移量,-k 3.7nbr 是指使用第三个字段中的第七个字符作为键,并且忽略空格,实现一个逆序数值排序
- -t<分隔字符> 指定排序时所用的栏位分隔字符(一般默认使用空白字符(空格和制表符)被当作是字段间的界定符)
sort -t ':' -k 7
指定分隔符为冒号(:)
- +<起始栏位>-<结束栏位> 以指定的栏位来排序,范围由起始栏位到结束栏位的前一栏位
- -i 排序时,除了040至176之间的ASCII字符外,忽略其他的字符
- -d 排序时,仅考虑英文字母、数字及空格字符,忽略其他的字符
- -M 将前面3个字母依照月份的缩写进行排序
- 应用:
sort > xxx_file
直接从命令行中输入一些内容到xxx_file文件,并完成排序工作,使用Ctrl-d
完成输入
-
uniq
:给定一个排好序的文件(包括标准输出), uniq 会删除相邻的任意重复行,并且把结果发送到标准输出;常和 sort 程序一块使用,来清理重复的输出- -c或–count:在每列旁边显示该行重复出现的次数
- -d或–repeated:仅显示重复出现的行列
- -f<栏位>或–skip-fields=<栏位>:忽略每行开头的 n 个字段,字段之间由空格分隔(不同于 sort 程序, uniq 没有选项来设置备用的字段分隔符)
- -s<字符位置>或–skip-chars=<字符位置>:跳过(忽略)每行开头的 n 个字符
- -u或–unique 仅显示出一次的行列(默认选项)
- -w<字符位置>或–check-chars=<字符位置> 比较行中不超过N个字符
- 应用:
uniq > xxx_file
直接从命令行中输入一些内容到xxx_file文件,并完成去重工作(若有序),使用Ctrl-d
完成输入
-
cut
:从文本行中抽取文本,并把其输出到标准输出,它能够接受多个文件参数或者标准输入-c char_list
:从文本行中抽取由 char_list 定义的文本,这个列表可能由一个或多个逗号分隔开的数值区间组成-b byte
:以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志-f field_list
:从文本行中抽取一个或多个由 field_list 定义的字段,这个列表可能包括一个或多个字段,或由逗号分隔开的字段区间-d delim_char
:当指定 -f 选项之后,使用 delim_char做为字段分隔符(默认使用 tab 字符)--complement
:抽取整个文本行,除了那些由 -c 和/或 -f 选项指定的文本b c f
list的范围:N
表示第N个(字符c、字节b、字段f);N-
表示从第N个到最后一个;N-M
表示从第N个到第M个;-M
表示从第一个到第M个- 举例:
cut -d ':' -f 1,3 distros.txt | cut -c 7-10
-
paste
:把每个文件以列对列(或行)的方式,一列列(或行)地加以合并 ,他就是相当于把两个不同的文件内容粘贴在一起,形成新的文件-d
:指定分隔符-s
:不使用平行的行目输出模式,而是每个文件占用一行(类似于转置,把每个文件当作一行)。举例:paste -s -d '*' file1 file2
- 可拼接多个文件,且文件的顺序对结果有影响
- 若文件之间的行数不同则短的文件填充为空
- paste命令
-
join
:把来自于多个基于共享关键域的文件的数据结合起来(类似于关系型数据库中的 join)join [OPTION]... FILE1 FILE2
- -a<1或2> 除了显示原来的输出内容之外,还显示指令文件中没有相同栏位的行
- -e<字符串> 若[文件1]与[文件2]中找不到指定的栏位,则在输出中填入选项中的字符串
- -i或–igore-case 比较栏位内容时,忽略大小写的差异
- -o<格式> 按照指定的格式来显示结果
- -t<字符> 使用栏位的分隔字符(默认情况下, join 命令使用空白字符做为输入字段的界定符,一个空格作为输出字段的界定符)
- -v<1或2> 跟-a相同,但是只显示文件中没有相同栏位的行
- -1<栏位> 连接[文件1]指定的栏位
- -2<栏位> 连接[文件2]指定的栏位
- 举例:
join testfile_2 testfile_1
-
comm
:比较两个(已排序的)文本文件,并且会显示每个文件特有的文本行和共有的文本行- 第一列包含第一个文件独有的文本行;第二列包含第二个文件独有的文本行;第三列包含两个文件共有的文本行
- 可对两个文件使用,或接受标准输入
-1/2/3
:不显示第一/二/三列,也可以多个结合使用:如-12
表示仅显示第三列- 应用
-comm file1 file2
;comm -13 file1 file2
-
diff
:逐行来比较文件之间的差异;如果指定要比较目录,则 diff 会比较目录中相同文件名的文件,但不会比较其中子目录
--c
:上下文格式;-C NUM
:指明多少行(默认为3行)
--u
:统一格式;-U NUM
:指明多少行(默认为3行)
--y
:并列格式;-W
:在使用-y参数时,指定栏宽
--r
:比较子目录的文件
--q
:仅返回两文件是否相同的结果
--a
:把所有文件视为文本
--b
:不检查空格字符
--B
:不检查空白行
--i
:忽略大小写
--w
:忽略全部的空格
--t
:输出时以tab字符展开;-T
:在每行前面加上前导的tab字符- 差异说明 - 正常模式:change,"5c5" 指两个文件的第5行不同;append,"12a13,14"指第一个文件比第二个文件少了13行和14行;delete,"13,14d12"的含义是第一个文件比第二个文件多了13行和14行 - 上下文格式:+,表示增加一行;-,表示减少一行;!,表示不相同的两行 - 并列格式:<,表示第一个文件多一行;>,表示第二个文件多一行;|,表示不相同的两行
-
patch
:把更改应用到文本文件中(使用补丁文件完成打补丁操作),一般是搭配diff
命令完成版本更新操作- 先生成补丁文件:
diff -Naur old_file new_file > diff_file
,再打补丁对 old_file 进行更新(把 old_file 变成 new_file)patch < diff_file
;撤销补丁:patch -R < diff_file
,添加-R
选项
- 先生成补丁文件:
-
tr
:基于字符的查找和替换操作- 接收标准输入,输出到标准输出
- 使用两个参数(大多数情况下,两个字符集应该长度相同;然而,有可能第一个集合大于第二个,比如把多个字符转换为单个字符)
- 要被转换的字符集
- 相对应的转换后的字符集
- 字符集有三种表示方法
- 一个枚举列表:ABCDEFGHIJKLMNOPQRSTUVWXYZ
- 一个字符域:A-Z(注意这种方法有时候面临与其它命令相同的问题,归因于语系的排序规则,因此应该谨慎使用)
- POSIX 字符类:[:upper:]
- 应用:
echo "lowercase letters" | tr a-z A-Z
把小写字母转换成大写字母;echo "lowercase letters" | tr [:lower:] A
把多个字符转换为单个字符Atr -d '\r' < dos_file > unix_file
除了换字之外, tr 命令能允许字符从输入流中简单地被删除,这里是删除每行末尾的回车符echo "aaabbbccc" | tr -s ab
使用-s
选项, tr 命令能“挤压”(删除)重复的字符(注意:重复的字符必须是相邻的,不相邻则没有效果)
-
sed
(stream editor):流式编辑器,对文本流进行编辑,文本可来自文件、标准输入- 按行执行命令
- 接收单个编辑命令(在命令行中)
- 执行包含多个命令的脚本文件
- sed 常用选项
-n
:不自动打印(不要默认地打印每一行)-f
:使用脚本文件来执行命令-i
:原地修改文本(不输出到标准输出)
- sed 编辑命令
s/regexp/replacement/
:仅第一次替换。只要找到一个 regexp 匹配项,就替换为 replacement 的内容。在末尾添加/g
进行全局替换,如:echo "aaabbbccc" | sed 's/b/B/g'
。 replacement 可能包括特殊字符 &,其等价于由 regexp匹配的文本。另外, replacement 可能包含序列 \1 到 \9,其是 regexp 中相对应的子表达式的内容y/set1/set2
:执行字符转写操作,通过把 set1 中的字符转变为相对应的 set2 中的字符。注意不同于 tr 程序, sed 要求两个字符集合具有相同的长度=
:输出当前的行号a
:在当前行的下一行追加文本。用法:sed 'a/特定字符串/新行字符串
orsed '/特定字符串/a 新行字符串'
d
:删除当前行i
:在当前行的上一行插入文本。用法:sed 'i/特定字符串/新行字符串
orsed '/特定字符串/i 新行字符串'
c
:取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行(n1,n2 指某些行,可能未指定)p
:打印当前行。默认情况下, sed 程序打印每一行,并且只是编辑文件中匹配指定地址的文本行。通过指定 -n 选项,这个默认的行为能够被忽略q
:退出 sed,不再处理更多的文本行。如果不指定 -n 选项,输出当前行Q
:退出 sed,不再处理更多的文本行
- sed 中的命令
- 开始于单个字符(如:s 等),随后是分隔符(** sed 将会接受紧随命令之后的任意字符做为分隔符**,因此分隔符可自定义,如:
\
、\_
等),其后跟着查找和替代字符串。举例:echo "front" | sed 's/front/back/'
与echo "front" | sed 's\_front\_back\_'
等价 - 命令之前都会带有一个地址,其指定了输入流中要被编辑的文本行,如果省略了地址,然后会对输入流的每一行执行编辑命令。sed 地址表示法:
- n:行号, n 是一个正整数
- $:最后一行
- /regexp/(默认情况只接收基本的正则表达式BRE):所有匹配一个 POSIX 基本正则表达式的文本行。注意正则表达式通过斜杠字符界定。选择性地,这个正则表达式可能由一个备用字符界定,通过\cregexpc 来指定表达式,这里 c 就是一个备用的字符
- addr1,addr2:从 addr1 到 addr2 范围内的文本行,包含地址 addr2 在内。地址可能是上述任意单独的地址形式
- first˜step:匹配由数字 first 代表的文本行,然后随后的每个在 step 间隔处的文本行。例如 1˜2 是指每个位于偶数行号的文本行,5˜5 则指第五行和之后每五行位置的文本行
- addr1,+n:匹配地址 addr1 和随后的 n 个文本行
- addr!:匹配所有的文本行,除了 addr 之外, addr 可能是上述任意的地址形式
- 可多个命令写在一起:
sed -i 's/lazy/laxy/; s/jumped/jimped/' foo.txt
- 开始于单个字符(如:s 等),随后是分隔符(** sed 将会接受紧随命令之后的任意字符做为分隔符**,因此分隔符可自定义,如:
- sed 命令详解1
- sed 命令详解2
- 按行执行命令
-
aspell
:一款交互式的拼写检查器(能够智能地检查各种类型的文本文件,包括 HTML 文件, C/C++ 程序,电子邮件和其它种类的专业文本),也可以作为一个独立的命令行工具使用aspell check textfile
拼写检查一个包含简单的文本文件
-
split
:将大文件分割成较小的文件,在默认情况下将按照每1000行切割成一个小文件-l NUM
or-NUM
:指定每个小文件NUM行。举例:split -l 100 date.file
-n NUM
:指定分成NUM个小文件。举例:split -n 33 date.file
把文件分成33个小文件-b
:指定每个小文件多少个字节。举例:split -b 10k date.file
-d
:指定数字后缀,默认从0开始。举例:split -b 10k date.file -d 110
以110为后缀名的开始-a
:指定后缀的长度。举例:split -b 10k date.file -d -a 4
以0为后缀名的开始,且长度为4split file SUFFIXES
:指定前缀SUFFIXES。举例:split -b 10k date.file -d -a 4 SSS
指定前缀为SSS,后缀长度为4的数字后缀
-
awk
格式化输出
-
nl
:对输出文本进行一些自定义,比如:添加文本行号,类似于cat -n
;接收多个文件输入或标准输入- nl 在计算文件行数的时候支持一个叫“逻辑页面”的概念,这允许 nl 在计算的时候去重设(再一次开始)可数的序列。用到一些选项的时候,可以设置一个特殊的开始值,并且在某个可限定的程度上还能设置它的格式。一个逻辑页面被进一步分为 header,body 和 footer 这样的元素。在每一个部分中,数行数可以被重设,并且/或被设置成另外一个格式。如果 nl 同时处理多个文件,它会把他们当成一个单一的文本流
- nl 命令简单实例
- nl 常用选项
-
fold
:限制文件行宽;接收多个文件或标准输入-w NUM
:限制每行的字符列数为NUM列-s
:基于空格断开-b
:基于字节数来分隔- 举例:
echo "The quick brown fox jumped over the lazy dog." | fold -w 12 -s
-
fmt
:文本格式化,重新格式化文件中的每一个段落,将结果写到标准输出 -
pr
:让文本为打印做好准备find /usr/bin/ -executable -type f | pr -T -4 | column -t
多列显示
-
printf
:模仿了C语言中的printf()函数。主要作用是输出文本,按照我们指定的格式输出文本- 在输出文本时,echo会换行。printf命令不会对输出文本进行换行,可以使用 \n 。printf命令的优势是格式化文本
- printf 命令实例
编译程序
make
、make install
./configure
Shell 编程
-
Shell 脚本注意点
#!
:shebang 被用来告诉操作系统将执行此脚本所用的解释器的名字,每个 shell 脚本都应该把这一文本行作为它的第一行- 修改文本权限为可执行状态。
chmod 755/700 file
权限为 755 的脚本,则每个人都能执行,和权限为 700 的脚本,只有文件所有者能够执行 - 将shell脚本文件放置到对应路径下
- 首先使用
echo $PATH
查看PATH中是否有 /home/USERNAME/bin 则可以在当前用户USERNAME下新建 bin 目录并将脚本文件 file 移动至 bin 中,即mkdir bin; mv file bin
,再直接使用脚本文件名即可file
- 若在上一步中的环境变量PATH中不存在 bin 目录的路径,则添加其到 PATH 中,即在 .bashrc 文件中添加这一行文本
export PATH=~/bin:"$PATH"
,保存退出后使用 source 命令强制刷新source .bashrc
- 首先使用
-
Shell 编程习惯
- 写好注释
- 为了可读性,可选择性使用:长选项名称、缩进、换行继续符
\
- 选择性配置 vim 文件
∼/.vimrc
-
变量与常量
- 创建变量:
variable=value
;使用变量:$var
- 变量赋值
a=z # Assign the string "z" to variable a.
b="a string" # Embedded spaces must be within quotes.
c="a string and $b" # Other expansions such as variables can be expanded into the assignment
d=$(ls -l foo.txt) # Results of a command
e=$((5 * 7)) # Arithmetic expansion
f="\t\ta string\n" # Escape sequences such as tabs and newlines
a=5 b="a string"
可在同一行中对多个变量赋值- 关于花括号
{}
屏蔽上下文的用法:已有filename="myfile"; touch $filename
,那么直接对文件改名会报错mv $filename $filename1
,而应该使用花括号 {} 来实现mv $filename ${filename}1
- 变量名命名规则
- 变量名可由字母数字字符(字母和数字)和下划线字符组成
- 变量名的第一个字符必须是一个字母或一个下划线
- 变量名中不允许出现空格和标点符号
- 特殊用法
declare -r TITLE=“Page Title”
强制常量的不变性, shell 会阻止之后给 TITLE 的任意赋值(比如:将一个变量强制赋值为整型)
- 常见变量
$HOSTNAME
这台机器的网络名称
- 创建变量:
-
Here Documents:一种 I/O 重定向形式,在脚本文件中嵌入正文文本,然后把它发送给一个命令的标准输入
-
这里的** command 是一个可以接受标准输入的命令名**, token 是一个用来指示嵌入文本结束的字符串(token 可随意命名,但必须必须在一行中单独出现,且文本行中没有末尾的空格)
- 把重定向操作符从
<<
改为<<-
, shell 会忽略在此 here document 中开头的 tab 字符。这就能缩进一个 here document,从而提高脚本的可读性
- 把重定向操作符从
-
优点:** here documents 中的单引号和双引号会失去它们在 shell 中的特殊含义**,因此可在一个 here document 中可以随意的嵌入引号
-
-
Shell 函数:是更为完美的别名 alias 替代物,实际上是创建较小的个人所用命令的首选方法(别名 alias 非常局限于命令的种类和它们支持的 shell 功能,然而 shell 函数允许任何可以编写脚本的东西)
- 两种形式:
- Shell 函数的命名规则和变量一样;一个函数必须至少包含一条命令; return 命令是可选的
- 局部变量:
local var
在变量名之前加上单词 local,只对其所在的 shell 函数起作用
-
if 分支结构
- 语法结构:
-
退出状态
$?
:命令(包括我们编写的脚本和 shell 函数)执行完毕后,会给系统发送的一个值,叫做退出状态- 一个 0 到 255 之间的整数,说明命令执行成功或是失败(零值说明成功,其它所有值说明失败)
$?
用来检查退出状态,比如:当一条命令执行完成之后,再使用echo $?
来检测退出状态true
返回0,总是执行成功;false
返回非0,总是执行失败exit
:接受一个单独的、可选的参数,其成为脚本的退出状态(在一个行动执行完之后,可使用 exit 来阻止脚本执行不必要的代码,但在程序中出现多个 exit 可能会使得程序逻辑较难理解)。举例:exit 1
失败退出return
:类似于exit
命令,但在 shell 函数中使用。举例:return 1
失败退出
-
测试
-
两种等价语法:
test expression
or[ expression ]
,这里的 expression 是一个表达式,其执行结果是 true 或者是 false -
测试:文件表达式
-
测试:字符串表达式
注意:当与test
一起使用的时候, > 和 < 表达式操作符必须用引号引起来(或者是用反斜杠转义),防止被 shell 解释为重定向操作符,造成潜在地破坏结果
-
测试:整型表达式
-
[[ expression ]]
复合命令(加强的 test 命令):除了 test 命令的所有表达式,- 还支持
=~
字符串表达式操作符。举例:string1 =~ regex
如果 string1 匹配扩展的正则表达式 regex,那么其返回值为真 - 还支持
==
操作符来匹配类型,如同路径名展开。举例:if [[ $FILE == foo.* ]]; then .. fi
则会匹配出 foo.bar 等
- 还支持
-
(( expression ))
复合命令,专门用于对整数进行操作,支持一套完整的算术计算- 如果算术计算的结果是非零值,则一个算术真测试值为真
- 它只处理整数,所以能够通过名字识别出变量,而不需要执行展开操作
-
逻辑操作符:结合表达式
-a
、&&
与运算(先执行 command1,如果并且只有如果 command1执行成功后,才会执行 command2);-o
、||
或运算(先执行 command1,如果并且只有如果 command1 执行失败后,才会执行 command2);!
、!
非运算- 灵活使用圆括号,用于分组
- 对于 bash 有特殊含义的字符,比如说 <, >, (,和),必须引起来或者是转义(不像 [[ ]] 和 (( )),test 使用的所有的表达式和操作符都被 shell 看作是命令参数)
- 技巧举例:
mkdir temp && cd temp
这会创建一个名为 temp 的目录,并且若它执行成功后,当前目录会更改为 temp[ -d temp ] || mkdir temp
测试目录 temp 是否存在,并且只有测试失败之后,才会创建这个目录(这种构造类型非常有助于在脚本中处理错误)[ -d temp ] || exit 1
如果这个脚本要求目录 temp,且目录不存在,然后脚本会终止,并返回退出状态 1
-
举例:
if [ -x "$FILE" ]; then ... fi
参数 $FILE 的引号不是必须的,但是为了防范空参数会加上引号(如果 $FILE 的参数展开是一个空值,就会导致一个错误(操作符将会被解释为非空的字符串而不是操作符))
-
读取输入
-
read
:从标准输入读取单行数据,可以用来读取键盘输入,当使用重定向的时候,读取文件中的一行数据read [-options] [variable...]
:options 可选一个或多个,且 variable 是用来存储输入数值的一个或多个变量名- 如果 read 命令接受到变量值数目少于期望的数字,那么额外的变量值为空,而多余的输入数据则会被包含到最后一个变量中。举例:
read var1 var2 var3 var4 var5
使用5个变量来接收输入 - 如果 read 命令之后没有列出变量名,则一个 shell 变量
REPLY
,将会包含所有的输入
- 如果 read 命令接受到变量值数目少于期望的数字,那么额外的变量值为空,而多余的输入数据则会被包含到最后一个变量中。举例:
- 可选项
-a array
:把输入赋值到数组 array 中,从索引号零开始-d delimiter
:用字符串 delimiter 中的第一个字符指示输入结束,而不是一个换行符-e
:使用 Readline 来处理输入。这使得与命令行相同的方式编辑输入-n num
:读取 num 个输入字符,而不是整行-p prompt
:为输入显示提示信息,使用字符串 prompt-r
:Raw mode. 不把反斜杠字符解释为转义字符-s
:Silent mode. 不会在屏幕上显示输入的字符。当输入密码和其它确认信息的时候,这会很有帮助-t seconds
:超时. 几秒钟后终止输入。 read 会返回一个非零退出状态,若输入超时-u fd
:使用文件描述符 fd 中的输入,而不是标准输入
- 注意:
- 不能在管道(pipe)中使用
read
命令!(因为 read 在 shell 的子 shell 环境中,把某些值赋值给变量 REPLY,但是当命令退出后,子 shell 和它的环境将被破坏掉,这样赋值的影响就会消失)
- 不能在管道(pipe)中使用
- 举例:
if read -t 10 -sp "Enter secret pass phrase > " secret_pass; then ... fi
-
IFS (Internal Field Separator):内部字符分隔符,一个shell 内部变量(默认值包含一个空格,一个 tab 和一个换行符,每一个都会把字段分割开)
- 应用:
IFS=":" read user pw uid gid name home shell <<< "$file_info"
这一行由三部分组成:一个IFS变量赋值(临时生效),一个带有一串参数的 read 命令,一个 here 字符串(<<<
)。针对IFS赋值也可以这样分开写:先写一行IFS=":"
,再写一行read user pw uid gid name home shell <<< "$file_info"
,最后再写一行重新还原IFS变量的值IFS="$OLD_IFS"
(但第一种方法更简洁…)
- 应用:
流程控制
-
while 循环:当…进行循环
- 语法:
while commands; do commands; done
- 重新开始循环
continue
- 停止循环
break
- 举例:
- 把标准输入管道到循环中
- 把标准输入管道到循环中
- 语法:
-
until 循环:直到…停止循环
-
case 多选复合命令
- case 命令检查一个变量值,然后试图去匹配其中一个具体的模式;当与之相匹配的模式找到之后,就会执行与该模式相关联的命令;若找到一个模式之后,就不会再继续寻找(针对使用 ;; 结尾的情况)
- 语法:
- case 执行多个动作:使用
;;&
来代替;;
可以进行继续匹配,而不是匹配一个后停止
- 可以使用竖线字符(|)作为分隔符,把多个模式结合起来,这就创建了一个“或”条件模式,如:不区分大小写
- 常用实例
-
for 循环
for variable [in words]; do commands; done
:传统的 shell 格式 for 循环,variable 是一个变量的名字,这个变量在循环执行期间会增加, words 是一个可选的条目列表,其值会按顺序赋值给 variable, commands 是在每次循环迭代中要执行的命令- 特性1:可通过展开的方式来花式创建 words 列表。举例:
for i in A B C D;
;for i in {A..D};
花括号展开;for i in distros*.txt;
路径名展开;for i in $(strings $1);
这里是命令行替换,用到了strings
命令(为每一个文件产生一个可读的文本格式的“words”列表) - 特性2:如果省略掉 for 命令的可选项 words 部分, for 命令会默认处理位置参数。举例:
for i;
- 特性1:可通过展开的方式来花式创建 words 列表。举例:
for (( expression1; expression2; expression3 )); do commands; done
:C 语言格式 for 循环,这里的 expression1(用于初始化循环条件),expression2(用于决定循环结束的时间)和 expression3(在每次循环迭代的末尾会执行)都是算术表达式,commands 是每次循环迭代时要执行的命令- C 语言形式的 for 循环,对于需要数字序列的情况很有用
- 举例:
for (( i = 0; i < 5; i += 1 )); do echo $i; done
调试错误Debug
- 语法错误
- 逻辑错误
- 测试
- 调试
- 找到问题区域:代码段隔离
- 追踪
- 添加执行位置的提示性信息(可输出到标准错误
>&2
) - 整个脚本追踪:
#!/bin/bash -x
。追踪开始后,行首的加号(+)表明追踪的迹象,使其与常规输出结果区分开来;加号是追踪输出的默认字符,它包含在 PS4(提示符 4) shell 变量中,可修改,如:先执行命令export PS4='$LINENO + '
(追踪执行到的当前行的行号),再执行脚本文件 - 部分区域追踪
set -x
:在脚本文件中,先写一行set -x # Turn on tracing
,中间是需追踪的代码块,最后再写一行set +x # Turn off tracing
- 添加执行位置的提示性信息(可输出到标准错误
- 代码执行时检查数值
位置参数
-
位置参数变量集合
- 这些变量按照从 0 到 9 给予编号命名,也即有变量
$0
到$9
- 位置参数
$0
总会包含命令行中出现的第一个单词,也就是已执行程序的路径名(也即使这个命令行不带参数) - 实际通过参数展开方式可以访问的参数个数多于 9 个,只要指定一个大于 9 的数字,用花括号把该数字括起来就可以。例如
${10}
,${55}
,${211}
,等等 $#
:确定参数数量;对参数使用shift
后参数数量会变化
- 这些变量按照从 0 到 9 给予编号命名,也即有变量
-
shift
:使所有的位置参数“向下移动一个位置”,访问多个参数- shift 命令也可以处理只有一个参数的情况(除了其值永远不会改变的变量
$0
) basename
去除文件路径的前缀部分,返回最后一个文件的基本名称。举例:basename /usr/bin/sort
会返回sort
;basename include/stdio.h .h
会返回stdio
- shift 命令也可以处理只有一个参数的情况(除了其值永远不会改变的变量
-
shell 函数中使用位置参数
- 如果一个包含 shell 函数 func 的脚本调用该函数,且带有一个文件名参数,那这个参数会传递给 func 函数
-
集体位置参数
$*
:展开成一个从 1 开始的位置参数列表。当它被用双引号引起来的时候,展开成一个由双引号引起来的字符串,包含了所有的位置参数,每个位置参数由 shell 变量 IFS 的第一个字符(默认为一个空格)分隔开$@
:展开成一个从 1 开始的位置参数列表。当它被用双引号引起来的时候,它把每一个位置参数展开成一个由双引号引起来的分开的字符串(在大多数情况下是最有用的方法,因为它保留了每一个位置参数的完整性)- 举例:
执行某个脚本,并传入两个字符串参数pass_params "word" "words with spaces"
,则对于上述4种位置参数的收集形式有以下结果
字符串与数字
-
参数展开(编写脚本文件时可使用,以提高脚本文件等的运行效率,通过
time
命令验证)- 基本参数
- 当
$a
展开后,会变成变量 a 所包含的值;简单参数也可能用花括号引起来:${a}
。举例:对于字符串变量a="foo"
,可使用花括号来展开echo "${a}_file"
,而不是echo "$a_file"
(shell 会试图展开变量 a_file,而不是变量 a) - 位置参数展开:
${11}
- 当
- 管理空变量的展开(几种用来处理不存在和空变量的参数展开形式,这些展开形式对于解决丢失的位置参数和给参数指定默认值的情况很方便)
${parameter:-word}
:若 parameter 没有设置(例如,不存在)或者为空,展开结果是 word 的值。若 parameter 不为空,则展开结果是 parameter 的值${parameter:=word}
:若 parameter 没有设置或为空,展开结果是 word 的值。另外, word 的值会赋值给 parameter(上一种不会)。若 parameter 不为空,展开结果是 parameter 的值(注意:位置参数或其它的特殊参数不能以这种方式赋值)${parameter:?word}
:若 parameter 没有设置或为空,这种展开导致脚本带有错误退出,并且 word 的内容会发送到标准错误(若使用$?
来查看退出状态,展开正确则有状态 1,发生错误则有状态 0)。若 parameter 不为空,展开结果是 parameter 的值${parameter:+word}
:若 parameter 没有设置或为空,展开结果为空。若 parameter 不为空,展开结果是 word 的值会替换掉 parameter 的值;然而, parameter 的值不会改变(若 parameter 为空则输出空,若 parameter 有值则输出 word 的值,但不会改变 parameter 的值)。举例:echo ${foo:+"substitute value if set"}
- 基本参数
-
返回变量名的参数展开
${!prefix*}
or${!prefix@}
:返回以 prefix 开头的已有变量名。举例:echo ${!BASH*}
列出了所有以 BASH 开头的环境变量名- 字符串展开
${#parameter}
:展开成由 parameter 所包含的字符串的长度(类似于 python 的 len);如果 parameter 是 @ 或者是 * 的话,则展开结果是位置参数的个数。举例:若foo="This string is long."
,则echo ${#foo}
结果为 20,echo ${foo:5}
结果为string is long.
,echo ${foo:5:6}
结果为string
;echo ${foo: -5}
结果为long.
;echo ${foo: -5:2}
结果为lo
${parameter:offset}
or${parameter:offset:length}
:提取的字符始于第 offset 个字符(从字符串开头算起)直到字符串的末尾,可指定提取的长度 length;若 offset 的值为负数,则认为 offset 值是从字符串的末尾开始算起,而不是从开头,而且负数前面必须有一个空格,为防止与${parameter:-word}
展开形式混淆;length 若出现,则必须不能小于零。若 parameter 是 @,展开结果是 length 个位置参数,从第 offset 个位置参数开始${parameter#pattern}
or${parameter##pattern}
:这种展开会从 paramter 所包含的字符串中清除开头一部分与 patten 通配符模式相匹配的文本;一个 # 清除最短的匹配结果,两个 # 清除最长的匹配结果。举例:若有foo=file.txt.zip
,则echo ${foo#*.}
结果为txt.zip
,而echo ${foo##*.}
结果为zip
${parameter%pattern}
or${parameter%%pattern}
:和上面的 # 和 ## 展开一样,但清除的将从末尾开始,而不是开头- 对 parameter 的内容执行查找和替换操作:
${parameter/pattern/string}
,只有第一个匹配项会被替换掉;${parameter//pattern/string}
,所有的匹配项都会被替换掉;${parameter/#pattern/string}
,仅当匹配项出现在字符串的开头才替换(仅当开头才替换);${parameter/%pattern/string}
,仅当匹配项出现在字符串的末尾才替换(仅当末尾替换)。举例:若foo=JPG.JPG
,则echo ${foo/JPG/jpg}
结果为jpg.JPG
,echo ${foo//JPG/jpg}
结果为jpg.jpg
,echo ${foo/#JPG/jpg}
结果为jpg.JPG
,echo ${foo/%JPG/jpg}
结果为JPG.jpg
- 大小写转换
declare
:用于声明 shell 变量,如:-u
大写变量,-l
小写变量,-a
数组变量,-i
整型变量。- 参数展开:
${parameter„}
,把 parameter 的值全部展开成小写字母;${parameter,}
,仅把 parameter 的第一个字符展开成小写字母,${parameterˆˆ}
,把 parameter 的值全部转换成大写字母;${parameterˆ}
,仅把 parameter 的第一个字符转换成大写字母(首字母大写)
-
算术求值与展开
- 基本格式:
$((expression))
这里的 expression 是一个有效的算术表达式,与复合命令 (( )) 有关 - 数基:默认为十进制;
0number
在算术表达式中,以零开头的数字被认为是八进制数;0xnumber
十六进制;base#number
此 number 以 base 为底。举例:echo $((0xff))
与echo $((2#11111111))
均输出 255 - 基本运算:
+
、-
、*
、/
整数除法、**
乘方、%
取模 - 赋值运算符
=
:给变量一个值就执行了一次赋值运算;也能在算术表达式中执行赋值运算。举例:if (( foo = 5 ));then echo "It is true."; fi
,此 if 代码块执行完后,会输出 It is true.(这里的条件表达式foo = 5
已经给 foo 赋值为 5,且测试条件计算为真,因为 foo 的值非零)==
:计算等价性(注意:test 命令接受单个 = 运算符来测试字符串等价性,因此这也是使用更现代的 [[ ]] 和 (( )) 复合命令来代替 test 命令的另一个原因)+=
…:parameter += value
等价于parameter = parameter + value
parameter++
、parameter--
:参数返回后自加、减一++parameter
、--parameter
:参数返回前自加、减一(对于大多数 shell 应用来说,可能更常用,相当于上一条)
- 位运算符(注意:除了按位取反运算符之外,其它所有位运算符都有相对应的赋值运算符,如:
<<=
)~
:按位取反。对一个数字所有位取反(没有赋值运算符!)<<
:位左移. 把一个数字的所有位向左移动>>
:位右移. 把一个数字的所有位向右移动&
:位与。对两个数字的所有位执行一个 AND 操作|
:位或。对两个数字的所有位执行一个 OR 操作^
:位异或。对两个数字的所有位执行一个异或操作
- 逻辑运算符(当表达式用于逻辑运算时,表达式遵循算术逻辑规则:表达式的计算结果是零认为假,而非零表达式认为真)
<=
:小于或相等>=
:大于或相等<
:小于>
:大于==
:相等!=
:不相等&&
:逻辑与||
:逻辑或expr1?expr2:expr3
:条件(三元)运算符。操作三个算术表达式(字符串不会起作用),并且若第一个表达式为真(或非零),则执行第二个表达式,否则,执行第三个表达式
- 其他
a=0; ((a<1?a+=1:a-=1))
这样赋值会报错,需要用括号把赋值表达式括起来((a<1?(a+=1):(a-=1)))
- 基本格式:
-
bc
:一种高精度计算器语言- 注释
/* ... */
:bc 使用和 C 编程语言一样的注释语法 - 使用
bc
- 执行脚本
bc foo.bc
,添加-q
来禁止输出版权信息 - 可通过标准输入把一个脚本传递给 bc 程序:
bc < foo.bc
- 可以使用 here 文档, here 字符串,和管道来传递脚本:
bc <<< "2+2"
- 执行脚本
- 应用
- 注释
数组
-
创建数组
- 数组变量就像其它 bash 变量一样命名,当被访问的时候,它们会被自动地创建。举例:创建
a[1]=foo
,使用echo ${a[1]}
(使用花括号是必需的,以便防止 shell 试图对数组元素名执行路径名展开操作) declare -a arr
- 数组变量就像其它 bash 变量一样命名,当被访问的时候,它们会被自动地创建。举例:创建
-
数组赋值
- 单个值赋值
name[subscript]=value
:这里的 name 是数组的名字, subscript 是一个大于或等于零的整数(或算术表达式);注意数组第一个元素的下标是 0,而不是 1;数组元素的值可以是一个字符串或整数 - 多个值赋值
name=(value1 value2 ...)
:这里的 name 是数组的名字, value. . . 是要按照顺序赋给数组的值,从元素 0 开始,如:days=(Sun Mon Tue Wed Thu Fri Sat)
;也可指定下标来赋值,如:days=([0]=Sun [1]=Mon [2]=Tue [3]=Wed [4]=Thu [5]=Fri [6]=Sat)
- 单个值赋值
-
访问数组元素
- 输出整个数组内容
- 下标
*
和@
可以被用来访问数组中的每一个元素(与位置参数一样,@ 表示法在两者之中更有用处);遇到引号后下标*
和@
的区别则出现不同
- 下标
- 确定数组元素个数
- 使用参数展开,能够确定数组元素的个数(使用
@
表示法),与计算字符串长度的方式几乎相同。举例:对于arr[100]=foo
,echo ${#arr[@]}
会得到数组含有的元素个数 1(而未赋值的元素 0-99 不会计入),而echo ${#arr[100]}
会输出数组元素 100 的长度 3
- 使用参数展开,能够确定数组元素的个数(使用
- 确定数组下标
${!array[*]}
or${!array[@]}
:和其它使用符号 * 和 @ 的展开一样,用引号引起来的 @ 格式是最有用的,因为它能展开成分离的词。举例:for i in "${!arr[@]}"; do echo $i; done
- 在数组末尾添加元素
- 通过使用 += 赋值运算符,能够自动地把值附加到数组末尾。举例:创建数组
arr=(a b c)
,末尾添加元素arr+=(d e f)
- 通过使用 += 赋值运算符,能够自动地把值附加到数组末尾。举例:创建数组
- 数组排序(通过管道与其他命令可实现多种多样的数组操作)
- Shell 没有这样做的直接方法,可借助
sort
命令来间接实现:a_sorted=($(for i in "${a[@]}"; do echo $i; done | sort))
- Shell 没有这样做的直接方法,可借助
- 删除数组及其元素
unset
:删除一个数组unset arr
;删除一个数组元素unset 'arr[3]'
(注意:数组下标从 0 开始,且添加引号来防止路径名展开)
- 其他
- 任何引用一个不带下标的数组变量,则指的是数组元素 0。举例:给一个数组赋空值不会清空数组内容,
foo=(a b c d e f)
再执行foo=
,而echo ${foo[@]}
的结果是 b c d e f(去掉第一个元素 a)
- 任何引用一个不带下标的数组变量,则指的是数组元素 0。举例:给一个数组赋空值不会清空数组内容,
- 输出整个数组内容
-
关联数组:使用字符串而不是整数作为数组索引
declare -A colors
:使用带有 -A 选项的 declare 命令创建。举例:declare -A colors
,colors["red"]="#ff0000"
,colors["blue"]="#0000ff"
- 取值:
echo ${colors["blue"]}
其他特性
-
命令组合(用于管理重定向,在重定向中合并流)
- 方案一,组命令:
{ command1; command2; [command3; ...] }
,鉴于 bash 实现组命令的方式,花括号与命令之间必须有一个空格,并且最后一个命令必须用一个分号或者一个换行符终止。举例:{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt
- 方案二,子 Shell(Subshell):
(command1; command2; [command3;...])
。举例:(ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt
- 应用场景:当构建一个管道线命令的时候,通常把几个命令的输出结果合并成一个流是很有用的,因此使用组命令和子 shell 使这种操作变得很简单。举例:
{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } | lpr
- 二者区别:
- 组命令在当前 shell 中执行它的所有命令,而子 shell 在当前 shell 的一个子副本中执行它的命令(这意味着运行环境被复制给了一个新的 shell 实例,当这个子 shell 退出时,环境副本会消失,所以在子 shell 环境(包括变量赋值)中的任何更改也会消失)
- 因此,除非脚本要求一个子 shell,在大多数情况下,组命令比子 shell 更受欢迎(组命令运行很快并且占用的内存也少)
- 进程替换
- 允许把一个子 shell 的输出结果当作一个用于重定向的普通文件;它其实是一种展开形式,可以检验它的真实值,如:执行
echo <(echo "foo")
,输出结果为/dev/fd/63
(表明子 shell 的输出结果,由一个名为/dev/fd/63
的文件提供) - 两种表达方式:一种适用于产生标准输出的进程
<(list)
,另一种适用于接受标准输入的进程>(list)
,这里的 list 是一串命令列表 - 应用:可用于解决
read
命令在一个子 shell 中执行,当该子 shell 终止的时候,它的 REPLY 副本被毁掉的问题(因为管道线中的命令总是在子 shell 中执行,任何给变量赋值的命令都会遭遇这样的问题)
- 允许把一个子 shell 的输出结果当作一个用于重定向的普通文件;它其实是一种展开形式,可以检验它的真实值,如:执行
- 方案一,组命令:
-
陷阱,
trap
(用于程序响应与处理信号)trap argument signal [signal...]
:argument 是一个字符串(普通字符串、shell 函数名等),它被读取并被当作一个命令;signal 是一个信号的说明,它会触发执行所要解释的命令- 应用:
- 若在脚本中定义了一个陷阱
trap "echo 'I am ignoring you.'" SIGINT SIGTERM
,当脚本运行的时候,这个陷阱每当接受到一个 SIGINT 或 SIGTERM 信号时(比如:当用户试图通过按下 Ctrl-c 组合键终止脚本运行的时候),就会执行一个 echo 命令来输出字符串 “I am ignoring you.”
- 若在脚本中定义了一个陷阱
-
异步执行
wait [-n] [id ...]
:等待作业完成并返回退出状态;使一个父脚本暂停运行,直到一个特定的进程(例如,子脚本)运行结束
-
命名管道(用来在两个进程之间建立连接,也可以像其它类型的文件一样使用)
- 特点:命名管道是一类特殊的文件,实际上形成了先入先出(FIFO)的缓冲,和普通(未命令的)管道一样,数据从一端进入,然后从另一端出现
- 创建一个命名管道
mkfifo named_pipe
:使用mkfifo
命令(通过ls
命令查看 named_pipe 文件,可发现其属性字段的第一个字母是“p”,表明它是一个命名管道) - 使用命名管道(需要两个终端窗口或两个虚拟控制台):先让数据流入
process1 > named_pipe
(按下 Enter 按键之后,命令将会挂起,这是因为在管道的另一端没有任何接受数据,也就是管道阻塞;一旦我们绑定一个进程到管道的另一端,该进程开始从管道中读取输入的时候,这种情况会消失,此时需要使用第二个终端窗口),然后在另一个终端窗口让数据流出process2 < named_pipe
(表现出来就像process1 | process2
),通过这样两个步骤则完成了命名管道的使用。举例:先ls -l pipe1
,再cat < pipe1