• 在之前的Linux笔记中有使用过>>>,比如如下指令:

    1
    2
    3
    echo 'hello shiyanlou' > redirect 
    echo 'www.shiyanlou.com' >> redirect
    cat redirect

    这部分命令用于将文本字符串 “hello shiyanlou” 输出到(>redirect文件中,此时文件并不存在,因此将自动创建redirect文件写入字符串,然后再次使用 echo 命令输出字符串www.shiyanlou.com追加到(>>)文件redirect中。

简单的重定向

  • Linux中默认提供了三种特殊设备,用于终端的显示和输出,分别为stdin(标准输入,对应于你在终端的输入),stdout(标准输出,对应于终端的输出),stderr(标准错误输出,对应于终端的输出)。
文件描述符 设备文件 说明
0 /dev/stdin 标准输入
1 /dev/stdout 标准输出
2 /dev/stderr 标准错误
  • 可以这样使用文件描述符,比如默认使用终端的标准输入和标准输出作为命令的输入和输出:

    1
    cat

    此时输入的内容也会在按下Enter时显示出来。

    (按下Ctrl+C退出)

  • 将cat的连续输出(heredoc方式)重定向到一个文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    mkdir Documents
    cat > Documents/test.c <<EOF
    #include <stdio.h>

    int main()
    {
    printf("hello world\n");
    return 0;
    }

    EOF

    这一段内容的作用是将文件作为标准输出,那么之后输入的内容都会被保存在文件/Documents/test.c中,<<EOF是文本输入的定界符,表示以下内容将作为输入直到遇到 “EOF” 为止。

  • 将一个文件作为命令的输入,标准输出作为命令的输出:

    1
    cat Documents/test.c

    image-20231117193411117

  • 将echo命令通过管道传过来的数据作为cat命令的输入,将标准输出作为命令的输出:

    1
    echo 'hi' | cat

    image-20231117193603353

  • 将echo命令的输出从默认的标准输出重定向到一个普通文件:

    1
    2
    echo 'hello shiyanlou' > redirect
    cat redirect

    image-20231117193628856

标准错误重定向

  • 有时执行指令得到的输出同事包含了标准输出和标准错误,比如如下操作,使用cat命令同时读取两个文件,其中一个存在,另一个不存在:

    1
    cat Documents/test.c hello.c

    可以看到除了正确输出了前一个文件的内容,还在末尾出现了一条错误信息.

    image-20231117194508097

  • 接下来将输出重定向到一个文件:

    1
    cat Documents/test.c hello.c > somefile

    但是依然出现了错误信息,而查看重定指向的文件,已经写入了期望包含的内容

    image-20231117195102336

  • 如果希望将错误或者警告都隐藏,那就可以用到前面提到的文件描述符了。将标准错误重定向到标准输出,再将标准输出重定向到文件,注意要将重定向到文件写到前面:

    1
    cat Documents/test.c hello.c >somefile  2>&1

    或者只用bash提供的特殊的重定向符号"&"将标准错误和标准输出同时重定向到文件:

    1
    cat Documents/test.c hello.c &>somefilehell

    查看文件内容,可以看到错误信息也被写入到文件中了:

    image-20231117195418320

使用tee命令同时重定向到多个文件

  • 如果既需要将输出重定向到文件,也需要将信息大隐刀终端,那么可以使用tee来实现:

    1
    echo 'hello shiyanlou' | tee hello

    可以看到内容既输出在了终端,同时也被写入到了文件当中:

    image-20231117195716791

永久重定向

  • 上面的操作完成的重定向只是临时的,如果希望做到永久重定向,可以使用exec命令完成,其作用是使用指定的命令替换当前的Shell,即使用一个进程替换当前进程,或者指定新的重定向。

  • 先开启一个子Shell,然后使用exec替换当前进程的重定向,将标准输出重定向到一个文件:

    1
    2
    zsh
    exec 1>somefile

    那么后面执行的命令的输出都将被重定向到文件中,,直到退出当前子shell,或取消exec的重定向。

    1
    2
    3
    ls
    exit
    cat somefile

    image-20231117201225039

创建输出文件描述符

  • 在Shel 中有9个文件描述符,上面仅仅是使用了默认提供的0、1、2号文件描述符。另外我们还可以使用3-8的文件描述符,只是它们默认没有打开而已。你可以使用下面命令查看当前 Shell 进程中打开的文件描述符:

    1
    cd /dev/fd/;ls -Al
  • 使用exec命令可以创建新的文件描述并使用:

    1
    2
    3
    4
    5
    6
    zsh
    exec 3>somefile
    cd /dev/fd/;ls -Al;cd -
    echo "this is test" >&3
    cat somefile
    exit

    注意上面的第四句命令中,>&之间不应该有空格,如果有空格则会出错

    image-20231117202354262

关闭文件描述符

  • 刚才设置了3号文件描述符,可以使用如下命令将其关闭:

    1
    2
    exec 3>&-
    cd /dev/fd;ls -Al;cd -

    image-20231117202538761

完全屏蔽命令的输出

  • 在Linux中有一个被称为“黑洞”的设备文件,所有导入它的数据都会“消失“

    在类UNIX系统中,/dev/null,或称空设备,是一个特殊的设备文件,它通常被用于丢弃不需要的输出流,或作为用于输入流的空文件,这些操作通常由重定向完成。读取它则会立即得到一个EOF。

  • 我们可以利用/dev/null屏蔽命令的输出:

    1
    cat Documents/test.c 1>/dev/null 2>&1

    上面这样的操作将使你得不到任何输出结果。

使用 xargs 分割参数列表

  • xargs是一条UNIX和类UNIX操作系统的常用命令,它的作用是将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题。

  • 这个命令在有些时候十分有用,特别是当用来处理产生大量输出结果的命令如 find,locate 和 grep 的结果。比如这个命令用于将/etc/passwd文件按:分割取第一个字段排序后,使用echo命令生成一个列表:

    1
    cut -d: -f1 < /etc/passwd | sort | xargs echo

    image-20231117203054249

实战训练

理解下面这段代码的作用,实际这段代码不会正常工作,请结合这一小节的知识分析这段代码没有正确工作的原因,并设法解决这个问题。

1
2
3
while read filename; do
rm -iv $filename
done <<(ls)
  • 这段代码的作用是将ls命令读取到的全部文件依次删除。

  • 但是在删除文件时会提示是否确认删除文件,而输入被ls得到的结果重定向了,因此无法完成确认操作,上述代码无法完成执行。

    image-20231117214749645

  • 因此需要在删除文件时由控制台输入是否确认删除文件,而在读取删除列表时再从重定向中获得输入,修改后代码如下:

    • 将文件描述符3指向标准输入:

      1
      exec 3<&0
    • 删除文件,由文件描述符3提供确认操作给rm命令,使其能够正确执行:

      1
      rm -iv $filename <&3
    • 遍历列表,依次删除文件:

      1
      2
      3
      while read filename; do
      <delete operation>
      done <<(ls)
  • 因此,完整代码如下:

    1
    2
    3
    4
    exec 3<&0
    while read filename; do
    rm -iv $filename <&3
    done <<(ls)

    image-20231117215259645