陈斌彬的技术博客

Stay foolish,stay hungry

Unix 系统上的文本操作简介

简介

UNIX 的基本哲学之一就是创建只做一件事并将这一件事做好的程序(或进程)。这一哲学要求认真考虑接口以及结合这些更小(有望更简单)流程的方法,以产生有用的 结果。文本数据通常是在这些接口之间流动。多年以来,越来越高级的文本处理工具和语言已经开发出来。从语言上来讲,早期专门处理文本的语言有 perl,后来又出现了 python 和 ruby。虽然这些语言以及其他语言都是非常强大的文本处理器,但这些工具并不是一直可行的,在生产环境下尤其如此。本文将介绍一些基本的 UNIX 文本处理命令,这些命令既可单独使用也可结合使用,可用来解决需要更新的语言才能解决的问题。对许多人来说,与长篇大论的解释相比,实例能够提供更多的信 息。请注意,由于有多种可用的 UNIX 和 UNIX 类系统,因此不同实现之间的命令标志、程序行为和输出结果会略有不同。

使用 cat

cat 命令是最基本的命令之一。这个命令用来创建、追加、显示以及合并文本文件。

我们可以使用 cat 命令创建文件,方法是:使用 ‘>’ 将标准输入 (stdin) 重定向到文件。使用 ‘>’ 操作符会缩短指定输出文件的内容。在此之后输入的文本会重定向到 ‘>’ 操作符右侧指定的文件。control-d 表示文件结束,将控制权返回给 shell。

使用 cat 创建文件的示例

$ cat > demo.txt
helloworld
believe your self
<ctrl-d>
$

使用 ‘>>’ 操作符将标准输入追加到现有文件。

使用 cat 追加文件的示例

$ cat >> demo.list
good
<ctrl-d>

使用 cat 命令不加标志,可查看 demo.list 文件的内容。请注意文件的内容如何包含来自重定向的输入以及追加操作符的示例。

使用无标志 cat 的示例

➜ /Users/apple/Desktop git:(master) ✗> cat demo.txt
helloworld
believe your self
good

可以使用 cat 命令对文件行进行编号

使用 cat 计算行的示例:

➜ /Users/apple/Desktop git:(master) ✗> cat -n demo.txt
 1  helloworld
 2  believe your self
 3  good

使用 nl

nl 过滤器会从 stdin 或指定文件读取行。输出则会写入 stdout 并重定向到文件,或传到另一个进程中。nl 的行为是由不同命令行选项控制的。

在默认情况下,nl 会计算行数,与 cat -n 的功能类似。

nl 默认用法示例:

 ➜ /Users/apple/Desktop git:(master) ✗> nl demo.txt
 1  helloworld
 2  believe your self
 3  good     

使用 wc

wc (wordcount) 命令计算指定文件或来自 stdin 的行数、单词数(由空格分隔)和字符数。

wc 用法示例

➜ /Users/apple/Desktop git:(master) ✗> wc demo.txt
       3       5      34 demo.txt
➜ /Users/apple/Desktop git:(master) ✗> wc -l demo.txt
       3 demo.txt
➜ /Users/apple/Desktop git:(master) ✗> wc -w demo.txt
       5 demo.txt
➜ /Users/apple/Desktop git:(master) ✗> wc -c demo.txt
      34 demo.txt

使用 grep

grep 命令在指定文件或 stdin 中搜索与给定表达式相匹配的模式。grep 的输出由多个选项标志控制。

为了演示,这里新创建了一个文件,与 demo.txt 配合使用。

➜ /Users/apple/Desktop git:(master) ✗> cat demo2.txt
good
well
done     
Good

grep 基本用法示例

➜ /Users/apple/Desktop git:(master) ✗> grep good demo.txt demo2.txt
demo.txt:good
demo2.txt:good

grep 拥有相当可观的选项标志。下面的示例将演示其中几个选项的用法。

要显示文件名(处理多个文件的情况下)以及发现模式匹配的行数,在本用例中使用的模式是计算每个文件中出现单词 ‘apple’ 的行数。

grep 示例:计算文件中匹配数

➜ /Users/apple/Desktop git:(master) ✗> grep -c good demo.txt demo2.txt
demo.txt:1
demo2.txt:1

在搜索多个文件时,可以使用 -h 选项取消在输出中显示文件名。

grep 示例:取消在输出中显示文件名

➜ /Users/apple/Desktop git:(master) ✗> grep -h good demo.txt demo2.txt
good
good

在很多情况下,需要进行不区分大小写的搜索。grep 命令的 -i 选项可以在搜索时忽略大小写。

grep 示例:不区分大小写

➜ /Users/apple/Desktop git:(master) ✗> grep -i good demo.txt demo2.txt
demo.txt:good
demo2.txt:good
demo2.txt:Good

有些时候,只需要输出文件名,不需要输出模式匹配的行。grep 提供 -l 选项,用于只输出包含匹配模式行的文件名。

grep 示例:只输出文件名

 ➜ /Users/apple/Desktop git:(master) ✗> grep -l good demo.txt demo2.txt 
demo.txt
demo2.txt

行号可以显示在输出中。使用 -n 选项来包含行号。

