03 管道符、重定向与环境变量
- 本章首先讲解与文件读写操作有关的重定向技术的 5 种模式—标准覆盖输出重定向、标准追加输出重定向、错误覆盖输出重定向、错误追加输出重定向以及输入重定向,让读者通过实验切实理解每个重定向模式的作用,解决输出信息的保存问题。
- 然后深入讲解管道命令符,帮助读者掌握命令之间的搭配使用方法,进一步提高命令输出值的处理效率。
- 随后通过讲解 Linux 系统命令行中的通配符和常用转义字符,让您输入的 Linux 命令具有更准确的意义,为下一章学习编写 Shell 脚本打好功底。
- 最后,本章深度剖析了 Bash 解释器执行 Linux 命令的内部原理,为读者掌握 PATH 变量及 Linux 系统中的重要环境变量打下了基础。
输入输出重定向
- 标准输入重定向(STDIN,文件描述符为 0):默认从键盘输入,也可从其他文件或命令中输入。
- 标准输出重定向(STDOUT,文件描述符为 1):默认输出到屏幕。
- 错误输出重定向(STDERR,文件描述符为 2):默认输出到屏幕。
输入重定向中用到的符号及其作用
| 符号 | 作用 |
|---|---|
命令 < 文件 | 将文件作为命令的标准输入 |
命令 << 分界符 | 从标准输入中读入,直到遇见分界符才停止 |
命令 < 文件1 > 文件2 | 将文件 1 作为命令的标准输入并将标准输出到文件 2 |
输入重定向的作用是把文件直接导入到命令中。
输入重定向
使用输入重定向后,命令只能读到信息流数据,读取不到文件名称的信息。
$ wc -l /etc/passwd
32 /etc/passwd
$ wc -l < /etc/passwd
32wc -l /etc/passwd 是一种非常标准的 命令 + 参数 + 对象 的执行格式,而这次的 wc -l < readme.txt 则是将 readme.txt 文件中的内容通过操作符导入到命令中,没有被当作命令对象进行执行,因此 wc 命令只能读到信息流数据,而没有文件名称的信息。
输出重定向中用到的符号及其作用
| 符号 | 作用 |
|---|---|
命令 > 文件 | 将标准输出重定向到一个文件中(清空原有文件的数据) |
命令 2> 文件 | 将错误输出重定向到一个文件中(清空原有文件的数据) |
命令 >> 文件 | 将标准输出重定向到一个文件中(追加到原有内容的后面) |
命令 2>> 文件 | 将错误输出重定向到一个文件中(追加到原有内容的后面) |
命令 >> 文件 2>&1 或 命令 &>> 文件 | 将标准输出与错误输出共同写入到文件中(追加到原有内容的后面) |
标准输出重定向
$ man bash > readme.txt
$ cat -n readme.txt
1 BASH(1) General Commands Manual BASH(1)
2
3 NAME
4 bash - GNU Bourne-Again SHell
5
6 SYNOPSIS
7 bash [options] [command_string | file]
8
9 COPYRIGHT
………………省略部分输出信息………………
4241
4242 There may be only one active coprocess at a time.
4243
4244 GNU Bash 5.1 2020 October 29 BASH(1)错误输出重定向
由于 /nonexistent 目录不存在,所以 ls 命令会产生一个错误消息,这个错误消息会被重定向到 error.txt 文件中。
$ ls /nonexistent 2> error.txt
$ cat -n error.txt
1 ls: cannot access '/nonexistent': No such file or directory标准输出重定向 + 追加
$ cat -n error.txt
1 ls: cannot access '/nonexistent': No such file or directory
$ ls -la /etc/passwd >> error.txt
$ cat -n error.txt
1 ls: cannot access '/nonexistent': No such file or directory
2 -rw-r--r-- 1 root root 1715 Dec 4 01:36 /etc/passwd错误输出重定向 + 追加
$ cat -n error.txt
1 ls: cannot access '/nonexistent': No such file or directory
2 -rw-r--r-- 1 root root 1715 Dec 4 01:36 /etc/passwd
$ ls /nonexistent 2>> error.txt # 错误输出消息不会重定向
$ cat -n error.txt
1 ls: cannot access '/nonexistent': No such file or directory
2 -rw-r--r-- 1 root root 1715 Dec 4 01:36 /etc/passwd
3 ls: cannot access '/nonexistent': No such file or directory
$ ls -la /etc/passwd 2>> error.txt # 成功/标准输出消息不会重定向
-rw-r--r-- 1 root root 1715 Dec 4 01:36 /etc/passwd
$ cat -n error.txt
1 ls: cannot access '/nonexistent': No such file or directory
2 -rw-r--r-- 1 root root 1715 Dec 4 01:36 /etc/passwd
3 ls: cannot access '/nonexistent': No such file or directory标准/错误输出重定向 + 追加
$ cat -n error.txt
1 ls: cannot access '/nonexistent': No such file or directory
2 -rw-r--r-- 1 root root 1715 Dec 4 01:36 /etc/passwd
3 ls: cannot access '/nonexistent': No such file or directory
$ ls -la /etc/passwd &>> error.txt # 标准/错误输出全部重定向
$ cat -n error.txt
1 ls: cannot access '/nonexistent': No such file or directory
2 -rw-r--r-- 1 root root 1715 Dec 4 01:36 /etc/passwd
3 ls: cannot access '/nonexistent': No such file or directory
4 -rw-r--r-- 1 root root 1715 Dec 4 01:36 /etc/passwd管道命令符
- 在学习 tr 命令时:见到过一个名为管道符的东西。同时按下键盘上的
Shift + 反斜杠(\)键即可输入管道符,其执行格式为“命令A | 命令B”。 - 管道命令符的作用:用一句话概括为 把前一个命令原本要输出到屏幕的信息当作后一个命令的标准输入。讲解
grep文本搜索命令时,我们通过匹配关键词/sbin/nologin找出了所有被限制登录系统的用户。 - 两条命令合并为一条:找出被限制登录用户的命令是
grep /sbin/nologin /etc/passwd,统计文本行数的命令则是wc -l。现在要做的就是把grep搜索命令的输出值传递给wc统计命令,即把原本要输出到屏幕的用户信息列表再交给wc命令作进一步的加工,因此只需要把管道符放到两条命令之间即可。grep /sbin/nologin /etc/passwd | wc -l
注意
- 不要误以为管道命令符只能在一个命令组合中使用一次。可以这样使用:
命令A | 命令B | 命令C。 - 一个特别贴切的类比:把管道符当做流水线作业,这跟吃顿烧烤是同一个道理,即第一个人负责切肉,第二个人负责串肉,第三个人负责烧烤,最后的处理结果交付给用户。
示例 1 修改用户密码
在修改用户密码时,通常都需要输入两次密码以进行确认,这在编写自动化脚本时将成为一个非常致命的缺陷。通过把管道符和 passwd 命令的 --stdin 参数相结合,可以用一条命令来完成密码重置操作。
$ echo "linuxprobe" | passwd --stdin root # 密码重置操作
Changing password for user root.
passwd: all authentication tokens updated successfully.示例 2 搜索进程
咱们在第 2 章学习 ps 命令的时候,输入 ps aux 命令后屏幕信息呼呼闪过,根本找不到有用的信息。现在也可以将 ps、grep、管道符 | 三者结合到一起使用了。
$ ps aux | grep bash # 搜索与 bash 有关的进程信息
root 1070 0.0 0.1 25384 2324 ? S Sep21 0:00 /bin/bash /usr/sbin/ksmtuned
root 3899 0.0 0.2 26540 5136 pts/0 Ss 00:27 0:00 bash
root 4002 0.0 0.0 12112 1056 pts/0 S+ 00:28 0:00 grep --color=auto bashINFO
如果需要将管道符处理后的结果既输出到屏幕,又同时写入到文件中,则可以与 tee 命令结合使用。
将显示系统中所有与 bash 相关的进程信息,并同时将输出到屏幕和文件中。
$ ps aux | grep bash | tee result.txt
root 1070 0.0 0.1 25384 2324 ? S Sep21 0:00 /bin/bash /usr/sbin/ksmtuned
root 3899 0.0 0.2 26540 5136 pts/0 Ss 00:27 0:00 bash
root 4320 0.0 0.0 12112 1112 pts/0 S+ 00:51 0:00 grep --color=auto bash
$ cat result.txt
root 1070 0.0 0.1 25384 2324 ? S Sep21 0:00 /bin/bash /usr/sbin/ksmtuned
root 3899 0.0 0.2 26540 5136 pts/0 Ss 00:27 0:00 bash
root 4320 0.0 0.0 12112 1112 pts/0 S+ 00:51 0:00 grep --color=auto bash命令行的通配符
通配符就是通用的匹配信息的符号。
- 星号(
*)代表匹配零个或多个字符 - 问号(
?)代表匹配单个字符 - 中括号内加上数字
[0-9]代表匹配0 ~ 9之间的单个数字的字符 - 中括号内加上字母
[abc]则是代表匹配a、b、c三个字符中的任意一个字符。
Linux 系统中的通配符及含义
| 通配符 | 含义 |
|---|---|
* | 任意字符 |
? | 单个任意字符 |
[a-z] | 单个小写字母 |
[A-Z] | 单个大写字母 |
[a-Z] | 单个字母 |
[0-9] | 单个数字 |
[[:alpha:]] | 任意字母 |
[[:upper:]] | 任意大写字母 |
[[:lower:]] | 任意小写字母 |
[[:digit:]] | 所有数字 |
[[:alnum:]] | 任意字母加数字 |
[[:punct:]] | 标点符号 |
示例 1 搜索文件
通配符可用于搜索文件或代替被通配的字符。
$ ls file*.txt
file1.txt file2.txt file3.txt file4.txt file5.txt
$ ls file[135].txt
file1.txt file3.txt file5.txt
$ ls file[2-4].txt
file2.txt file3.txt file4.txt
$ ls file[^2-4].txt
file1.txt file5.txt示例 2 创建文件
通配符可以与创建文件的命令相结合,创建出好多个文件
$ touch file{1,2,3,4,5}.txt
$ ls *.txt
demo.txt file1.txt file2.txt file3.txt file4.txt file5.txt示例 3 通配符放到前面
通配符不一定非要放到最后面,也可以放到前面。
$ ls *.txt
demo.txt file1.txt file2.txt file3.txt file4.txt file5.txt
$ ls /etc/*.conf
/etc/adduser.conf /etc/gai.conf /etc/logrotate.conf /etc/overlayroot.local.conf /etc/sudo_logsrvd.conf
/etc/ca-certificates.conf /etc/hdparm.conf /etc/mke2fs.conf /etc/pam.conf /etc/sysctl.conf示例 4 输出指定信息
$ echo file{1,2,3,4,5}
file1 file2 file3 file4 file5常用的转义字符
反斜杠(
\):使反斜杠后面的一个变量变为单纯的字符。\n:换行符\t:制表符\r:回车,回到当前行的开头\b:退格符,删除前一个字符
单引号(
'):转义其中所有的变量为单纯的字符串。双引号(
"):保留其中的变量属性,不进行转义处理。反引号(`):把其中的命令执行后返回结果。
小技巧
虽然可能不够严谨,但绝对简单:如果参数中出现了空格,就加双引号;如果参数中没有空格,那就不用加双引号。
# 输出 Hello, world!
$ echo "Hello, world!"
Hello, world!
# 输出带有换行符的字符串
$ echo -e "Hello,\nworld!"
Hello,
world!
# 输出带有退格符的字符串
$ echo -e "Hello\bWorld"
HellWorld
# 输出带有制表符的字符串
$ echo -e "Name\tAge\tGender"
Name Age Gender
# 输出带有双引号的字符串
$ echo "\"Hello\", she said."
"Hello", she said.
# 输出带有单引号的字符串
$ echo 'It\'s a beautiful day!'
It's a beautiful day!
# 输出带有反斜杠的字符串
$ echo "This is a backslash: \\"
This is a backslash: \
# 反引号
$ echo `uname -a` # 等同于 echo $(uname -a)
Linux linuxprobe.com 4.18.0-193.el8.x86_64 #1 SMP Fri Mar 27 14:35:58 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
# 输出带有反斜杠的字符串
$ PRICE=5
$ echo "Price is $PRICE"
Price is 5
$ echo "Price is \$$PRICE"
Price is $5重要的环境变量
变量
- 变量的定义:变量是计算机系统用于保存可变值的数据类型。
- 变量名称:在 Linux 系统中,变量名称一般都是大写的,命令则都是小写的,这是一种约定俗成的规范。
- 用来做什么:Linux 系统中的环境变量是用来定义系统运行环境的一些参数,比如每个用户不同的家目录、邮件存放位置等。
- 变量值:可以直接通过变量名称来提取到对应的变量值。
命令在 Linux 中执行的步骤
第 1 步:判断用户是否以绝对路径或相对路径的方式输入命令(如
/bin/ls),如果是绝对路径则直接执行,否则进入第 2 步继续判断。第 2 步:Linux 系统检查用户输入的命令是否为
别名命令,即用一个自定义的命令名称来替换原本的命令名称。bash$ ls anaconda-ks.cfg Documents initial-setup-ks.cfg Pictures Templates Desktop Downloads Music Public Videos $ rm anaconda-ks.cfg rm: remove regular file 'anaconda-ks.cfg'? y # Linux 系统为了防止用户误删除文件而特意设置的 rm 别名命令 `rm -i` # 可以用 alias 命令来创建一个属于自己的命令别名,语法格式为 `alias 别名=命令` # 若要取消一个命令别名,则是用 unalias 命令,语法格式为 `unalias 别名` $ unalias rm $ rm initial-setup-ks.cfg $第 3 步:Bash 解释器判断用户输入的是内部命令还是外部命令。内部命令是解释器内部的指令,会被直接执行;而用户在绝大部分时间输入的是外部命令,这些命令交由步骤 4 继续处理。可以使用
type 命令名称来判断用户输入的命令是内部命令还是外部命令。bash$ type echo echo is a shell builtin $ type uptime uptime is /usr/bin/uptime第 4 步:系统在多个路径中查找用户输入的命令文件,而定义这些路径的变量叫作
PATH,可以简单地把它理解成是“解释器的小助手”,作用是告诉 Bash 解释器待执行的命令可能存放的位置,然后 Bash 解释器就会乖乖地在这些位置中逐个查找。PATH是由多个路径值组成的变量,每个路径值之间用冒号间隔,对这些路径的增加和删除操作将影响到 Bash 解释器对 Linux 命令的查找。bash$ echo $PATH /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/root/bin $ PATH=$PATH:/root/bin $ echo $PATH /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/root/bin:/root/bin
为什么不能将当前目录(.)添加到 PATH 中呢?
- 尽管可以将当前目录(
.)添加到PATH变量中,从而在某些情况下可以让用户免去输入命令所在路径的麻烦。 - 但是,如果黑客在比较常用的公共目录
/tmp中存放了一个与ls或cd命令同名的木马文件,而用户又恰巧在公共目录中执行了这些命令,那么就极有可能中招了。
Linux 系统中最重要的 10 个环境变量
| 变量名称 | 作用 |
|---|---|
HOME | 用户的主目录(即家目录) |
SHELL | 用户在使用的 Shell 解释器名称 |
HISTSIZE | 输出的历史命令记录条数 |
HISTFILESIZE | 保存的历史命令记录条数 |
MAIL | 邮件保存路径 |
LANG | 系统语言、语系名称 |
RANDOM | 生成一个随机数字 |
PS1 | Bash 解释器的提示符 |
PATH | 定义解释器搜索用户执行命令的路径 |
EDITOR | 用户默认的文本编辑器 |
变量使用
# 定义一个普通变量
$ name="John"
# 打印变量内容
$ echo "Hello, $name!"
Hello, John!
# 定义一个环境变量
$ export MY_VAR="my_value"
# 使用环境变量
$ echo "The value of MY_VAR is: $MY_VAR"
The value of MY_VAR is: my_value
# 取消设置的环境变量
unset MY_VARexport 设置环境变量
export直接在终端设置的变量能够立即生效,但在重启服务器后就会失效。- 因此我们需要将变量和变量值写入到
.bashrc或者.bash_profile文件中,以确保永久能使用它们。
总结
把
ls命令的正常输出信息追加写入到error.txt文件中的命令是什么?ls >> error.txt(注意区分>和>>的不同)。请简单概述管道符的作用。
把左面(前面)命令的输出值作为右面(后面)命令的输入值以便进一步处理信息。
Bash 解释器的通配符中,星号(
*)代表几个字符?零个或多个。
PATH变量的作用是什么?设定解释器搜索所执行命令的路径,找到其所在位置。
一般情况下,为参数添加双引号有什么好处?
双引号通常用于界定参数的个数,以免程序或命令在执行时产生歧义,因此参数中若有空格,则建议添加双引号。
使用什么命令可以把名为
LINUX的一般变量转换成全局变量?export LINUX。