1. 输入输出重定向
在linux工作必须掌握的命令一文中,我们已经掌握了几乎所有基础常用的Linux命令,那么接下来的任务就是把多个命令适当的组合到一起,使其协同工作,会更高效的处理数据,做到这一点就必须搞清楚命令的输入重定向和输出重定向的原理。
简而言之,输入重定向就是把文件导入到命令中,而输出重定向则是指把原本输出到屏幕的数据信息写入到指定文件中。在日常工作中,相较于输入重定向,输出重定向的频率更高,所以又将输出重定向分为了标准输出重定向和错误输出重定向两种不同的技术,以及清空写入与追加写入两种模式。
- 标准输入重定向(STDIN,文件描述符为0):默认从键盘输入,也可以从其他文件或命令输入。
- 标准输出重定向(STDOUT,文件描述符为1):默认输出到屏幕。
- 错误输出重定向(STDERR,文件描述符为2):默认输出到屏幕。
比如我们分别查看两个文件的属性信息,其中第二个文件时不存在的,虽然针对这两个文件的操作都分别会在屏幕上输出一些数据信息,但这两个操作的差异其实很大:
[root@root test]# ls -l test1.txt
-rw-r--r--. 1 root root 8 1月 22 17:10 test1.txt
[root@root test]# ls -l test2.txt
ls: 无法访问test2.txt: 没有那个文件或目录
在上述命令中,名为test1.txt文件是存在的,输出的是该文件的一些权限、所有者、所属组、文件大小以及修改时间等信息,这也是该命令的标准输出信息,而名为test2.txt文件是不存在的,因此在执行ls 命令后显示的报错提示信息也是该命令的错误输出信息。那么,要想把原本输出到屏幕上的数据转而写入到文件当中,就要区别对待这两种输出信息。
对于输入重定向来讲,用到的符号及其作用如表:
符号 | 输入重定向符号作用 |
---|---|
命令 < 文件 | 将文件作为命令的标准输入 |
命令 << 文件 | 从标准输入中读入,直到遇见分解符才停止 |
命令 < 文件1 > 文件2 | 将文件1作为命令的标准输入并将标准输出到文件2 |
对于输出重定向来讲,用到的符号以及作用如下表:
符号 | 输出重定向符号以及作用 |
---|---|
命令 > 文件 | 将标准输出重定向到一个文件中(清空原有文件的数据) |
命令 2> 文件 | 将错误输出重定向到一个文件中(清空原有文件的数据) |
命令 >> 文件 | 将标准输出重定向到一个文件中 (追加到原有内容的后面) |
命令 2>> 文件 | 将错误输出重定向到一个文件中 (追加到原有内容的后面) |
命令 >> 文件 2 >&1 或 命令 &>> 文件 | 将标准输出与错误输出共同写入到文件中(追加到原有内容的后面) |
对于重定向中的标准输出模式,可以省略文件描述符1 不写,而错误输出模式的文件描述 2是必须要写的。我们先来小试牛刀。通过标准输出重定向将man bash 命令原本要输出到屏幕的信息写入到readme.txt中,然后显示readme.txt文件中的内容。具体命令如下:
有没有感觉很方便呢?我们接下来尝试输出重定向技术中的覆盖写入与追加写入这2种不同模式带来的变化。首先通过覆盖写入模式向readme.txt文件写入一行数据,然后再通过追加写入模式向文件再写入一次数据,其命令如下:
[root@root test]# echo "welcome to china!" > readme.txt
[root@root test]# echo "china is very beautiful!" >> readme.txt
在执行cat命令之后,可以看到如下所示的文件内容:
[root@root test]# cat readme.txt
welcome to china!
china is very beautiful!
虽然都是输出重定向技术,但是不同命令的标准输出和错误输出还是有区别的。例如查看当前目录中某个文件的信息,这里以readme.txt为例,因为这个文件是真实存在的,因此使用标准输出即可将原本要输出到屏幕的信息写入到文件中,而错误的输出重定向则依然把信息输出到了屏幕上。
如果想把命令的报错信息输出写入到文件,该怎么操作呢?当用户在执行一个自动化的Shell脚本时,这个操作会特别有用,因为它把整个脚本执行过程中的报错信息都记录到了文件中,便于安装后的排错工作,接下来我们以一个不存在的文件进行演示:
[root@root test]# ls -l xxx
ls: 无法访问xxx: 没有那个文件或目录
[root@root test]# ls -l xxx > test4.txt
ls: 无法访问xxx: 没有那个文件或目录
[root@root test]# ls -l xxx 2> test4.txt
[root@root test]# cat test4.txt
ls: 无法访问xxx: 没有那个文件或目录
输入重定向相对来说有些冷门,在工作中遇到的概率会小一点,输入重定向的作用是把文件直接导入到命令中,接下来使用输入重定向把readme.txt文件导入给wc -l命令,统计一下文件中的内容行数。
[root@root test]# wc -l < readme.txt
3109 readme.txt
上述命令实际上等同于cat readme.txt | wc -l 管道符命令的组合
2. 管道命令符
管道命令符的作用可以用一句话来概括“把前一个命令原本要输出到屏幕的标准正常数据当作是最后一个命令的标准输入”,我们通过匹配关键词/sbin/nologin找出了所有被限制登录的用户。
- 找出被限制登录用户的命令时 grep “/sbin/nologin” /etc/passwd;
- 统计文本行数的命令则是 wc-l
把这两条命令合并为一条就是
[root@root test]# grep "/sbin/nologin" /etc/passwd | wc -l
33
这个管道符就像一个法宝,我们可以将它套用到其他不同的命令上,比如用翻页的形式查看 /etc 目录中文件列表及属性信息。
[root@root test]# ls -l /etc | more
总用量 1036
-rw-r--r--. 1 root root 16 8月 23 2022 adjtime
-rw-r--r--. 1 root root 1529 4月 1 2020 aliases
-rw-r--r--. 1 root root 12288 8月 23 2022 aliases.db
drwxr-xr-x. 2 root root 236 8月 23 2022 alternatives
-rw-------. 1 root root 541 8月 9 2019 anacrontab
-rw-r--r--. 1 root root 55 8月 8 2019 asound.conf
drwxr-x---. 3 root root 43 8月 23 2022 audisp
drwxr-x---. 3 root root 83 8月 23 2022 audit
drwxr-xr-x. 2 root root 68 1月 9 2023 bash_completion.d
-rw-r--r--. 1 root root 2853 4月 1 2020 bashrc
drwxr-xr-x. 2 root root 6 10月 2 2020 binfmt.d
-rw-r--r--. 1 root root 37 10月 23 2020 centos-release
-rw-r--r--. 1 root root 51 10月 23 2020 centos-release-upstream
drwxr-xr-x. 2 root root 6 10月 13 2020 chkconfig.d
-rw-r--r--. 1 root root 1108 8月 8 2019 chrony.conf
-rw-r-----. 1 root chrony 481 8月 8 2019 chrony.keys
drwxr-xr-x. 2 root root 25 1月 9 2023 containerd
--More--
3. 命令行通配符
大家都有遇到提笔忘记的尴尬,有时候明明 一个文件名称就在嘴边但就是想不起来,如果记得一个文件的开头几个字母,想遍历找出所有以这个关键词开头的文件,该怎么操作呢?又比如,假设想要批量批量查看所有硬盘文件的相关权限属性,一种方式是这样的:
[root@root test]# ls -l /dev/sda
brw-rw----. 1 root disk 8, 0 1月 21 08:42 /dev/sda
[root@root test]# ls -l /dev/sda1
brw-rw----. 1 root disk 8, 1 1月 21 08:42 /dev/sda1
[root@root test]# ls -l /dev/sda2
brw-rw----. 1 root disk 8, 2 1月 21 08:42 /dev/sda2
[root@root test]# ls -l /dev/sda3
brw-rw----. 1 root disk 8, 3 1月 21 08:42 /dev/sda3
[root@root test]# ls -l /dev/sda4
ls: 无法访问/dev/sda4: 没有那个文件或目录
幸亏我的硬盘文件和分区只有4个,要是有几百个,估计要花一天时间来忙这个事情了,由此可见,这种方式的效率确实。比如,这些硬盘设备文件都是以sda开头并且存放到了/dev目录中,这样一来,即使我们不知道硬盘的分区编号和具体分区的个数,也可以使用通配符来搞定。顾名思义,通配符就是通用的匹配信息的符号,比如星号(*)代表匹配零个或多个字符,问号(?)代表匹配单个字符,中括号内加上数字[0-9]代表匹配0~9之间的的单个数字字符,而中括号内加上字母[abc] 则是代表匹配a、b、c三个字符中任意一个字符,其实就是编程中正则表达式。
[root@root test]# ls -l /dev/sda*
brw-rw----. 1 root disk 8, 0 1月 21 08:42 /dev/sda
brw-rw----. 1 root disk 8, 1 1月 21 08:42 /dev/sda1
brw-rw----. 1 root disk 8, 2 1月 21 08:42 /dev/sda2
brw-rw----. 1 root disk 8, 3 1月 21 08:42 /dev/sda3
4. 常用转义字符
为了能够更好的理解用户的表达,Shell解释器还提供了特别丰富的转义字符来处理输入的特殊数据。
4个最常用的转义字符如下所示:
反斜杠(\):是反斜杠后面的一个变量变为单纯的字符串。
单引号(‘’):转义其中所有的变量为单纯的字符串。
双引号(“”):保留其中的变量属性,不进行转义处理。
反引号(``):把其中的命令执行后返回结果。
我们先定义一个名为PRICE的变量并赋值为5,然后输出以双引号括起来的字符串与变量信息:
[root@root test]# PRICE=5
[root@root test]# echo "Price is $PRICE"
price is 5
接下来,我们希望能够输出“Price is $5
” ,即价格是5美元的字符串内容,但碰巧美元符号与变量提取符号合并后的$$
作用是显示当前程序的进程ID号码,于是命令执行后输出的内容并不是我们所预期的:
[root@root test]# [root@root test]# echo "price is $$PRICE"
price is 7142PRICE
要想让第一个“$” 乖乖的作为美元符号,那么就需要反斜杠(\)来进行转义,将这个命令提取符转义称单纯的文本,去除其特殊功能。
[root@root test]# [root@root test]# echo "price is \$$PRICE"
price is $5
而如果只需要某个命令的输出值时,可以像“命令”这样,将命令用反引号括起来,达到预期效果。例如,将反引号与uname -a命令结合,然后使用echo命令来查看本机的Linux版本和内核信息:
[root@root test]# echo `uname -a`
Linux root 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
5. 重要的环境变量
变量是计算机系统用于保存可变值的数据类型。在Linux系统中,变量名称一般都是大写的,这是一种约定俗成的规范。我们可以直接通过变量名称来提取到对应的变量值。Linux系统中环境变量是用来定义系统运行环境的一些参数,比如每个用户不同的家目录、邮件存放位置等。
在Linux系统中一切都是文件,Linux命令也不例外,那么在用户执行了一条命令之后,Linux系统中到底发生了什么事情呢?简单来说,命令在Linux中的执行分为4个步骤。
第一步: 判断用户是否以绝对路径或相对路径的方式输入命令(如/bin/ls),如果是的话则直接执行。
第二步: Linux系统检查用户输入的命令是否为“别名命令”,即用一个自定义的命令名称来替换原本的命令名称。可以用 alias 命令来创建一个属于自己的命令别名,格式为 “alias 别名=命令” 。若要取消一个命令别名,则是用unalias命令,格式为:“unalias 别名”。我们之前使用rm 命令删除文件时,Linux系统都会要求我们再确认是否执行删除操作,其实这就是Linux系统为了防止用户误删文件而特意设置的rm别名命令。
[root@root test]# alias rm
alias rm='rm -i'
第三步: Bash解释器判断用户输入的是内部命令还是外部命令。内部命令时解释器内部的命令,会被直接执行;而用户在绝大部分时间输入的是外部命令,这些命令交由步骤4处理。可以使用“type 命令名称” 来判断用户输入的命令时内部命令还是外部命令。
第四步: 系统在多个路径中查找用户输入的命令文件,而定义这些路径的变量叫作PATH,可以简单地把它理解成是“解释器的小助手”,作用是告诉Bash解释器待执行的命令可能存放的位置,然后Bash解释器就会乖乖地在这些位置中逐个查找。PATH是由多个路径值组成的变量,每个路径值之间用冒号隔开,对这些路径的增加和删除操作将影响到Bash解释器对Linux命令的查找。
[root@root test]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/data/jdk1.8.0_341/bin:/home/data/redis/bin:/home/data/kafka_2.12-3.0.0/bin:/root/bin
Linux系统中最重要的环境变量
变量名称 | 作用 |
---|---|
HOME | 用户的主目录 |
SHELL | 用户在使用的Shell解释器名称 |
HISTSIZE | 输出的历史命令记录条数 |
HISTFILESIZE | 保存的历史命令记录条数 |
邮件保存路径 | |
LANG | 系统语言、语系名称 |
RANDOM | 生成一个随机数字 |
PSI | Bash解释器的提示符 |
PATH | 定义解释器搜索用户执行命令的路径 |
EDITOR | 用户默认的文本编辑器 |