Skip to content

03 管道符、重定向与环境变量

  • 本章首先讲解与文件读写操作有关的重定向技术的 5 种模式—标准覆盖输出重定向、标准追加输出重定向、错误覆盖输出重定向、错误追加输出重定向以及输入重定向,让读者通过实验切实理解每个重定向模式的作用,解决输出信息的保存问题。
  • 然后深入讲解管道命令符,帮助读者掌握命令之间的搭配使用方法,进一步提高命令输出值的处理效率。
  • 随后通过讲解 Linux 系统命令行中的通配符和常用转义字符,让您输入的 Linux 命令具有更准确的意义,为下一章学习编写 Shell 脚本打好功底。
  • 最后,本章深度剖析了 Bash 解释器执行 Linux 命令的内部原理,为读者掌握 PATH 变量及 Linux 系统中的重要环境变量打下了基础。

输入输出重定向

  • 标准输入重定向(STDIN,文件描述符为 0):默认从键盘输入,也可从其他文件或命令中输入。
  • 标准输出重定向(STDOUT,文件描述符为 1):默认输出到屏幕。
  • 错误输出重定向(STDERR,文件描述符为 2):默认输出到屏幕。

输入重定向中用到的符号及其作用

符号作用
命令 < 文件将文件作为命令的标准输入
命令 << 分界符从标准输入中读入,直到遇见分界符才停止
命令 < 文件1 > 文件2将文件 1 作为命令的标准输入并将标准输出到文件 2

输入重定向的作用是把文件直接导入到命令中。

输入重定向

使用输入重定向后,命令只能读到信息流数据,读取不到文件名称的信息。

bash
$ wc -l /etc/passwd
32 /etc/passwd

$ wc -l < /etc/passwd
32

wc -l /etc/passwd 是一种非常标准的 命令 + 参数 + 对象 的执行格式,而这次的 wc -l < readme.txt 则是将 readme.txt 文件中的内容通过操作符导入到命令中,没有被当作命令对象进行执行,因此 wc 命令只能读到信息流数据,而没有文件名称的信息。

输出重定向中用到的符号及其作用

符号作用
命令 > 文件将标准输出重定向到一个文件中(清空原有文件的数据)
命令 2> 文件将错误输出重定向到一个文件中(清空原有文件的数据)
命令 >> 文件将标准输出重定向到一个文件中(追加到原有内容的后面)
命令 2>> 文件将错误输出重定向到一个文件中(追加到原有内容的后面)
命令 >> 文件 2>&1命令 &>> 文件将标准输出与错误输出共同写入到文件中(追加到原有内容的后面)

标准输出重定向

bash
$ 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 文件中。

bash
$ ls /nonexistent 2> error.txt

$ cat -n error.txt
   1  ls: cannot access '/nonexistent': No such file or directory

标准输出重定向 + 追加

bash
$ 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

错误输出重定向 + 追加

bash
$ 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

标准/错误输出重定向 + 追加

bash
$ 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 参数相结合,可以用一条命令来完成密码重置操作。

bash
$ echo "linuxprobe" | passwd --stdin root # 密码重置操作
Changing password for user root.
passwd: all authentication tokens updated successfully.

示例 2 搜索进程

咱们在第 2 章学习 ps 命令的时候,输入 ps aux 命令后屏幕信息呼呼闪过,根本找不到有用的信息。现在也可以将 psgrep、管道符 | 三者结合到一起使用了。

bash
$ 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 bash

INFO

如果需要将管道符处理后的结果既输出到屏幕,又同时写入到文件中,则可以与 tee 命令结合使用。

将显示系统中所有与 bash 相关的进程信息,并同时将输出到屏幕和文件中。

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 搜索文件

通配符可用于搜索文件或代替被通配的字符。

bash
$ 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 创建文件

通配符可以与创建文件的命令相结合,创建出好多个文件

bash
$ touch file{1,2,3,4,5}.txt

$ ls *.txt
demo.txt  file1.txt  file2.txt  file3.txt  file4.txt  file5.txt

示例 3 通配符放到前面

通配符不一定非要放到最后面,也可以放到前面。

bash
$ 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 输出指定信息

bash
$ echo file{1,2,3,4,5}
file1 file2 file3 file4 file5

常用的转义字符

  • 反斜杠(\:使反斜杠后面的一个变量变为单纯的字符。

    • \n :换行符
    • \t :制表符
    • \r :回车,回到当前行的开头
    • \b :退格符,删除前一个字符
  • 单引号(':转义其中所有的变量为单纯的字符串。

  • 双引号(":保留其中的变量属性,不进行转义处理。

  • 反引号(`):把其中的命令执行后返回结果。

小技巧

虽然可能不够严谨,但绝对简单:如果参数中出现了空格,就加双引号;如果参数中没有空格,那就不用加双引号。

bash
# 输出 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 中存放了一个与 lscd 命令同名的木马文件,而用户又恰巧在公共目录中执行了这些命令,那么就极有可能中招了。

Linux 系统中最重要的 10 个环境变量

变量名称作用
HOME用户的主目录(即家目录)
SHELL用户在使用的 Shell 解释器名称
HISTSIZE输出的历史命令记录条数
HISTFILESIZE保存的历史命令记录条数
MAIL邮件保存路径
LANG系统语言、语系名称
RANDOM生成一个随机数字
PS1Bash 解释器的提示符
PATH定义解释器搜索用户执行命令的路径
EDITOR用户默认的文本编辑器

变量使用

bash
# 定义一个普通变量
$ 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_VAR

export 设置环境变量

  • export 直接在终端设置的变量能够立即生效,但在重启服务器后就会失效。
  • 因此我们需要将变量和变量值写入到 .bashrc 或者 .bash_profile 文件中,以确保永久能使用它们。

总结

[!question]- 把 ls 命令的正常输出信息追加写入到 error.txt 文件中的命令是什么? ls > > error.txt(注意区分 > > > 的不同)。

[!question]- 请简单概述管道符的作用。 把左面(前面)命令的输出值作为右面(后面)命令的输入值以便进一步处理信息。

[!question]- Bash 解释器的通配符中,星号(*)代表几个字符? 零个或多个。

[!question]- PATH 变量的作用是什么? 设定解释器搜索所执行命令的路径,找到其所在位置。

[!question]- 一般情况下,为参数添加双引号有什么好处? 双引号通常用于界定参数的个数,以免程序或命令在执行时产生歧义,因此参数中若有空格,则建议添加双引号。

[!question]- 使用什么命令可以把名为 LINUX 的一般变量转换成全局变量? export LINUX