彭某的技术折腾笔记

彭某的技术折腾笔记

Linux 多个命令的连接

28
2023-07-21

Linux 多个命令的连接

2023年7月21日

摘要

在使用 Linux 命令行的时候,我们时常会遇到需要某一个命令的输出作为另一个命令的输入的时候。为了应对这样的情形,Linux 中有四个非常实用的工具:管线符号 |xargs 命令,反引号和 tee 命令。本文将对以上四个命令做一些基本的介绍。

一些概念

在讲述关于 Linux 命令之间数据传递的方式之前,首先需要知道 Linux 的命令中,所处理的数据都是什么类型的。这里讲的类型不是指的类似整数或是字符串之类的类型,而是更为概括的两种类型:标准输入和命令行参数。要对命令的输入输出进行连接或是重定向,最重要的一件事就是要先清晰的知道每一个命令的输入和输出是属于哪种类型的。

标准输入

在 Linux 或者其他各种各样符合 POSIX (Portable Operating System Interface X) 的系统中,都存在三个关于输入输出的接口文件(Linux 中万物皆文件):

  • 标准输入(stdin)
  • 标准输出(stdout)
  • 标准错误(stderr)

关于这三个接口,可以理解为他们就是三个出入口,但是背后可以对接各种各样的东西,键盘输入,屏幕上的终端显示,网络数据等。这些数据源只要按照他们的标准设计,都可以和系统对接上,然后用户就可以按照定义好的标准对其进行读写。

对于这三个标准接口,值得注意的是,他们仅仅代表等是源源不断进进出出的“数据”,而没有实际的语法或是语意,他们只管作为一条带有大门的传送带,流式的,对原始数据进行传输,仅此而已。至于数据的含义,与他们无关,他们传输的甚至都不一定要是人类可读的字符串。

命令行参数输入

和标准输入不同,命令行输入是有实际语义的,可以理解为他们是一个一个“子命令”,更细化的描述某一个“主命令”要怎么执行,因此,命令行参数输入多是一些选项列表中的选项,或是命令提前定义好的,可接受的格式的参数或者指令。

管线

管线对接的数据格式是标准输入输出。他的作用是无条件的将前一个命令的输出结果源源不断的输入给下一个命令。

管线命令的使用格式是:

COMMAND_A -ARGS_1 INPUT_1 | COMMAND_B -ARGS_2

其可以把 COMMAND_A -ARGS_1 INPUT_1 运行后的输出结果作为标准输出,然后被 | 原封不动的当作标准输入传输给 COMMAND_B -ARGS_2 进行执行。

比较常用的用法是对于当前目录内的文件进行筛选:

ls -ahl | grep FILE_NAME

此操作先使用 ls -ahl 列出当前目录下的所有文件,然后将结果交给 grep 命令以 FILE_NAME 作为关键字进行筛选。

再举一个比较巧妙的例子,在某些比较古老的电脑上,受限于机械硬盘的随机读写速度比较缓慢,在两块硬盘之间移动数据会比较缓慢,此时也可以借助 tar 和管线命令变成连续写入:

tar -cf - DIRECTORY | tar -C TARGET_DIRECTORY -xvf -

tar 命令中,用到的参数如下:

  • -c - 打包
  • -x - 解包
  • -f FILE - 指定执行操作的文件为 FILE,后面的 -tar 命令中代表标准输入输出,GNU 系统中,-f - 可省略,默认即代表标准输入输出
  • -C DIR - 指定输出目录为 DIR

此时,通过 tar 命令打包的文件,将连续的通过管线传输至另一个 tar 命令进行解包,不经过中间文件中转。

类型转换

管线可以解决输出为 stdout 和 输入为 stdin 的命令的对接,但是一旦前一个命令的输出类型是 stdout 而后一个命令的输入类型是参数列表,二者不一致,就需要通过额外的命令进行转换。由于参数列表只能作为输入(稍微想想就知道如果存在的话很奇怪),所以不存在输出为参数列表然后需要转换至 stdin 作为输入的情况。但是如果想把参数列表转换成 stdout 作为输出的话,可以用 echo PARAMETERS 来进行。

要实现标准输出到参数的转换,就是要实现字符串的按规则分割和生成参数列表,如果此时的标准输出连字符串都不是的话,那就还要使用管线重定向到一个用户自己编写的解析程序解析成字符串,再进行参数列表生成。

在 Linux 中,有一个自带命令 xargs 可以完成转换,大致的格式为:

STDOUT | xargs -ARGS_1 COMMAND -ARGS_2

这样的使用大致等效于:

COMMAND -ARGS_2 PARAMETERS

其中 STDOUT 指代了上一个命令的整体,因为对于管线来说他收到的只有最终输出的标准输出流。-ARGS_1xargs 的执行选项,-ARGS_2COMMAND 的执行选项。PARAMETERS 是由 STDOUT 转换而来的参数列表。

此命令本身有一些常用选项:

  • -d DIV - 指定分隔符为 DIV,默认为一切空字符:空格,\t\n
  • -p - 先显示解码后的真正要执行的命令,输入 yes 确认再执行
  • -t - 先显示解码后的真正要执行的命令,然后直接执行,不用确认
  • -n N - 转换出来的参数列表几个为一组作为后续命令的参数,如果有多组会每组参数执行一次
  • -L N - 一次从标准输出读取 N 行作为参数,在输入本身就是多行的时候有用
  • -I VAR 每个参数存进 VAR,替代后续命令中的 VAR,每个参数执行一次,一共会执行多次,例如 xargs -I {} mkdir {}
  • -P N - 最大线程数,由于可能有些选项会让命令执行多次,因此可以多线程,N=0 的时候有多少用多少
  • -0 - 将分隔符设为空字符 \0,在处理文件名的时候实用,直接将整个字符串无论啥样直接作为一整个参数

输出到文件

想要将输出重定向到文件很简单,这样就可以了:

COMMAND > FILE

这样默认是覆盖文件,如果不想覆盖,只想追加在已有文件内容后面,就可以:

COMMAND >> FILE

一旦将输出重定向到文件过后,终端上就不会显示了。

双重定向

使用管线的时候,前一个命令的输出会直接传入管线,不会输出到屏幕,如果我们想让他既输出又传入管线,就可以使用 tee 命令。

tee 命令将会把收到的输入复制两份,传至两个地方:

ls | tee out.txt backup.txt

ls 的结果复制两份,一份存入 out.txt,一份存入 backup.txt

ls | tee out.txt | cat

一份存入 out.txt,一份输出给 cat 命令进行显示。

注意,catecho 同样都是用来显示,但他们的不同在于 cat 接收的是文件或标准输入,而 echo 接收的是参数列表。

ls | tee /dev/stdout | cat

一份直接输出给 stdout 进行显示,一份输出给 cat 命令进行显示。不知道这样做有什么意义,但是可以这样做,会显示两遍。

tee 命令就一个常用选项:

  • -a - 追加到文件末尾,而不是覆盖。

反引号

反引号会将内部命令先执行,然后替换到相同位置,再把此时的命令重新解析再执行一遍。例如:

ls -ahl `which python`

此命令会把反引号内部的 which python 执行一遍,假如得到 /usr/bin/python,那么此时执行的命令就直接等效于:

ls -ahl /usr/bin/python

等于是区分了一个命令执行的先后顺序。

  • 0