macOS从文件读取特定行

1,793 阅读3分钟

在 macOS中读取文件特定行

1. 概述

当我们使用macOS命令行时,读取文本文件是一个常见的操作。有时,我们知道文件中的 X 行包含有趣的数据,而我们只想读取 X 行。

2. 问题介绍

问题很简单。让我们通过一个例子得到一个更清晰的图景。

例如,我们有一个名为道德经.txt的文件:

$ nl 道德经.txt 
1 第一章 论德

     2 上德不德是以有德下德不失德是以无德上德无为而无以为也上仁为之而无以为也上义为之而有以为也上礼为之而莫之应也则攘臂而乃之故失道而后德失德而后仁失仁而 后义失义而后礼夫礼者忠信之泊也而乱之首也前识者道之华也而愚之首也是以大丈夫居其厚而不居其泊居其实而不居其华故去皮取此

     3 第二章 得一

     4 昔之得一者天得一以清地得一以宁神得一以灵浴得一以盈侯王得一以为天下正其至之也谓天毋已清将恐裂谓地毋已宁将恐发谓神毋已灵将恐歇谓浴毋已盈将恐竭谓侯王 毋已贵以高将恐蹶故必贵而以贱为本必高矣而以下为基夫是以侯王自谓孤寡不谷此其贱之本与非也故致数与无与是故不欲禄禄若玉硌硌若石

     5 第三章 闻道

     6 上士闻道堇能行之中士闻道若存若亡下士闻道大笑之弗笑不足以为道是以建言有之曰明道如费进道如退夷道如纇上德如浴大白如辱广德如不足建德如输质真如渝大方无隅大器晚成大音希声大象无刑道隐无名夫唯道善始且善成

     7 第四章 反复

......

如上面的输出所示,我们使用了nl命令来打印带有行号的文件内容。

我们知道道德经.txt文件的第五行包含了一些有趣的信息。因此,我们只想阅读第 5 行。

在BSD(类Unix)命令行中有很多方法可以做到这一点。在本教程中,我们将探索四种方法:

3. 使用纯 Bash 命令

为了解决这个问题,让我们创建一个 shell 脚本getLine.sh

$ cat getLine.sh 
#!/bin/bash
FILE="$1"
LINE_NO=$2
i=0
while read line; do
  i=$(( i + 1 )
  test $i = $LINE_NO && echo "$line";
done <"$FILE"

上面的 shell 脚本看起来很简单。它接受两个参数:文件和目标行号。

基本上,它只包含一个循环。在循环中,我们增加一个计数器变量 $i。 当它达到给定的目标行号时,我们输出该行。例如,如果我们使用道德经.txt 文件运行脚本:

$ ./getLine.sh 道德经.txt 5
5 第三章 闻道

输出显示预期的行已打印。我们的脚本有效。

如果我们仔细阅读脚本,我们可能会发现它还有优化的空间。

我们在循环中检查文件中的每一行,即使我们已经找到并打印了我们需要的行。 好吧,如果我们用道德经.txt运行这个脚本,这不是问题 。毕竟,我们的示例输入文件只有七行。然而,在现实世界中,我们可能会处理 700 万行的文件。

所以,找到目标线后,能打破循环就好了。所以,让我们稍微改变一下脚本:

$ cat getLine2.sh
#!/bin/bash
FILE="$1"
LINE_NO=$2
i=0
while read line; do
  i=$(( i + 1 ))
  case $i in $LINE_NO) echo "$line"; break;; esac
done <"$FILE"

一旦我们找到了我们需要的行,我们就使用case语句来中断循环。 让我们来测试一下:

$ ./getLine2.sh 道德经.txt 5
5 第三章 闻道

它也有效。所以,我们用一个小 Bash 脚本解决了这个问题。

4. 使用sed 命令

该 sed的命令是在解决这类问题非常好。让我们看看几个紧凑的sed one-liners 来完成这项工作:

$ sed '5!d' 道德经.txt
5 第三章 闻道

$ sed -n '5p' 道德经.txt
5 第三章 闻道

在第一个line 中“ 5!d ”表示过滤第 5 行之外的所有行,而在第二个命令中, “ -n '5p'  ”表示只打印第 5 行

这两个单线工作正如我们预期的那样。但是,与 Bash 脚本类似,它们将遍历整个输入文件。因此,如果输入文件很大,它们将花费不必要的长时间。

所述 的sed提供了“ q ”命令,其允许“跳槽”进一步处理。我们可以将 ' q ' 命令放在两个单行中:

$ sed '5!d;q' 道德经.txt
5 第三章 闻道

从输出中,我们看不出任何区别。因此,让我们使用sedsed(一个sed调试实用程序)工具在带和不带“ q ”的情况下运行sed命令,看看“ q ”命令是如何工作的。 首先,让我们看看没有' q '命令的版本:

$ sedsed -d '5!d' 道德经.txt
PATT:1, 第一章 论德
HOLD:$
COMM:5 !d
PATT:4 上德不德是以有德下德不失德是以无德上德无为而无以为也上仁为之而无以为也上义为之而有以为也上礼为之而莫之应也则攘臂而乃之故失道而后德失德而后仁失仁而 后义失义而后礼夫礼者忠信之泊也而乱之首也前识者道之华也而愚之首也是以大丈夫居其厚而不居其泊居其实而不居其华故去皮取此
...
5 第三章 闻道
PATT:6 上士闻道堇能行之中士闻道若存若亡下士闻道大笑之弗笑不足以为道是以建言有之曰明道如费进道如退夷道如纇上德如浴大白如辱广德如不足建德如输质真如渝大方无隅大器晚成大音希声大象无刑道隐无名夫唯道善始且善成
HOLD:$
COMM:5 !d
PATT:7 第四章 反复
HOLD:$
COMM:5 !d

然后,我们可以看到sed命令从最后一行(第 7 行)一路处理了文件。

接下来,我们将使用“ q ”测试 sed命令:**

$ sedsed -d '5!d;q' 道德经.txt
PATT:I am line 1, 第一章 论德
HOLD:$
COMM:5 !d
PATT:I am line 2, 上德不德是以有德下德不失德是以无德上德无为而无以为也上仁为之而无以为也上义为之而有以为也上礼为之而莫之应也则攘臂而乃之故失道而后德失德而后仁失仁而 后义失义而后礼夫礼者忠信之泊也而乱之首也前识者道之华也而愚之首也是以大丈夫居其厚而不居其泊居其实而不居其华故去皮取此
...
PATT:I am line 5, 第三章 闻道
HOLD:$
COMM:q
I am line 5, 第三章 闻道

如调试输出所示,  sed处理在第 5 行停止。

5. 使用 awk命令

该 awk的命令是另一种强大的文本处理工具。它还可以用紧凑的单行来解决问题:awk 'NR==5' 道德经.txt。

但是,正如我们之前讨论过的,我们希望在打印第 5 行后停止进一步处理。

同样,  awk有“ exit ”命令来退出当前处理

$ awk 'NR==5{ print; exit }' 道德经.txt
第三章 闻道

因此,正如上面的输出所示,我们已经解决了这个问题。

6.使用headtail命令

使用headtail命令,我们可以轻松获取文件的第一部分和最后部分。

如果我们将这两个命令结合起来,我们也可以读取特定的行。

假设我们要阅读第 X 行。这个想法是:

  • 首先,我们使用head命令获取第 1 行到 X :head -n X input
  • 然后,我们将第一步的结果通过管道传递给tail 命令以获取最后一行:head -n X input | tail -1

让我们测试一下这个想法是否适用于我们的示例:

 head -n 5 道德经.txt | tail -1
第三章 闻道