grep 示例:包含行号

➜ /Users/apple/Desktop git:(master) ✗> grep -n good demo.txt demo2.txt
demo.txt:3:good
demo2.txt:1:good

grep 示例:输出不匹配行

➜ /Users/apple/Desktop git:(master) ✗> grep -v helloworld demo2.txt
good
well
done
Good

有时,需要的模式是一个单词,两边被空格或其他字符(例如连字符或括号)包围。grep 的多数版本都提供了 -w 选项,能方便地编写此类模式的搜索。

grep 示例:单词匹配

➜ /Users/apple/Desktop git:(master) ✗> grep -w good demo.txt demo2.txt
demo.txt:good
demo2.txt:good

流、管道、重定向、tee 和 here docs

在 UNIX 中,一个终端默认包含三个流,一个输入流,两个基于输出的流。输入流称为 stdin,通常映射到键盘(也可使用其他输入设备,或从其他进程中传入)。标准输出流称为 stdout,并通常输出到终端上,输出也可供其他进程使用(就像 stdin 一样)。另一个输出流 stderr 主要用作状态报告,通常输出到终端,如 stdout。这三个流都有各自的文件描述符,每一个流都可以从其他流中传入或重定向,即使是它们全都连接到终端。每个流的文件描述符分别是:

  • stdin = 0
  • stdout = 1
  • stderr = 2

这三个流可以传送或重定向到文件或其他进程。这个构造通常称为 “构建一个管道”。例如,程序员可能想将 stdout 流和 stderr 流合并,然后在终端上显示它们,再将结果保存到文件中以便检查版本问题。使用 2>&1stderr 流以及文件描述符 2 会被重定向到 &1(指向 stdout 流)。这样就能有效地将 stderr 合并到 stdout。使用 | 符号表示管道。管道连接左侧进程 (make) 的 stdout 和右侧进程 (tee) 的 stdintee 命令会复制(合并)将数据发送到终端以及文件的 stdout 流,在本示例中,称为 build.log

合并和拆分标准流的示例

$ make –f build_example.mk 2>&1 | tee build.log

另一个重定向示例,使用 cat 命令和一些流重定向制作文本文件副本。

使用重定向制作备份文件的示例

➜ /Users/apple/Desktop git:(master) ✗> cat < demo.txt > demo.txt.bak

前面使用 nl 命令为在 stdout 中显示的文件加行号。管道可用于将 stdout 流(来自 cat demo.txt)发送到另外一个进程,在本用例中,是 nl 命令。

简单管道传送到 nl 的示例

➜ /Users/apple/Desktop git:(master) ✗> cat demo.txt | nl
 1  helloworld
 2  believe your self
 3  good

先前显示的另一个示例是针对模式执行不区分大小写的搜索。这也可以使用重定向来实现(在这本用例中为来自 stdin 或使用管道,与上述简单管道示例相似)。

grep 示例:通过 stdin 重定向和管道

➜ /Users/apple/Desktop git:(master) ✗> grep -i good < demo2.txt
good
Good
➜ /Users/apple/Desktop git:(master) ✗> cat demo2.txt | grep -i good
good
Good

在某些情况下,要将文本块重定向到某个命令或文件中作为脚本的一部分。实现此操作的机制是使用 ‘here document’(或 ‘here-doc’)。要将 here-doc 嵌入到脚本,需要使用 ‘<<’ 操作符重定向下列文本,直到到达文件结束分隔符为止。在 << 操作符后指定分隔符。

➜ /Users/apple/Desktop git:(master) ✗> cat << EOF
heredoc> oranges
heredoc> mangos
heredoc> pinapples
heredoc> EOF
oranges
mangos
pinapples

可将此输出重定向到文件,在本示例中,分隔符 ‘EOF’ 改为 ‘!’。

示例:将基本 here-doc 重定向到文件

➜ /Users/apple/Desktop git:(master) ✗> cat << ! > demo3.txt
heredoc> oranges
heredoc> mangeos
heredoc> piapples
heredoc> !
➜ /Users/apple/Desktop git:(master) ✗> cat demo3.txt
oranges
mangeos
piapples

使用 head 和 tail

headtail 命令用来查看文件的顶部 (head) 或底部 (tail)。要显示文件顶部两行和底部两行,请分别使用这两个命令加 -n 选项标志。相同地,-c 选项显示文件中的前几个或最后几个字符

示例:head 和 tail 命令的基本用法

➜ /Users/apple/Desktop git:(master) ✗> head -n2 demo2.txt
good
well
➜ /Users/apple/Desktop git:(master) ✗> tail -n2 demo2.txt
done
Good%
➜ /Users/apple/Desktop git:(master) ✗> head -c12 demo2.txt
good
well
do%       
➜ /Users/apple/Desktop git:(master) ✗> tail -c12 demo2.txt
ll
done
Good%

tail 命令的常见用途就是观察日志文件或者正在运行的进程输出,查看其中是否有问题,或者关注进程何时结束。-f (tail –f) 选项使 tail 持续观察流,即使是到达文件结束标记也继续观察,并在流包含更多数据时,持续显示输出。

Resource Reference